
Framework: Entity & EntityManager
Немного отвлечёмся от игровых экранов и перейдём ко второй основе моейго фреймворка — к сущьностям (Entity) и их менеджеру (EntityManager).
Сущность — это основа всех игровых объектов. Будь-то юнит, платформа или триггер — все они так или иначе унаследованы от класса Entity. Класс этот наследуется от EventDispatcher и реализует несколько базовых методов.
Про класс Simulation я расскажу позже, как и про его параметр eventer, пока просто считайте, что sim.eventer.add — это тоже самое, что dispatchEvent — генерация события.
У каждой сущности есть уникальный id, который, правда, мне пока ещё не разу не понадобился.
Параметр alive показывает, жива сущность или уже сдохла и трогать её не надо.
Метод setAttr принимает Object, в который мы можем поместить любые входные параметры. Про это я уже говорил. Разные сущноси могут прнимать разные параметры, поэтому в данным случае универсальность — это хорошё. Так же мы оставили конструктор без параметров, что позволит реализовать кеширование любых Объектов, отнаследованных от Entity — вместо удаления и создания новых объектов можно будет просто вызвать метод setAttr уже созданых и помещённых в специальном кеше.
Про кеширование подробно рассказывали на форуме http://flashgamedev.ru, но я позже напишу про это подробней применительно к моему фреймворку.
Теперь нам нужен некий класс для того, чтобы управлять всем этим добром. Для этого используется класс EntityManager, который реализует добавление, удаление и обработку сущностей.
Тут стоит обратить внимание на следующие вещи.
1. При добавлении и удалении сущности генерятся события об этом, которые в последствии можно будет поймать и совершить с сущностью необходимые действия. Например при удалении сущности игрока нужно показать экран проигрыша.
2. Менеджер подписывается на событие EntityEvent.DEAD, которое генерится при вызове метода die у сущности.
3. Непосредственное удаление сущностей при их смерти (onDead) происходит только после их полной обработки, а до того они помещаются в специальный массив dead_list. Делается это для того, чтобы не было ошибок обращения к параметрам и методам удалённой сущности. Например нельзя удалять тела Box2D, пока программа находится внутри метода Step. И если сущность содержит в себе физическое тело, а мы его попытаемся удалить сразу, то произойдёт ошибка.
Entity
elmortem.game.entities.EntityСущность — это основа всех игровых объектов. Будь-то юнит, платформа или триггер — все они так или иначе унаследованы от класса Entity. Класс этот наследуется от EventDispatcher и реализует несколько базовых методов.
package elmortem.game.entities {
import elmortem.game.Simulation;
import flash.events.EventDispatcher;
public class Entity extends EventDispatcher {
static private var _ids:uint = 0;
private var pId:uint;
private var pName:String;
private var pAlive:Boolean;
private var pSim:Simulation = null;
public var attr:Object;
public function Entity() {
}
public function setAttr(attr:Object):Entity {
this.attr = attr;
pId = _ids++;
pName = (attr.name != null)?attr.name:"entity" + pId;
pAlive = true;
return this;
}
public function init(sim:Simulation):void {
pSim = sim;
}
public function free():void {
attr = null;
pName = null;
}
public function die():void {
if(!pAlive) return;
pAlive = false;
sim.eventer.add(this, new EntityEvent(this, EntityEvent.DEAD));
}
public function update(delta:Number):void {
}
public function render():void {
}
public function get id():uint { return pId; }
public function get name():String { return pName; }
public function get alive():Boolean { return pAlive; }
public function get sim():Simulation { return pSim; }
}
}
Про класс Simulation я расскажу позже, как и про его параметр eventer, пока просто считайте, что sim.eventer.add — это тоже самое, что dispatchEvent — генерация события.
У каждой сущности есть уникальный id, который, правда, мне пока ещё не разу не понадобился.
Параметр alive показывает, жива сущность или уже сдохла и трогать её не надо.
Метод setAttr принимает Object, в который мы можем поместить любые входные параметры. Про это я уже говорил. Разные сущноси могут прнимать разные параметры, поэтому в данным случае универсальность — это хорошё. Так же мы оставили конструктор без параметров, что позволит реализовать кеширование любых Объектов, отнаследованных от Entity — вместо удаления и создания новых объектов можно будет просто вызвать метод setAttr уже созданых и помещённых в специальном кеше.
Про кеширование подробно рассказывали на форуме http://flashgamedev.ru, но я позже напишу про это подробней применительно к моему фреймворку.
EntityManager
elmortem.game.entities.EntityManagerТеперь нам нужен некий класс для того, чтобы управлять всем этим добром. Для этого используется класс EntityManager, который реализует добавление, удаление и обработку сущностей.
package elmortem.game.entities {
import elmortem.game.Simulation;
import flash.events.EventDispatcher;
public class EntityManager extends EventDispatcher {
private var sim:Simulation; // об этом позже!
private var list:/*Entity*/Array;
private var dead_list:/*Entity*/Array;
public function EntityManager(sim:Simulation) {
super();
this.sim = sim;
list = [];
dead_list = [];
}
public function free():void {
clear();
list = null;
dead_list = null;
}
public function clear():void {
for (var i:int = 0; i < list.length; i++) {
list[i].free();
}
list = [];
dead_list = [];
}
public function add(entity:Entity):Entity {
if (entity == null || list == null) {
trace("Entity or List is empty.");
return null;
}
list.push(entity);
entity.init(sim);
entity.addEventListener(EntityEvent.DEAD, onDead);
sim.eventer.add(new EntityEvent(entity, EntityEvent.ADD));
return entity;
}
public function remove(entity:Entity):void {
if (entity == null || list == null) {
trace("Entity or List is empty.");
return;
}
var index:int = list.indexOf(entity);
if(index >= 0) {
list.splice(index, 1);
sim.eventer.add(new EntityEvent(entity, EntityEvent.REMOVE));
entity.free();
}
}
public function findEntitiesByClass(cls:Class):/*Entity*/Array {
var arr:/*Entity*/Array = [];
if (list == null) return arr;
for(var i:int = 0; i < list.length; i++) {
if(list[i] is cls) {
arr.push(list[i]);
}
}
return arr;
}
public function findEntityByName(name:String, cls:Class = null):Entity {
if (list == null) return null;
if(cls == null) cls = Entity;
for(var i:int = 0; i < list.length; i++) {
if(list[i] is cls && list[i].name == name) {
return list[i];
}
}
return null;
}
public function update(delta:Number):void {
var i:int;
for(i = 0; i < list.length; i++) {
if(list[i].alive) {
list[i].update(delta);
}
}
// dead list
if (dead_list.length > 0) {
for(i = 0; i < dead_list.length; i++) {
remove(dead_list[i]);
}
dead_list = [];
}
}
public function render():void {
for(var i:int = 0; i < list.length; i++) {
list[i].render();
}
}
public function onDead(e:EntityEvent):void {
if (dead_list == null) return;
dead_list.push(e.entity);
}
}
}
Тут стоит обратить внимание на следующие вещи.
1. При добавлении и удалении сущности генерятся события об этом, которые в последствии можно будет поймать и совершить с сущностью необходимые действия. Например при удалении сущности игрока нужно показать экран проигрыша.
2. Менеджер подписывается на событие EntityEvent.DEAD, которое генерится при вызове метода die у сущности.
3. Непосредственное удаление сущностей при их смерти (onDead) происходит только после их полной обработки, а до того они помещаются в специальный массив dead_list. Делается это для того, чтобы не было ошибок обращения к параметрам и методам удалённой сущности. Например нельзя удалять тела Box2D, пока программа находится внутри метода Step. И если сущность содержит в себе физическое тело, а мы его попытаемся удалить сразу, то произойдёт ошибка.
- +9
- elmortem
Комментарии (17)
А так свой фреймворк пришлось создавать и оттачивать со временем…
А новичкам думаю очень полезно будет, особенно если расписать поподробнее, хотя бы комменты к функциям добавить.
Расскажи про свой, мне интересно, обожаю чужой код.
Руки дойдут — поделюсь своими наработками.
Маленькое замечание:
public function remove(entity:Entity):void {
…
// лучше не id, а itemIndex, targetIndex или index… а то я не сразу понял, что речь не про id в Entity
var id:int = list.indexOf(entity);
…
}
Может есть смысл в fashwiki? Возможна коллективная доработка.
Удобное и частое сокращение для индекса — idx