Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > As3 изучение и немного As2

Оценить эту запись

Система диалогов, создаем подобие old School типа Fallout.

Запись от in4core размещена 07.05.2014 в 01:01

Привет !
Давай те сначала посмотрим на результат, о чем пойдет речь. Все схематично, никакого дизайна

И так, черные квадраты - это Персонажи нашей РПГ, по клику на которых мы открываем диалог и можем с ним работать.
1й квадрат - инфо персонаж, ничего полезного не делает, просто выдает инфу.
2й квадрат - персонаж, выбор веток диалога скрывает другие ветки.
4й квадрат - инфо персонаж
3й квадрат - персонаж, выбор веток диалога скрывает другие ветки 4го персонажа ( взаимодействие персонажей )

dd.swf   (9.9 Кб)


Диалог в XML

Код:
<?xml version="1.0" encoding="utf-8" ?>
<data>

	<person name = "James">
		<dialog title = "Эй бро, как делишки?">
			<line title = "Здорова, все ок." lock = "false" id = "1">
				<dialog title = "Куда напрвляешься?">
					<line title = "Eду в СПБ." lock = "false" id = "10" >
						<dialog title = "Ну удачи бро.">
							<line title = "Бывай!" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "Хреново!" lock = "false" id = "2">
				<dialog title = "Да не ссы ты, я сто раз так делал!!!">
					<line title = "Ладно, но с тебя пивас!" lock = "false" id = "11">
						<dialog title = "Заметано.">
							<line title = "Бывай!" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "Да пошел ты!" lock = "false" id = "3">
				<dialog title = "Heh Fuck Himself!">
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>	
			<line title = "[Выход]" lock = "false" id = "0"/>
		</dialog>		
	</person>
	
	<person name = "Гнусный Орк">
		<dialog title = "Бурлы мурлы, моя твоя не понимать...моя садись, твоя поехали...">
			<line title = "Ты кто такой?" lock = "false" id = "1">
				<dialog title = "Моя друг человека!">
					<line title = "Я слежу за тобой..." lock = "false" id = "10" lockedid = "2">
						<dialog title = "Да-да следить, следить...">
							<line title = "[Про себя : ну и мурло же ты][Выход]" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "Выходи сражаться исчадие ада!" lock = "false" id = "2">
				<dialog title = "[Орк сопит] Я боюсь тебя человека!">
					<line title = "[достать незаметно нож] А ты не бойся, сейчас мы подружимся!" lock = "false" id = "11">
						<dialog title = "Дружить, давай дружить!">
							<line title = "[Напасть]" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "[Грозно зарычать на орка]" lock = "false" id = "3">
				<dialog title = "[Орк испугался и убежал]">
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>	
			<line title = "[Выход]" lock = "false" id = "0"/>
		</dialog>		
	</person>
	
	<person name = "Гнусный Орк с пушкой из НКР">
		<dialog title = "Бурлы мурлы, моя твоя не понимать...моя садись, твоя поехали...">
			<line title = "Ты кто такой?" lock = "false" id = "1">
				<dialog title = "Моя друг человека!">
					<line title = "Я слежу за тобой..." lock = "false" id = "10">
						<dialog title = "Да-да следить, следить...">
							<line title = "[Про себя : ну и мурло же ты][Выход]" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "Выходи сражаться исчадие ада!" lock = "false" id = "2">
				<dialog title = "[Орк сопит] Я боюсь тебя человека!">
					<line title = "[достать НЕЗАМЕТНО бозар] А ты не бойся, сейчас мы подружимся!" lock = "false" id = "11" lockedperson = "Гнусный Орк с пушкой из НКР_connected" lockedpersonid = "11">
						<dialog title = "Дружить, давай дружить!">
							<line title = "[Напасть]" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "[Грозно зарычать на орка]" lock = "false" id = "3">
				<dialog title = "[Орк испугался и убежал]">
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>	
			<line title = "[Выход]" lock = "false" id = "0"/>
		</dialog>		
	</person>
	
	<person name = "Гнусный Орк с пушкой из НКР_connected">
		<dialog title = "Бурлы мурлы, моя твоя не понимать...моя садись, твоя поехали...">
			<line title = "Ты кто такой?" lock = "false" id = "1">
				<dialog title = "Моя друг человека!">
					<line title = "Я слежу за тобой..." lock = "false" id = "10">
						<dialog title = "Да-да следить, следить...">
							<line title = "[Про себя : ну и мурло же ты][Выход]" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "Выходи сражаться исчадие ада!" lock = "false" id = "2">
				<dialog title = "[Орк сопит] Я боюсь тебя человека!">
					<line title = "[достать НЕЗАМЕТНО бозар] А ты не бойся, сейчас мы подружимся!" lock = "false" id = "11">
						<dialog title = "Дружить, давай дружить!">
							<line title = "[Напасть]" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "[Грозно зарычать на орка]" lock = "false" id = "3">
				<dialog title = "[Орк испугался и убежал]">
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>	
			<line title = "[Выход]" lock = "false" id = "0"/>
		</dialog>		
	</person>

</data>
Вот такая прелесть получилась. Скажу сразу, никакого опыта ранее в создании подобного не было, а тем более в создании РПГ или ММО. Поэтому основываясь на своем опыте и интуиции решил попробовать свои силы. ( специально для Akopolipsis - что можно создавать модули без 101 вопроса на форуме, основываясь на своем опыте ).
Так же попрошу людей, кто хорошо разбирается в XMLnode - сообщить, если данные конструкции избыточны, так как сам я очень мало работал с такими запросами.
Поехали.

