FlashImp Flash Implosion new generation

29Дек/110

Структуры данных: MagicObject

source:

Название MagicObject, имхо, явно лучше E4XMap и подобных названий.

Intro.

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

Сегодня хочу поделиться простенькой реализацией e4x в Объекте, где основной фишкой является расширение нативного e4x, который не позволяет callDescendants.

Например вот так:

object..method(args)

Зачем это нужно?
Допустим, дано:
- StateMachine и сложное дерево состояний;
- Анимация игрового объекта завязана на состояния, точнее, на смены состояний;
- Есть VO (ValueObjects) и есть рендеры, которые визуализируют изменения в VO;
- Рендеры имеют сложную иерархию, уровни вложенности.

Как происходит изменение состояния и отображение изменений при использовании MagicObject:
- Изменяем состояние в StateMachine;
- При удачном изменении состояния обновляем данные в VO;
- Вызываем обновление во всех рендерах внутри игрового уровня:

// magic = MagicObject instance
// magic[keyA] = Renderer instance
// magic[keyB] = Renderer instance
// magic[keyB][keyC] = Renderer instance - sub-renderer
// magic.subMagic.[keyD] = Renderer instance - sub-renderer
(magic..update)();
// или так:
(magic..update)(magic.changes);

Метод update() вызовется последовательно у всех объектов, имеющих этот метод.

Поясню как работает:
magic..update достаёт со всех уровней вложенности значения всех полей с именем update. Если там есть экземпляры класса Function, то всё это хозяйство оборачивается noname-фукцией, а ей задаются все свойства массива (для итеративного пробега) и пары, типа ключ-значение, где значение - каждое найденое поле с именем update.
На примере:

const result:Object = magic..update;
// можно вызвать:
result(); // or result(args);
// можно получить определённый элемент:
for(var key:String in magic)
	trace('in', magic, ':', key, '=', magic[key]),
	// или вызвать:
	magic[key] is Function && magic[key]();

History tracking:

const magic:MagicObject = new MagicObject();
magic.x = 0;
magic.x = 1;
 
// вернёт все изменения на первом уровней вложенности:
trace(magic.changes); // :Vector.<MagicNode>
 
// вернёт все изменения на всех уровнях вложенности:
trace(magic..changes); // [:Vector.<MagicNode>,..] или Vector.<MagicNode> если элемент один.
 
// чистим историю изменений:
magic.clearChanges();
// or magic..clearChanges();

Уведомления об изменениях:
Создали экземпляр:

 const magic:MagicObject = new MagicObject();

Дали ссылку на колбэк:

magic.notificationCallback = function():void
	{
		trace('new changes:', magic.changes);
	}

или так:

magic.notificationCallback = function(changes:Vector.<MagicNode>):void
	{
		trace('new changes:', changes);
	}

сдели какие-то изменения:

 magic.x = 0;
magic.x = 1;

.. и мы сразу получаем эти изменения в колбэк.

Собственно сам класс лежит здесь.

Вот пример расширения на деле:

package ru.kozlovskij.game.objects.data
{
	import ru.kozlovskij.utils.data.MagicNode;
	import ru.kozlovskij.utils.data.MagicObject;
 
	/**
	 * @author Aleksandr Kozlovskij (created: Dec 27, 2011)
	 */
	public dynamic class GameObjectData extends MagicObject
	{
		protected static const PRIVATE_ACCESSOR_PREFIX:String = '_';
 
		private var _notificationCallback:Function;
 
		//------------ constructor ------------//
 
		public function GameObjectData()
		{
			super();
			super.notificationCallback = __notificationCallback;
		}
 
		//------------ initialize ------------//
 
		//--------------- ctrl ---------------//
 
		//------------ get / set -------------//
 
		public function get selected():Boolean
		{
			return this._selected;
		}
 
		public function set selected(value:Boolean):void
		{
			this._selected = value;
		}
 
 
		override public function get notificationCallback():Function
		{
			return _notificationCallback;
		}
 
		override public function set notificationCallback(value:Function):void
		{
			_notificationCallback = value;
		}
 
		//------- handlers / callbacks -------//
 
		protected function __notificationCallback(changes:Vector.<MagicNode>):void
		{
			const ns:Namespace = nodeResetNS;
			for each(var node:MagicNode in changes)
			{
				const property:String = node.public::property;
				if(property.charAt() == PRIVATE_ACCESSOR_PREFIX)
					node.ns::property = property.substr(1);
			}
 
			// finally:
			_notificationCallback && (_notificationCallback.length ? _notificationCallback(changes) : _notificationCallback());
		}
	}
}

Получилось несколько сумбурно и возможно что-то забыл упомянуть.
Комментарии, идеи и замечания очень приветствуются. Надеюсь, кому-то это пригодится.

Комментарии (0) Пинги (0)

Пока нет комментариев.


Оставить комментарий

Вы должны войти в систему чтобы публиковать комментарии.

Нет обратных ссылок на эту запись.