Работа с компонентами

Компонент - это основа нашего движка. Компонент представляет из себя базовую частичку функциональности объекта, каждый игровой объект это агрегация компонент и связи между ними. Например компонент Drawer - может инкапсулировать в себя функциональность рисования игрового объекта. Компонент Position - хранить позицию. У каждого компонента есть так называемые атрибуты - инициализационные данные и параметры - это другие компоненты которые ему нужны для работы. Описание класса с одним компонентом будет выглядеть вот так:

class SimpleComponentExample
{
	component Position posComponet
	{
		position = Vector3(0,0,0);
	};
};

“Position” - тип компонента, “posComponet” его имя. Имя необходимо что бы передавать компонент в качестве параметра другому компоненту. Дальше идет секция описания атрибутов компонента или его инициализационных данных. Атрибуту с именем “position” присваевается значения Vector3(0,0,0). Атрибуты у компонентов строго типизированные, а список и их типы надо получать из описания компонентов, которое будет делать при их создании, наверно где-нибудь здесь.

Передача компонента ввиде параметра выглядит вот так:

class SimpleComponentParamExample
{
	component Position posComponet
	{
		position = Vector3(0,0,0);
	};
	
	component Drawer draw(Position = posComponent);
};

Здесь используется сокращенный вариант объявления компонента, без описания его атрибутов, когда у компонентов нет атрибутов - можно использовать такую запись. В описании компонента как бы две секции описания: его параметров и его атрибутов, в зависимости от типа компонентов любой их них может не быть, вплоть до вот такого:

class SimpleComponentParamExample
{
	component Simple s;
};

Но если компонент требует описания параметров и атрибутов - то при попытки обработки такого скрипта будет ошибка. Итак для передаче параметра используются круглые скобки, в них через запятую пишется имя параметра и имя компоенета, думаю в большинстве случаев компоненты в качестве параметров будут принимать не больше одного компонента какого-то типа, поэтому чаще всего имя параметра будет совпадать с типом параметра, но не факт, ещё реальных тестов не было, а наша теория может оказаться просто теорией. Думаю походу разберемся и если что читайте новости :)

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

class Gun
{
	constructor(object projectile, string modelName);

	component Spawner sp
	{
		prototype = projectile;
	};
	component GunMechanics mech(Spawner = sp);
	component Drawer draw
	{
		model = modelName;
	};
};

Класс состоит из трех компонент: “sp” -компонент типа “Spawner” - это компонент котый отвественнен за порождение объектов из прототипа, который задается атрибутом “prototype”, “mech” - компонент механики орудия(“GunMechanics”), с ним будет взаимодействовать объект который будет управлять орудием, грубо говоря этот компонент инкапсулирует в себе всю механику стрельбы и использует “sp” для порождения снарядов и наконец draw - это компонент отвественный за отрисовку нашего орудия, ему передается в качестве атрибута имя модели.

Теперь представим что нам надо создать пулемет. У нас есть класс который описывает пулю, пусть он называется “Bullet”. Описание пулемета будет таким:

class MiniGun: Gun
{

	constructor():Gun(bul, "minigunModel")
	{
		MinigunBullet bul();	
	};
};

Что мы сделали - мы создали класс MiniGun который полностью унаследовал все компоненты класса Gun, внутри него мы создали инстанс пули “MiniBullet bul();” и передали его качестве параметра в конструктор родительского класса, туда же мы передали название модели, после разверти скрипта в базовом классе Gun атрибуты projectile и modelName, примут значения: первый станет инстансом bul, второй будет “minigunModel”; Можно было бы и не наследоваться, а использовать компоновку двух инстансов, но в этом случае теряется возможность получить доступ к копонентам Gun, а нам ведь оттуда нужно получить механику орудия. Дальнейшее использование MiniGun может быть в описании робота:

class Robot
{
	constructor()
	{
		MiniGun miniGun;
		
	};
	component Position pos;
	component RobotMechanics mech(Position = pos, GunMechanics = miniGun.mech);
	component AI ai(mech);

	component Drawer draw
	{
		model = "robotModel"
	};
};

Вся соль данного примера заключается в

component RobotMechanics mech(Position = pos, GunMechanics = miniGun.mech);

Здесь создается компонент типа RobotMechanics который инкапсулирует в себе функциональность механики робота, в нем могут быть спрятаны функции для перемещения ведения огня и например радар, этот компонент передается AI - мозгам, которые и будут использовать их. Но робот не сам стреляет - он стреляет через орудие - которое мы создали и для осущетсвления выстрелов ему, в качестве параметра, необходима механика орудия “GunMechanics = miniGun.mech” этой строчкой мы её передаем. Сначала идет обращение к объекту по имени, потом через “.” идет обращение по имени к компоненту который мы хотим передать. Так же дочерние инстансы могут брать компоненты класса их порождающего, но этот пример я напишу позже :)

Prior art:

Scott Bilas, A Data-Driven Game Object System

Component based software engineering