1. Общие понятия.
Прежде чем начать писать данный модуль, я пару дней в голове прокручивал все возможные удобства/неудобства/реализации и только потом присел за перо.

Первым делом создадим самый простейший класс холдер XML диалогов, пускай будет, при ините сразу пихнем туда весь листик.

Код AS3:
package admin.models 
{
	/**
	 * ...
	 * @author in4core progression lab
	 */
	public class DialogsStatic 
	{
		private static var _dialogs:XML = null;
 
		public static function insertDialogs(xml:XML):void
		{
			_dialogs = xml;
		}
 
		public static function getDialogsByPersonName(id:String):XMLList
		{
			return _dialogs.person.(@name == id)
		}
	}
}
Внимание на getDialogsByPersonName пока не обращаем, о нем скажу позже.
Далее создадим стандартный набор персонажа игры, а именно PersonView и PersonModel
View
Код AS3:
package admin.views 
{
	import admin.models.PersonModel;
	import flash.display.Sprite;
	import flash.events.Event;
 
	/**
	 * ...
	 * @author in4core progression lab
	 */
	public class PersonView extends Sprite 
	{
		private var _model:PersonModel;
 
		public function PersonView(model:PersonModel) 
		{
			this._model = model;
 
			addEventListener(Event.ADDED_TO_STAGE , onAdded);
		}
 
		private function onAdded(e:Event):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, onAdded);
 
			graphics.beginFill(0x0);
			graphics.drawRect(0, 0, 50, 50);
		}
 
		public function get model():PersonModel 
		{
			return _model;
		}
 
	}
 
}
Комментарии я думаю тут излишни, все просто и понятно.
Model
Код AS3:
package admin.models 
{
	import flash.events.EventDispatcher;
 
	/**
	 * ...
	 * @author in4core progression lab
	 */
	public class PersonModel extends EventDispatcher 
	{
		private var _name:String = "default";
		private var _dialogues:XMLList = null;
 
		public function PersonModel() 
		{
 
		}
 
		public function get isSpeakable():Boolean
		{
			if (_dialogues) return true;
 
			return false;
		}
 
		public function get name():String 
		{
			return _name;
		}
 
		public function set name(value:String):void 
		{
			_name = value;
		}
 
		public function get dialogues():XMLList 
		{
			return _dialogues;
		}
 
		public function set dialogues(value:XMLList):void 
		{
			_dialogues = value;
		}
 
	}
 
}
Опять же все просто. Метод( геттер) isSpeakable - на всякий случай имеем, дабы понять, говорящий персонаж ( будет ли с нами общаться вообще ) или нет. В данной реализации это проверяется, как видно по тому, сунули мы диалог в модельку или нет. Сразу же забегая вперед, персонаж может сначала быть доступен для разговора, а потом нет, в зависимости от условий или самой сути игры, в этом случае, конечно же, данный метод придется переписать, хоть на Boolean и в зависимости от условий менять значение. Но, а пока - все проще сделаем.

Где нибудь на просторах BaseView потихоньку начнем собирать первого персонажа и структуру.

Код AS3:
private function onAdded(e:Event):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, onAdded);
 
			var xml:XML = new XML( new _xmls() );
 
			DialogsStatic.insertDialogs( xml );
 
			var model:PersonModel = new PersonModel();
			model.name = "James";
			model.dialogues = DialogsStatic.getDialogsByPersonName(model.name);
			var person:PersonView = new PersonView(model);
			person.addEventListener(MouseEvent.CLICK , createDialog);
 
			this.addChild(person);
		}
Вроде бы создали.. уф... Столько кода понаписали, а видимо только черный квадратик ! Шикарно !
Теперь мы ясно представляем для чего был метод getDialogsByPersonName - а именно, чтобы ввести инъекцию тех диалогов в модельку, которые именно нужны ей, а не все подряд.
Ну чтож с общими понятиями структуры разобрались. Переходим к созданию ХМЛ первого персонажа ( инфо )

2. Создаем диалоги персонажей

Код:
<?xml version="1.0" encoding="utf-8" ?>
<data>

	<person name = "James">
		<dialog title = "Эй бро, как делишки?">
			<line title = "Здорова, все ок." lock = "false" id = "1">
				<dialog title = "Куда напрвляешься?">
					<line title = "Eду в СПБ." lock = "false" id = "10" >
						<dialog title = "Ну удачи бро.">
							<line title = "Бывай!" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "Хреново!" lock = "false" id = "2">
				<dialog title = "Да не ссы ты, я сто раз так делал!!!">
					<line title = "Ладно, но с тебя пивас!" lock = "false" id = "11">
						<dialog title = "Заметано.">
							<line title = "Бывай!" lock = "false" id = "0"/>
							<line title = "[Выход]" lock = "false" id = "0"/>
						</dialog>
					</line>
					<line title = "[Назад]" lock = "false" id = "-1"/>
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>
			<line title = "Да пошел ты!" lock = "false" id = "3">
				<dialog title = "Heh Fuck Himself!">
					<line title = "[Выход]" lock = "false" id = "0"/>
				</dialog>
			</line>	
			<line title = "[Выход]" lock = "false" id = "0"/>
		</dialog>		
	</person>
</data>
Сейчас попробую пояснить, что есть что и для чего. Ветки типа dialog - будут создавать диалог с персонажем, где аттрибут title в dialog - это речь персонажа с которым мы говорим, а аттрибут title в line - варианты наших ответов. Поговорим о замечательных аттрибутах ID. Они нам нужны для навигации, поиска, изменений и т.п. без них никуда, - не вижу особой проблемы в каждом диалоге прописывать ID строчки ответа, причем они не обязательно должны быть уникальными в каждом персонаже, можно плодить одинаковые с 1 до N всегда, собственно я так и делаю. Единственное, что вы заметите, я использовал айди 10 и 11 для нижних уровней, мне так было проще не потеряться. Да и в принципе можно взять себе за основу - каждый новый уровень +10 ID. Так проще и понятнее, сомневаюсь, что где нибудь у вас будет БОЛЕЕ чем 10 вариантов ответов, а если будет, тогда с 1 по N как писал выше.
Для закрытия диалога я взял ID = 0 , для возврата на предыдущий диалог ID = -1. Далее вы можете регистрировать любые коды с -Х для действий, например -2 - начать бой из диалога, -3 получить квест, -4 отменить квест и т.п. Особо много тут тоже не предвидится.
Аттрибут lock - это самое главное во всей этой кухне. Данный аттрибут показывает - показывать строчку ответа или нет.

3. DialogView и Manager диалогов
И так все готово, чтобы показать на экран наш первый диалог, для этого нам потребуется вид диалога в первую очередь, ну , а чтобы работать с диалогами некий менеджер диалогов. Приступим.

Код AS3:
package admin.views 
{
	import flash.display.Sprite;
	import flash.events.Event;
 
	/**
	 * ...
	 * @author in4core progression lab
	 */
	public class DialogView extends Sprite 
	{
 
		public function DialogView() 
		{
			addEventListener(Event.ADDED_TO_STAGE , onAdded);
		}
 
		private function onAdded(e:Event):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, onAdded);
 
			graphics.beginFill(0xAA0000);
			graphics.drawRect(0, 0, 600 , 200);
			graphics.beginFill(0x880000);
			graphics.drawRect(1, 1, 598 , 198);
		}
 
		public function createDialog( list : XMLList , name : String ):void
		{
			this.removeChildren();
 
			var header:DialogLine = new DialogLine();
			header.create(name + " : " + list.@title , null);
			addChild(header);
 
			header.x = 10;
			header.y = 10;
 
			var lst:XMLList = list.line.(@lock == false);
 
			for (var i:int = 0; i < lst.length(); i++)
			{
				var line:DialogLine = new DialogLine();
 
				line.create( (i+1) + ". " + lst[i].@title, lst[i].@id); 
 
				addChild(line);
 
				line.y = line.height * i + header.height + 20;
				line.x = 10;
			}
		}
 
		public function destroyDialog():void 
		{
			this.removeChildren();
		}
	}
}
Здесь подробно рассмотрим метод createDialog. Как мы видим, там же используется некий класс DialogLine - а именно наших вариантов ответов. Сразу же его и покажем

Код AS3:
package admin.views 
{
	import admin.utils.DialogsEvents;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.text.TextField;
 
	/**
	 * ...
	 * @author in4core progression lab
	 */
	public class DialogLine extends Sprite 
	{
		private var _field:TextField = new TextField();
		private var _id:String = null;
 
		public function DialogLine() 
		{
			super();
			addChild(this._field);
		}
 
		public function create(text:String , id:String):void
		{
			this._id = id;
 
			this._field.text = text;
			this._field.width = this._field.textWidth + 5;
			this._field.height = 30;
			this._field.selectable = false;
 
			if (id != null) 
			{
				this._field.textColor = 0xFF0000;
				this._field.addEventListener(MouseEvent.CLICK , lineSelected);
				this._field.addEventListener(MouseEvent.MOUSE_OVER , onOver);
				this._field.addEventListener(MouseEvent.MOUSE_OUT , onOut);
			}
			else 
			{
				this._field.textColor = 0xFFFFFF;
			}
		}
 
		private function onOut(e:MouseEvent):void 
		{
			this._field.textColor = 0xFF0000;
		}
 
		private function onOver(e:MouseEvent):void 
		{
			this._field.textColor = 0xFFFF00;
		}
 
		private function lineSelected(e:MouseEvent):void 
		{
			this.dispatchEvent(new DialogsEvents(DialogsEvents.LINE_SELECTED , _id , true));
		}
 
		public function get id():String 
		{
			return this._id;
		}
 
	}
 
}
Думаю комментарии тут излишни, все просто и удобно. Вернемся к методу createDialog у класса DialogView.
Для создания заголовка так же используем DialogLine с id = null. Сделал как попроще, не плодя все новые и новые сущности. Ну , а далее пробегаясь по нодам ХМЛлиста - забиваем список. Примечаем, что при нажатии на вариант ответа мы отправляем событие LINE_SELECTED - наверх используя bubbles = true. Собственно говоря подписать на клик можно и внутри DialogView, но раз все события овера, аута тут - пускай и клик, как мышиное событие там же висит - нестрашно, на скорость явно не повлияет, ведь одновременно мы можем открыть только один диалог Так что в данном модуле о скорости вообще речь поднимать не стоит.

Перейдем к созданию менеджера ( контроллера ) диалогов. Что он будет делать : работать с моделями персонажей по изменению диалог-листа, обрабатывать переходы между уровнями диалогов ( и т.п. в этом духе, в зависимости от ваших нужд, оценивать получение квестов например ).

На самом деле, все что описал до этого и так понятно, простейшие стандартные классы, вся кухня конечно же строится именно на данном менеджере, поэтому большая часть времени ушла конечно на его проектировку ( ну , а если честно, на вспоминание работы с XMLnode ).

Пожалуй сразу выложу весь листинг данного класса

Код AS3:
package admin.controllers 
{
	import admin.models.PersonModel;
	import admin.utils.DialogsEvents;
	import admin.views.DialogView;
	import flash.events.EventDispatcher;
 
	/**
	 * ...
	 * @author in4core progression lab
	 */
	public class DialogManager extends EventDispatcher 
	{
		private var _model:PersonModel;
 
		private var _lastDialog:Vector.<XMLList> = new Vector.<XMLList>();
		private var _curDialog:XMLList;
		private var _personModels:Vector.<PersonModel> = new Vector.<PersonModel>();
 
		public function DialogManager() 
		{
 
		}
 
		public function addModel(model:PersonModel):void
		{
			this._personModels.push(model);
		}
 
		public function changeConnectedDialog(name:String , id:String) : void
		{
			for (var i:int = 0; i < this._personModels.length; i++)
			{
				if (this._personModels[i].name == name)
				{
					this._personModels[i].dialogues..*.line.(@id == id).@lock = true;
				}
			}
		}
 
		public function connect(model:PersonModel):void
		{
			this._model = model;
			this._lastDialog = new Vector.<XMLList>();
			this._curDialog = model.dialogues.dialog;
			this._lastDialog.push(this._curDialog);
		}
 
		public function getCurrentDialog():XMLList
		{
			return this._curDialog;
		}
 
		public function logic(id:String):void 
		{
			checkInLock(id);
			checkInConnected(id);
 
			if (id == "0") 
			{
				this.dispatchEvent(new DialogsEvents(DialogsEvents.CLOSE_DIALOG));
			}
			else if (id == "-1")
			{
				this._curDialog = this._lastDialog[this._lastDialog.length - 1];
				this._lastDialog.pop();
				this.dispatchEvent(new DialogsEvents(DialogsEvents.DIALOG_LEVEL_CHANGED));
			}
			else 
			{
				this._lastDialog.push(this._curDialog);
				this._curDialog = this._curDialog.line.(@id == id).dialog;
				this.dispatchEvent(new DialogsEvents(DialogsEvents.DIALOG_LEVEL_CHANGED));
			}
		}
 
		private function checkInConnected(id:String):void 
		{
			var search:String = this._model.dialogues..*.line.(@id == id).@lockedperson;
			var ids:String = this._model.dialogues..*.line.(@id == id).@lockedpersonid;
 
			if ( search != "" )
			{
				dispatchEvent(new DialogsEvents(DialogsEvents.CONNECTED_PERSONS , [ search , ids ]));
 
				changeConnectedDialog(search , ids);
			}
		}
 
		private function checkInLock(id:String):void 
		{
			var search:String = this._model.dialogues..*.line.(@id == id).@lockedid;
 
			if ( search != "" )
			{
				this._model.dialogues..*.line.(@id == search).@lock = true;
 
				for (var i:int = 0 ; i < this._lastDialog.length; i++)
				{
					this._lastDialog[i]..*.line.(@id == search).@lock = true;
				}
 
			}
		}
 
		public function get model():PersonModel 
		{
			return _model;
		}
	}
}
Сверхъестественного тут тоже ничего нет, вся проблема была, как я уже сказал на вспоминание XMLnode. И так, создавая диалог, запускаем метод connect - дабы начать работать с конкретной моделью и диалогом. Далее думаю все понятно по названию методов, основным здесь является одноименный logic - который и работает с диалогом напрямую, вычисляет по ID - что делать, и соответственно посылает события, которые мы можем обработать по нашему желанию.
Давай те дополним просторы BaseView , теперь он немного разросся.
Код AS3:
package admin.views 
{
	import admin.controllers.DialogManager;
	import admin.models.BaseModel;
	import admin.models.DialogsStatic;
	import admin.models.PersonModel;
	import admin.utils.DialogsEvents;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
 
	/**
	 * ...
	 * @author in4core progression lab
	 */
	public final class BaseView extends Sprite 
	{
		[Embed(source = "../../../bin/dialogs.xml", mimeType="application/octet-stream")] private var _xmls:Class;
 
		private var _baseModel:BaseModel;
		private var _dview:DialogView = new DialogView();
		private var _dmanager:DialogManager = new DialogManager();
 
		public function BaseView() 
		{
			addEventListener(Event.ADDED_TO_STAGE, onAdded);
		}
 
		public function set baseModel(model:BaseModel):void
		{
			this._baseModel = model;
		}
 
		private function onAdded(e:Event):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, onAdded);
 
			var xml:XML = new XML( new _xmls() );
 
			DialogsStatic.insertDialogs( xml );
 
			addChild(this._dview);
			this._dview.y = stage.stageHeight - this._dview.height - 20;
			this._dview.x = stage.stageWidth / 2 - this._dview.width / 2;
 
			this._dview.addEventListener(DialogsEvents.LINE_SELECTED , onDialogLineSelected);
			this._dmanager.addEventListener(DialogsEvents.DIALOG_LEVEL_CHANGED , onUpdateDialogLevel);
			this._dmanager.addEventListener(DialogsEvents.CLOSE_DIALOG , onDialogCloses);
			this._dmanager.addEventListener(DialogsEvents.CONNECTED_PERSONS , onConnectedPersons);
 
			var model:PersonModel = new PersonModel();
			model.name = "James";
			model.dialogues = DialogsStatic.getDialogsByPersonName(model.name);
			var person:PersonView = new PersonView(model);
			person.addEventListener(MouseEvent.CLICK , createDialog);
 
			this.addChild(person);
 
			var model1:PersonModel = new PersonModel();
			model1.name = "Гнусный Орк";
			model1.dialogues = DialogsStatic.getDialogsByPersonName(model1.name);
			var person1:PersonView = new PersonView(model1);
			person1.addEventListener(MouseEvent.CLICK , createDialog);
 
			this.addChild(person1);
 
			person1.x = 100;
 
			var model2:PersonModel = new PersonModel();
			model2.name = "Гнусный Орк с пушкой из НКР";
			model2.dialogues = DialogsStatic.getDialogsByPersonName(model2.name);
			var person2:PersonView = new PersonView(model2);
			person2.addEventListener(MouseEvent.CLICK , createDialog);
 
			this.addChild(person2);
 
			person2.x = 200;
 
			var model3:PersonModel = new PersonModel();
			model3.name = "Гнусный Орк с пушкой из НКР_connected";
			model3.dialogues = DialogsStatic.getDialogsByPersonName(model3.name);
			var person3:PersonView = new PersonView(model3);
			person3.addEventListener(MouseEvent.CLICK , createDialog);
 
			this.addChild(person3);
 
			person3.x = 300;
 
			this._dmanager.addModel(model2);
			this._dmanager.addModel(model3);
		}
 
		private function onConnectedPersons(e:DialogsEvents):void 
		{
 
		}
 
		private function onDialogCloses(e:DialogsEvents):void 
		{
			this._dview.destroyDialog();
		}
 
		private function onUpdateDialogLevel(e:DialogsEvents):void 
		{
			this._dview.createDialog( this._dmanager.getCurrentDialog() , this._dmanager.model.name );
		}
 
		private function onDialogLineSelected(e:DialogsEvents):void 
		{
			this._dmanager.logic(e.params as String);
		}
 
		private function createDialog(e:MouseEvent):void 
		{
			const person:PersonView = e.currentTarget as PersonView;
			const model:PersonModel = person.model;
			this._dmanager.connect(model);
			onUpdateDialogLevel(null);
		}
	}
 
}
Отлично, система заряжена. Остается только написать корректный XML - и в путь.
Перед тем, как закончить , хотелось бы сказать пару слов о сложных диалогах и взаимодействии. Выше я описал, что все взаимодействия , а так же скрытие веток и открытие происходят благодаря аттрибуту lock в XML. Когда мы хотим из одного диалога изменить часть диалога другого персонажа - я использовал следующие аттрибуты lockedperson и lockedpersonid, благодаря первому мы узнаем У КОГО будем менять, благодаря второму, узнаем ЧТО будем менять.
Безусловно чем больше будет аттрибутов в итоге, тем нагруженней у вас будет Manager - но это не так страшно, ведь в данной статье я уже показал диалогового функционала БОЛЬШЕ, чем - во всех современных играх вместе взятых ( шучу ). - но тем не менее, благодаря им, можно делать очень крутые взаимодействия.
Хотел показать еще пару примеров, например - получение квеста, отклонение квеста и заново его получение, но как оказалось и подумалось - смысла в этом нет, это всего лишь доп аттрибут и стандартная работа с lock, как в примере 2го-4го персонажа.
Если средним-начинающим что то непонятно, выложу полный листинг всех классов начиная с Main.
P.s. по коду прошу грозно не ругаться, статья вышла большая, если видите ошибку/недоделку/нонсенс - сообщите, культурно поправим.
P.p.s - про XMLnode - уже написал, прошу обратить на это особое внимание, если можно упростить записи, обязательно скажите, не молчите!

Всем спасибо за внимание!
Вложения
Тип файла: swf dd.swf (9.9 Кб, 980 просмотров)
Всего комментариев 34

Комментарии

Старый 07.05.2014 15:48 Hauts вне форума
Hauts
 
Аватар для Hauts
Мне кажется, что "создаем подобие old School типа Fallout" — вот эта часть названия лишняя.
Старый 07.05.2014 15:54 in4core вне форума
in4core
 
Аватар для in4core
Hauts - может быть, но чем? Система диалогов точно такая же, скрытие, открытие веток, получение квестов, взаимодействие персонажей. Али я не прав?
Старый 07.05.2014 22:51 Tails вне форума
Tails
 
Аватар для Tails
Статья однозначно интересная!

Раз уж мы взялись создавать отдельно обособленную систему диалогов, я думаю, что было-бы хорошо если она:
1. Будет не привязана к конкретной апи визуализации.
2. Будет иметь более 1 формата ввода данных "из коробки", как минимум JSON
3. Иметь возможность для расширения имеющихся парсеров.
4. В связи с предыдущими пунктами - иметь так-же чёткую, где-то отдельно описанную на кириллице архитектуру построения диалогов.
5. Редактор?

Сам ответа на эти вопросы не знаю, предлагаю автору подумать над ними и предложить решение.
Старый 07.05.2014 23:05 in4core вне форума
in4core
 
Аватар для in4core
1. Не понял честно говоря данного вопроса. Модуль совершенно отдельный, он не привязан ни к чему по сути. Реализовывать его можно где угодно в вашем приложении. Самое главное тут конечно Manager - на нем все строится , а это обычный контроллер по сути. Все остальное простые модельки и виды, которые строятся по вашему усмотрению. Данная реализация просто как пример, как все это можно сделать и как будет выглядеть и работать, а писать под конкретный проект это дело уже другое. Если дадите такую задачу, попробую помочь Вам.

2. Вот тут честно говоря, я против. JSON в диалоговом представлении реально неудобен. XML - здесь наше все, мы видим четко структуру , внутренности, уровни и т.п. XML - здесь лучший вариант. Но если кому то удобнее в JSON - переделать под JSON - 5 минут времени, но честно дружище - замучаешься ты с ним это уж точно.

3.Тут тоже честно говоря не понял вопроса, о каких парсерах речь. Поподробнее если можно.
4. Да, это безусловно нужно сделать , так сказать описать протакол, но это делать нужно думаю уже для реального проекта, зачем для ознакомления писать протакол работы?

5. Вот это интересный вопрос, и честно говоря - один из самых простых. Создать редактор, на том же флеше ( мини приложение ) - проще простого, редактор + создатель диалогов.
Если действительно Вам кажется это сложно/интересно, я напишу в след статье такой редактор.
Старый 07.05.2014 23:56 elder_Nosferatu вне форума
elder_Nosferatu
 
Аватар для elder_Nosferatu
@in4core

Во первых, зря ты так радикально (хоть и в вежливой форме) отверг все выше предложеные пункты. Ты же не ответ новичку даешь на поставленый вопрос, а делаешь запись в блоге (!). Как по мне, так очень даже прикольно было бы у себя выложить не просто идею в сырой форме, а целый инструмент, который этой же записью в блоге описан, к которому есть пример (уже есть) и который можно запросто скачать и пихнуть себе в прожект. Тогда эта запись в блоге будет вполне себе полезным шагом, а не просто выпендрежем и носоутиралкой для Acopalipsis`а.

Во вторых... Не понял при чем здесь "old School", но для "Fallout" будет явно неудобно. У меня нет опыта в написании масштабных нелинейных игр, но могу предположить, что после написания/приобретения игрового движка игроделы, помимо графическо-звукового контента, создают не просто диалоги, а сюжет. Вполне логично, что для этих целей должен быть один инструмент. А теперь Fallout. Во второй части этой замечательной игры мне удалось пройти в начале Храм Испытаний (убить несколько монстров и получить левел_ап), а потом на одних только диалогах вырасти до 6-го уровня. Осмелюсь предположить, что реплики в диалоге - такие же важные игровые действия (события), как и убийство оппонентов, получение сюжетно важных (и не только) предметов, отпирания дверей и разгадывание головоломок. И если это так, то описание части сюжета в диалогах отдельно от остальной части (сюжета) есть вполне сравнимое действие с написанием AS3-приложения в кадрах...
Короче, предлагаю вынести всю игровую логику из диалогов и делегировать ее отдельно стоящему контроллеру/менеджеру игровых действий, а от самих диалогов оставить только презентативную часть. Сами тексты реплик тоже не стоит относить к диалогам. Если их держать отдельно то и перевод на другой язык проще осуществить. К тому же old School, не old School, но и в древнем Fallout были озвучено-анимированые ветки диалогов.

Написал много, попробую сократить. Реплика диалога - разновидность игрового действия и работать с ней нужно наравне с другими действиями. А вот как она будет представлена игроку - это вопрос отображения, но не вопрос игровой логики.

ЗЫ: тема очень интересная, так что хотелось бы узнать мысли людей, имевших опыт в создании игр подобного направления.
Старый 08.05.2014 00:07 in4core вне форума
in4core
 
Аватар для in4core
elder_Nosferatu - я не отвергаю на самом деле, просто не готов заниматься этим ВНЕ реального проекта, в данном случае это была просто проба пера, и как оказалась уже интересна многим.

По поводу - логики , уровней и т.п.
Как раз таки все верно я сделал, я не стал плодить сущности лишние, как ты говоришь выше. Тут все наглядно и просто и вырасти до 99 уровня на таких диалогах как раз очень просто. Я бы хотел услышать от тебя констуртивизма, что у тебя по твоему мнению не получается?
Вот пример, я даю ID -2 и exp : 500 Это значит, если нажав на данную реплику я хватаю 500 exp. А далее я могу это переправить хоть в контроллер уровней, хоть куда.
В примере у меня 0, -1 ID зарезервированы, -2 просто exp , -3 exp + level , -4 начать бой. Вот и все, их не так много получится, не более 10. Зато ВСЯ структура будет построена в XML ! А если сделать редактор, то вообще полет. А сюжет тут не причем, все решается lock - изменение веток сюжета. Изначально описывается ВЕСЬ сценарий и впперед, удалять, открывать, давать ЕХП. все просто. Я как фанат флаута и 1го и 2го ( особенно ) раз 20 прошел, заявляю, что данная система 100% его покроет.
Старый 08.05.2014 00:14 in4core вне форума
in4core
 
Аватар для in4core
P.s.
Цитата:
К тому же old School, не old School
what ?
Старый 08.05.2014 00:17 Tails вне форума
Tails
 
Аватар для Tails
Пробежался по коду ещё раз, не скажу, что вник полностью на 100%
Сперва отвечу:
  1. Ок, понятно, очень хорошо когда мы можем подключить модуль к любому апи вывода.
  2. Полностью согласен, что xml читается гораздо лучше. Однако этот единственный аргумент в пользу xml не может быть поводом для отказа от остальных форматов, у которых есть безусловные плюсы. Самый большой недостаток xml - его потребление памяти, что может сыграть решающую роль на мобилках.
  3. Про парсеры ниже.
  4. Зачем обязательно для реального? У нас есть определённое "ядро" исполнения, вот то что оно может разобрать, для него и стоит описать отдельным текстом. В ознакомительных целях очень удобно иметь перед глазами подробную инструкцию на человеческом языке.
  5. Редактор конечно дело очень полезное, он дополнительно сведёт на нет смысл использования xml. Однако как-бы там ни-было, задача эта второстепенная, пока сосредоточимся на основной цели.

Теперь вкратце, что думается:
this-ы это зло! шутка, это дело вкуса лично каждого, а о вкусах не спорят
Что бросается в глаза, так это то, что xml постоянно весит в памяти, как я понимаю. Это действительно очень не хорошо. System.disposeXML не зря был добавлен в флеш плеер.
Ещё весь этот механизм кажется немного слипшемся, было-бы хорошо разбить его на отдельные задачи:
  1. Отдельно описать ядро исполнения, думаю, тут можно обойтись без MVC и составить сущности в единственном экземпляре, без деления на модели и контроллеры. Сущности должны быть расширяемыми и максимально открытыми. Они должны уметь тот самый "базовый" функционал: переходы, ответы, возвраты.
  2. Каждая сущность - отдельный класс, вплоть до выведения классов - вариант ответа пользователя. Столь детальное деление необходимо для построения гибкой системы, однако, тут очень важно учесть, что-бы эта детальность не мешала программисту и работала по дефолту на ура, а при необходимости расширения - давала широкие возможности.
  3. Избавиться от динамического разбора XML во время исполнения. Вынести разбор XML в отдельный модуль - парсер, который будет уметь разбирать его лишь единожды и самостоятельно создавать модель сущностей системы.
  4. Добавить аналогичный XML - парсер JSON.
Старый 08.05.2014 00:21 elder_Nosferatu вне форума
elder_Nosferatu
 
Аватар для elder_Nosferatu
У меня разработка таких игр до клавиатуры не доходила, так что утверждать ничего не берусь, но мне кажется, что для дизайнера уровней не должно быть разницы на какое игровое действие (узнать_от_проститутки_как_открыть_дверь, найти_открывачку_для_дверей, открыть_дверь) вешать сюжетный триггер. У этого человека есть редактор уровней и в нем он все и делает. Так что мне больше нравится идея описания всего сюжета одной структурой, а не отдельных составляющих сюжета отдельными структурами.
Старый 08.05.2014 00:30 in4core вне форума
in4core
 
Аватар для in4core
elder_Nosferatu - благодарбю за ответ. Твоя система вполне себе система, почему бы тебе ее не описать и мы совместную статью сделаем? Попробуй сделать такой же простой пример как у меня. Вдруг людам больше понравится, сколько людей стока и мнений.
Старый 08.05.2014 00:38 in4core вне форума
in4core
 
Аватар для in4core
Цитата:
он дополнительно сведёт на нет смысл использования xml.
Как раз таки наоборот, редактор будет создавать XML файлы. Держать в памяти и не надо! Зачем?* Это просто пример. Можно для каждого перса иметь ОТДЕЛЬНЫЙ файл который ничего не занимает. И методом connect - подгружать этот файл. Я думал Вам ребята взрослые это и так понятно будет, новички бы еще могли спросить , но вы Шучу.

Tails - я не откажу тебе, если это правда тебе нужно , пригодится и т.п. И напишу для тебя в этой статье пункты которые действително покажутся важными. С чего начать хочешь ?
Старый 08.05.2014 00:43 elder_Nosferatu вне форума
elder_Nosferatu
 
Аватар для elder_Nosferatu
Вот что-что, а умение увиливать у тебя прокачано класно!

А твое предложение я пожалуй отклоню:
* Если я оперативно предложил другой вариант реализации, значит какие то мысли у меня уже были до этого.
* Если я не подтверждаю свои слова конткретными примерами реализации, значит их нет.
* Если у меня нет готовой реализации до сих пор (не смотря на то, что я давно об этом размышлял), значит есть у меня дела поважнее (обидно, но так и есть).
Так что сам, батенька, сам.
Старый 08.05.2014 00:46 in4core вне форума
in4core
 
Аватар для in4core
elder_Nosferatu - ну ты просто душка! У самого то, умение соскачить уже и не качается дальше, макс левел А если по делу, я на словах то понял, более менее, что ты написал, а как реализовать ХЗ. Поэтому и сказал - тебе же оно виднее, твоя же идея. А так я за, распиши в личку подробнее ТЗ, я может быть возьмусь в выходные , гляну.
Старый 08.05.2014 16:41 elder_Nosferatu вне форума
elder_Nosferatu
 
Аватар для elder_Nosferatu
Ну если следовать моей идеи, то от "система диалогов" будет просто вьюшкой и перестанет быть "системой" в твоем понимании (никакой логики внутри, просто отображение полученых данных: текст реплики, озвученая реплика с текстом, анимированая и озвученая реплика с текстом). Вместо этого прийдется создавать более общую сущность - "систему игровых событий".
А вот тут я и остановился. Общая идея как бы и есть, но детализировать ее до полноценного ТЗ без практики не смогу. Сперва нужно самому пройтись по граблям на реальном прожекте. В связи с этим я и просил в первом своем посте отозваться людей с опытом.
Старый 08.05.2014 18:23 in4core вне форума
in4core
 
Аватар для in4core
elder_Nosferatu - в том то и дело, что это ТОЛЬКО идея. Ты предлагаешь не использовать ID и прочие теги в диалогах как я понял, или просто ID текста и все, а уже логику полностью описывать в приложении. Просто поразмысли над тем - что тебе придется создать для этого целую систему событий , а так же 101 сущность. А как известно чем БОЛЬШЕ система, тем сложнее ее поддерживать и модифицировать. То есть в твоем варианте - кроме сложной системы, я прелестей не вижу. Наоборот - только минуса.
Как ни крути и моя система будет работать по той же схеме, что ты описал в любом случае. Менеджер будет управлять диалогом всего лишь, и посылать события о его изменении, а дальше... а дальше уже создается система событий обработки и взамиодействия. То есть я разослал ID : 3 - далее какой то лог-контроллер рассматривает его, и начинает работу с моделью, где проходит вся логика ( что будет дальше, квест там, изменение мира, или еще чего ) , потом снова отправляется событие, но уже в виды, которые отображают. - Стандартный MVC собтсвенно говоря, просто В РЕАЛЬНОМ проекте, структур будет больше, а начальная составляющая , как я описал в статье всегда одна. Сам то менеджер может быть разбит на под-менеджеры и т.п. и управлять ( отсылать события ) - разными частями, а не только общей, как в примере.
Старый 08.05.2014 20:21 Tails вне форума
Tails
 
Аватар для Tails
Нет нет, для меня писать ничего не нужно. Мне просто интересно высказаться самому и послушать мнения других людей по этой теме, посмотреть варианты реализаций.
Старый 09.05.2014 00:48 Korchy вне форума
Korchy
 
Аватар для Korchy
А как быть, если после прохождения некоей цепочки ответов нужно вернуться к одному из предыдущих экранов с ответами, но в нем должен добавиться еще одна сточка с новым вариантом ответа?
Получается нужно дублировать всю вышестоящую ветку или надо предусматривать варианты перемещения между экранами с выводом ответов в зависимости от места источника перехода.
Старый 09.05.2014 05:13 in4core вне форума
in4core
 
Аватар для in4core
Цитата:
А как быть, если после прохождения некоей цепочки ответов нужно вернуться к одному из предыдущих экранов с ответами, но в нем должен добавиться еще одна сточка с новым вариантом ответа?
Получается нужно дублировать всю вышестоящую ветку или надо предусматривать варианты перемещения между экранами с выводом ответов в зависимости от места источника перехода.
Дружище, прости, но я не понял твоего вопроса, ПАРАМЕТР lock все решает, описано это. Зашел ГЛУБОКО и изменил ДО этого, что было - показано как. + ID -1 ( back )
Старый 09.05.2014 05:15 in4core вне форума
in4core
 
Аватар для in4core
Твой вопрос кстати не просто рассмотрен во втором ПЕРСОНАЖЕ - но еще и показан
Старый 13.05.2014 19:54 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Давайте вместо XML'а замутим YAML!
Старый 13.05.2014 20:11 in4core вне форума
in4core
 
Аватар для in4core
Цитата:
Давайте вместо XML'а замутим YAML!
Я особо не сведущ в этих делах, поэтому зашел в вики и глянул о чем речь, как выяснилось тот же json . В чем разница то принципиальная ? Даж интересно
Старый 14.05.2014 12:03 gloomyBrain вне форума
gloomyBrain
 
Аватар для gloomyBrain
Поддерживаются комментарии и можно делать (если парсер позволяет) наследование/объединение фалов. И еще много чего, все есть в wiki. Пожалуй, это самый удобный формат для конфигов на сегодняшний день.
Старый 16.05.2014 00:19 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Человекочитаемость у YAMLа выше, чем у XML'а с JSON'ом вместе взятых.
Старый 16.05.2014 00:29 dimarik вне форума
dimarik
 
Аватар для dimarik
Раздел News на офсайте yaml.org оставил чувство мертвечинки.
Старый 19.05.2014 15:23 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
А что, у http://www.xml.org/ выходят еженедельные обновления?
Старый 19.05.2014 18:41 toFL вне форума
toFL
Цитата:
Система диалогов, создаем подобие old School типа Fallout.
Помню лет 15 назад на Pascal писал такую штуку. Всего-то через 2 года после выхода первого Fallout
Обновил(-а) toFL 19.05.2014 в 19:22
Старый 19.05.2014 21:17 in4core вне форума
in4core
 
Аватар для in4core
Цитата:
А что, у http://www.xml.org/ выходят еженедельные обновления?
Так в этом случае обновлять то уже нечего, это уже устойчивое чтиво, а вот йамл все таки свежатинка
Старый 20.05.2014 11:24 mayakwd вне форума
mayakwd
 
Аватар для mayakwd
13 лет "свежатине".
Старый 20.05.2014 13:33 in4core вне форума
in4core
 
Аватар для in4core
Цитата:
13 лет "свежатине".
А хмлу сколько \? Это как деда с внуком сранивать!
Старый 20.05.2014 14:02 ChuwY вне форума
ChuwY
 
Аватар для ChuwY
Цитата:
А хмлу сколько \? Это как деда с внуком сранивать!
16, вроде как.
Старый 20.05.2014 14:53 in4core вне форума
in4core
 
Аватар для in4core
Я конечно хз, но 16 лет назад уже был as2 а xml тока появиолся? )))) Помоему языку больше лет явно, я бы дал 30. хотя кто знает. Откуда инфа ?
Старый 20.05.2014 15:03 in4core вне форума
in4core
 
Аватар для in4core
Прочекал 1996 год. Странно, думал реально больше. Тогда сорр. закрыли
Старый 22.05.2014 01:51 КорДум вне форума
КорДум
 
Аватар для КорДум
Цитата:
16 лет назад уже был as2
Ну как бы 11 лет назад, он появился в седьмой версии FP в 2003 году.
Старый 15.07.2015 22:15 Wells77 вне форума
Wells77
Очень интересная статья - начал писать код а потом заметил что нехватает import admin.utils.DialogsEvents; И толком ничего не протестил. Если можна, киньте классы на profik@rambler.ru или свяжитесь по скайпу profikus1982. Делаю неплохую рпг в стиле фаллаута, и фактически осталось систему квестов допилить на чем и застопорился.

ЗЫ: BaseModel тоже не наблюдаю
Обновил(-а) Wells77 15.07.2015 в 22:57
 

 


Часовой пояс GMT +4, время: 15:57.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.