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

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

Рейтинг: 5.00. Голосов: 7.

Что такое entity framework

Запись от artcraft размещена 12.09.2012 в 18:20
Обновил(-а) artcraft 17.09.2012 в 13:02

Это не дословный перевод статьи Ричарда Лорда которая мне понравилась.

--------------------------------------------

Entity system-ы растут в популярности, например широко извесный юнити и менее известные as3 библиотеки Ember2, Xember и моя собственная Ash. Причиной тому то что они упрощают игровую архитектуру и позволяют четко разделять обязанности в коде.

В этой статье я расскажу о эволюции олдскульной game loop арихитекуры в entity-образную. Запаситесь терпением.

Примеры
В качестве примера я буду использовать игру Asteroids, она идеально подходить так как содержит простые версии понятий встречающихся в более серьёзных играх - рендеринг, физика, AI, персонаж, NPC (non player character)

Game loop
Для понимания того что из себя представляет entity-система, следует представлять что такое обыкновеннвый game loop (игровой цикл).
Game loop для игры Asteroids может выглядеть примерно так:
Код AS3:
function update( time:Number ):void{
  game.update( time );
  spaceship.updateInputs( time );
  for each( var flyingSaucer:FlyingSaucer in flyingSaucers ){
    flyingSaucer.updateAI( time );
  }
  spaceship.update( time );
  for each( var flyingSaucer:FlyingSaucer in flyingSaucers ){
    flyingSaucer.update( time );
  }
  for each( var asteroid:Asteroid in asteroids ){
    asteroid.update( time );
  }
  for each( var bullet:Bullet in bullets ){
    bullet.update( time );
  }
  collisionManager.update( time );
  spaceship.render();
  for each( var flyingSaucer:FlyingSaucer in flyingSaucers ){
    flyingSaucer.render();
  }
  for each( var asteroid:Asteroid in asteroids ){
    asteroid.render();
  }
  for each( var bullet:Bullet in bullets ){
    bullet.render();
  }
}
такой game loop вызывается раз 30-60 раз в секунду, для обновления игры. Важен порядок операций в этом цикле, каждый кадр происходит обновление игровых объектов, проверка столкновений и потом отрисовка.

Этот цикл простой так как 1) игра простая 2) в игре только одно состояние, но если игра будет сложнее, то этот цикл станет обрастать условными операторами и рано или поздно превратится в кашу (3000 строчек не предел)

Entity-система пытается решить эту проблему. Пускай даже в ущерб некоторым другим важным вещам, вроде отделения представления от контроллера.

Эволюция
Первый шаг на пути эволюции это добавление понятия процесс. Процессы это объекты которые могут быть запущены, могут протекать и могут быть остановлены. Интерфейс процесса выглядит примерно так:
Код AS3:
interface IProcess{
  function start():Boolean;
  function update( time:Number ):void;
  function end():void;
}
Игровой цикл может быть разбит на некоторое количество процессов (вроде движение объектов, проверка столкновений, ренедринг). Для управления этими процессами мы создаём менеджер процессов:
Код AS3:
class ProcessManager{
  private var processes:PrioritisedList;
 
  public function addProcess( process:IProcess, priority:int ):Boolean{
    if( process.start() ){
      processes.add( process, priority );
      return true;
    }
    return false;
  }
 
  public function update( time:Number ):void{
    for each( var process:IProcess in processes ){
      process.update( time );
    }
  }
 
  public function removeProcess( process:IProcess ):void{
    process.end();
    processes.remove( process );
  }
}
Это несколько упрощённая версия процесс менеджера, но идея понятна: игровой цикл разбит на процессы которыми управляет процесс менеджер.

Процесс визуализации (rendering)
Давайте, в качестве примера, посмотрим на отрисовку.
Если просто запихнуть часть кода из бывшего game loop в render-процесс то будет что-то вроде:
Код AS3:
class RenderProcess implements IProcess{
  public function start() : Boolean{
    // initialise render system
    return true;
  }
 
  public function update( time:Number ):void{
    spaceship.render();
    for each( var flyingSaucer:FlyingSaucer in flyingSaucers ){
      flyingSaucer.render();
    }
    for each( var asteroid:Asteroid in asteroids ){
      asteroid.render();
    }
    for each( var bullet:Bullet in bullets ){
      bullet.render();
    }
  }
 
  public function end():void{
    // clean-up render system
  }
}
Но это не самый оптимальный путь. Можно значительно всё упростить добавив единый итерфейс для всех видимых объектов:
Код AS3:
interface IRenderable{
  function render();
}
Код AS3:
class RenderProcess implements IProcess{
  private var targets:Vector.<IRenderable>;
 
  public function start() : Boolean{
    // initialise render system
    return true;
  }
 
  public function update( time:Number ):void{
    for each( var target:IRenderable in targets ){
      target.render();
    }
  }
 
  public function end() : void{
    // clean-up render system
  }
}
В этой ситуации Spaceship класс может выглядеть так
Код AS3:
class Spaceship implements IRenderable{
  public var view:DisplayObject;
  public var position:Point;
  public var rotation:Number;
 
  public function render():void{
    view.x = position.x;
    view.y = position.y;
    view.rotation = rotation;
  }
}
Эти примеры используют дисплей лист, код будет другим если, например, рендеринг будет происходить через stage3d, но принцип не меняется. Нужно знать что нарисовать в какой области экрана и под каким углом, остальное забота функции render.

Использование наследования
В этом коде есть недостаток, единственное что отличает Spaceship от других игровых объектов это графика (значение свойства view) а также позиция и поворот, всё остальное может быть общим для всех игровых объектов. Поэтому давайте вынесем общую функциональность в базовый класс используя наследование:
Код AS3:
class Renderable implements IRenderable{
  public var view:DisplayObject;
  public var position:Point;
  public var rotation:Number;
 
  public function render():void{
    view.x = position.x;
    view.y = position.y;
    view.rotation = rotation;
  }
}
Код AS3:
class Spaceship extends Renderable{
}
Все видимые объекты будут наследоваться от Renderable, вот так:

Название: entity1.png
Просмотров: 3045

Размер: 9.6 Кб

Процесс дивежения
Для дальнейшего понимания нужен еще один процесс поэтому давайте рассмотрим MoveProcess отвечающий за движение игровых объектов:
Код AS3:
interface IMoveable{
  function move( time:Number );
}
Код AS3:
class MoveProcess implements IProcess{
  private var targets:Vector.<IMoveable>;
 
  public function start():Boolean{
    return true;
  }
 
  public function update( time:Number ):void{
    for each( var target:IMoveable in targets ){
      target.move( time );
    }
  }
 
  public function end():void{
  }
}
Код AS3:
class Moveable implements IMoveable{
  public var position:Point;
  public var rotation:Number;
  public var velocity:Point;
  public var angularVelocity:Number;
 
  public function move( time:Number ):void{
    position.x += velocity.x * time;
    position.y += velocity.y * time;
    rotation += angularVelocity * time;
  }
}
Код AS3:
class Spaceship extends Moveable{
}
Множественное наследование
То что у нас получилось - прекрасно, но к сожалению есть одна сложность: наш корабль должен быть однорвеменно moveable и renderable, однако большинство современных языков программирования, включая AS3 не разрешают множественное наследование

Эту проблему можно решить цепочкой наследования, можно сделать moveable наследником renderable:
Код AS3:
class Moveable extends Renderable implements IMoveable{
  public var velocity:Point;
  public var angularVelocity:Number;
 
  public function move( time:Number ):void{
    position.x += velocity.x * time;
    position.y += velocity.y * time;
    rotation += angularVelocity * time;
  }
}
Код AS3:
class Spaceship extends Moveable{
}
теперь космическй корабль и Moveable и Renderable. Аналогичным образом мы можем создать и другие игровые объекты:

Название: entity2.png
Просмотров: 2985

Размер: 12.9 Кб

также мы можем создавать неподвижные видимые объекты:

Название: entity2a.png
Просмотров: 2982

Размер: 15.3 Кб

Moveable но не Renderable
Но что если нам понадобится движущийся но невидимый объект? Это ломает красивое дерево наследования и заставляет нас добавить альтернативную имплементацию Moveable которая не будет наследоваться от Renderable:
Код AS3:
class InvisibleMoveable implements IMoveable{
  public var position:Point;
  public var rotation:Number;
  public var velocity:Point;
  public var angularVelocity:Number;
 
  public function move( time:Number ):void{
    position.x += velocity.x * time;
    position.y += velocity.y * time;
    rotation += angularVelocity * time;
  }
}
Название: entity3.png
Просмотров: 3007

Размер: 19.2 Кб

В простой игре такое решение не элегантно но терпимо, но в игре посложнее это может обернутся большими сложностями, добавление каждого нового объекта который противоречит общему принципу наследования, вроде силового поля в этой ситуации, превратится в проблему.

Композиция предпочтительнее наследования
OOP принцип предпочтения композиции наследованию - широко известен. Использование этого принципа спасёт нас от возможной путаницы с наследованием.

Нам по прежнему понадобятся раздельные Renderable и Moveable классы, но вместо наследования от них мы создадим космический корабль который будет содержать инстансы каждого из них:
Код AS3:
class Renderable implements IRenderable{
  public var view:DisplayObject;
  public var position:Point;
  public var rotation:Number;
 
  public function render():void{
    view.x = position.x;
    view.y = position.y;
    view.rotation = rotation;
  }
}
Код AS3:
class Moveable implements IMoveable{
  public var position:Point;
  public var rotation:Number;
  public var velocity:Point;
  public var angularVelocity:Number;
 
  public function move( time:Number ):void{
    position.x += velocity.x * time;
    position.y += velocity.y * time;
    rotation += angularVelocity * time;
  }
}
Код AS3:
class Spaceship{
  public var renderData:IRenderable;
  public var moveData:IMoveable;
}
При таком подходе мы можем комбинировать разное виды поведения объектов в любых комбинациях, и не оказаться в затруднительной ситуацией возникающей при наследовании.

Нажмите на изображение для увеличения
Название: entity4.png
Просмотров: 381
Размер:	26.3 Кб
ID:	289

Объекты объединяющие разные виды поведений, такие как: корабль, астероид, пуля, летающая тарелка, силовое поле и называются сущностями (Entity)

Код процессов остаётся неизменным:
Код AS3:
interface IRenderable{
  function render();
}
Код AS3:
class RenderProcess implements IProcess{
  private var targets:Vector.<IRenderable>;
 
  public function update(time:Number):void{
    for each(var target:IRenderable in targets){
      target.render();
    }
  }
}
Код AS3:
interface IMoveable{
  function move();
}
Код AS3:
class MoveProcess implements IProcess{
  private var targets:Vector.<IMoveable>;
 
  public function update(time:Number):void{
    for each(var target:IMoveable in targets){
      target.move( time );
    }
  }
}
Но мы не передаём каждому процессу сам космический корабль, мы передаём процессу одну из составляющих корабля. Создание корабля теперь выглядит так:
Код AS3:
public function createSpaceship():Spaceship{
  var spaceship:Spaceship = new Spaceship();
  ...
  renderProcess.addItem( spaceship.renderData );
  moveProcess.addItem( spaceship.moveData );
  ...
  return spaceship;
}
Такой подход гораздо лучше, он даёт свободу в смешивании любых видов поведения и использовании любых процессов любым игровым объектом. Но опять есть одна проблема.

Общие данные
Значения позиции и поворота в экземпляре Renderable должны совпадать с значениями в экземпляре класса Moveable, ведь RenderProcess должен отрисвать объект в том месте куда его подвинул MoveProcess.
Код AS3:
class Renderable implements IRenderable{
  public var view:DisplayObject;
  public var position:Point;
  public var rotation:Number;
 
  public function render():void{
    view.x = position.x;
    view.y = position.y;
    view.rotation = rotation;
  }
}
Код AS3:
class Moveable implements IMoveable{
  public var position:Point;
  public var rotation:Number;
  public var velocity:Point;
  public var angularVelocity:Number;
 
  public function move( time:Number ):void{
    position.x += velocity.x * time;
    position.y += velocity.y * time;
    rotation += angularVelocity * time;
  }
}
Код AS3:
class Spaceship{
  public var renderData:IRenderable;
  public var moveData:IMoveable;
}
Для выхода из этого затруднения, нам нужно вынести данные за пределы классов Renderable и Moveable, и заставить их использовать одни и те же данные совместно.
Для этого мы вводим еще одно понятие Сomponents (составляющие) - это просто value object-ы хранящие некоторые данные для совместного использования разными процессами
Код AS3:
class PositionComponent{
  public var x:Number;
  public var y:Number;
  public var rotation:Number;
}
Код AS3:
class VelocityComponent{
  public var velocityX:Number;
  public var velocityY:Number;
  public var angularVelocity:Number;
}
Код AS3:
class DisplayComponent{
  public var view:DisplayObject;
}
Код AS3:
class Renderable implements IRenderable{
  public var display:DisplayComponent;
  public var position:PositionComponent;
 
  public function render():void{
    display.view.x = position.x;
    display.view.y = position.y;
    display.view.rotation = position.rotation;
  }
}
Код AS3:
class Renderable implements IRenderable{
  public var display:DisplayComponent;
  public var position:PositionComponent;
 
  public function render():void{
    display.view.x = position.x;
    display.view.y = position.y;
    display.view.rotation = position.rotation;
  }
}
Код AS3:
class Moveable implements IMoveable{
  public var position:PositionComponent;
  public var velocity:VelocityComponent;
 
  public function move( time:Number ):void{
    position.x += velocity.velocityX * time;
    position.y += velocity.velocityY * time;
    position.rotation += velocity.angularVelocity * time;
  }
}
Теперь когда мы создаём космический корабль мы уверены что разные поведения (Moveable и Renderable) используют общие данные хранящиеся в PositionComponent
Код AS3:
class Spaceship{
  public function Spaceship(){
    moveData = new Moveable();
    renderData = new Renderable();
    moveData.position = new PositionComponent();
    moveData.velocity = new VelocityComponent();
    renderData.position = moveData.position;
    renderData.display = new DisplayComponent();
  }
}
На устройство процессов это изменение никак не влияет.

Отличный момент для передышки
К этому моменту у нас есть изящное разделение задач. Игровой цикл разделён на процессы. Каждый процесс управляет набором объектов-действий. Каждое из этих действий выполняет одно конкретное действие с конкретным игровым объектом. Через систему компоенетов(составляющих) эти действия совместно используют данные игрового объекта. Таким образом несколько процессов одновременно могут производить сложные действия с объектами игрового мира и при этом каждый процесс остаётся достаточно простым.

Такая архитектура лежит в основе большого количества entity-фрэймворков для разработки игр. Такая архитектура следует хорошим практикам OOP и прекрасно работает.

Но это еще не всё, дальше немного безумия.

Отказ от хороших практик OOP
В данный момент наша архитектура использует такие важные принципы ооп как инкапсуляция, принцип single responsibility (имплементации IRenderable и IMoveable инкапсулируют данные и логику необходимые для выполнения единственной задачи) и сomposition over inheritance (Игровой объект ракета это комбинация имплементаций IRenderable и IMoveable)

Следующий шаг на пути эволюции entity-системы нарушает один из базовых принципов OOP. Мы нарушим инкапсуляцию данных и логики Renderable и Moveable, в частности мы удалим логику из этих классов и целиком перенесём её в процесс.
Таким образом это:
Код AS3:
interface IRenderable{
  function render();
}
Код AS3:
class Renderable implements IRenderable{
  public var display:DisplayComponent;
  public var position:PositionComponent;
 
  public function render():void{
    display.view.x = position.x;
    display.view.y = position.y;
    display.view.rotation = position.rotation;
  }
}
Код AS3:
class RenderProcess implements IProcess{
  private var targets:Vector.<IRenderable>;
 
  public function update( time:Number ):void{
    for each( var target:IRenderable in targets ){
      target.render();
    }
  }
}
теперь будет устроено так:
Код AS3:
class RenderData{
  public var display:DisplayComponent;
  public var position:PositionComponent;
}
Код AS3:
class RenderProcess implements IProcess{
  private var targets:Vector.<RenderData>;
 
  public function update( time:Number ):void{
    for each( var target:RenderData in targets ){
      target.display.view.x = target.position.x;
      target.display.view.y = target.position.y;
      target.display.view.rotation = target.position.rotation;
    }
  }
}
А это:
Код AS3:
interface IMoveable{
  function move( time:Number );
}
Код AS3:
class Moveable implements IMoveable{
  public var position:PositionComponent;
  public var velocity:VelocityComponent;
 
  public function move( time:Number ):void{
    position.x += velocity.velocityX * time;
    position.y += velocity.velocityY * time;
    position.rotation += velocity.angularVelocity * time;
  }
}
Код AS3:
class MoveProcess implements IProcess{
  private var targets:Vector.<IMoveable>;
 
  public function move( time:Number ):void{
    for each( var target:Moveable in targets ){
      target.move( time );
    }
  }
}
меняем на:
Код AS3:
class MoveData{
  public var position:PositionComponent;
  public var velocity:VelocityComponent;
}
Код AS3:
class MoveProcess implements IProcess{
  private var targets:Vector.<MoveData>;
 
  public function move( time:Number ):void{
    for each( var target:MoveData in targets ){
      target.position.x += target.velocity.velocityX * time;
      target.position.y += target.velocity.velocityY * time;
      target.position.rotation += target.velocity.angularVelocity * time;
    }
  }
}
Возможно не слишком очевидно зачем надо было это делать. Мы избавились от объектов-действий (Renderable и Moveable), и теперь процесс вынужеден выполнять их работу сам.

Первым последствием этого изменения является то что все игровые объекты теперь должны использовать одинаковый способ визуализации, т.к. отрисовкой занимается процесс а он один на всех. Но это нас никак не ограничивает ведь если нам например понадобится часть объектов отрисовать в stage3d а часть используя дисплей лист, то мы всегда можем добавить ещё один рендерящий процесс, и оба процесса смогут работать параллельно, так что гибкость мы не потеряли.

Чего мы добились так это возможности значительо изменить архитектуру иговых объектов и упростить их конфигурирование.
До сих пор ракета была устроена так:
Код AS3:
class Spaceship{
  public var moveData:MoveData;
  public var renderData:RenderData;
}
она содержала 2 объекта с данными
Код AS3:
class MoveData{
  public var position:PositionComponent;
  public var velocity:VelocityComponent;
}
Код AS3:
class RenderData{
  public var display:DisplayComponent;
  public var position:PositionComponent;
}
а внутри еще данные
Код AS3:
class PositionComponent{
  public var x:Number;
  public var y:Number;
  public var rotation:Number;
}
Код AS3:
class VelocityComponent{
  public var velocityX:Number;
  public var velocityY:Number;
  public var angularVelocity:Number;
}
Код AS3:
class DisplayComponent{
  public var view:DisplayObject;
}
и всё это использовалось двумя процессами:
Код AS3:
class MoveProcess implements IProcess{
  private var targets:Vector.<MoveData>;
 
  public function move( time:Number ):void{
    for each( var target:MoveData in targets ){
      target.position.x += target.velocity.velocityX * time;
      target.position.y += target.velocity.velocityY * time;
      target.position.rotation += target.velocity.angularVelocity * time;
    }
  }
}
Код AS3:
class RenderProcess implements IProcess{
  private var targets:Vector.<RenderData>;
 
  public function update( time:Number ):void{
    for each( var target:RenderData in targets ){
      target.display.view.x = target.position.x;
      target.display.view.y = target.position.y;
      target.display.view.rotation = target.position.rotation;
    }
  }
}
Но ракета больше не использует данные которые содержит сама, всем этим занимаются процессы, а ракета только общедоступное хранилище значений, поэтому мы можем значительно всё упростить:
Код AS3:
class Spaceship{
  public var position:PositionComponent;
  public var velocity:VelocityComponent;
  public var display:DisplayComponent;
}
Код AS3:
class PositionComponent{
  public var x:Number;
  public var y:Number;
  public var rotation:Number;
}
Код AS3:
class VelocityComponent{
  public var velocityX:Number;
  public var velocityY:Number;
  public var angularVelocity:Number;
}
Код AS3:
class DisplayComponent{
  public var view:DisplayObject;
}
Теперь игровому объекту не обязательно знать какие процессы могут его использовать. Игровой объект представляет из себя набор компонентов совокупность которых определяет текущее состояние.

Systems & Nodes
Следующим шагом мы перекладываем задачу получения данных необходимых процессу для работы на entity-фрэймворк. Для исключения путаницы вводим новое понятие Node. Node - это выборка данных игрового объекта которую фрэймвок предоставляет процессу.
Код AS3:
class MoveNode{
  public var position:PositionComponent;
  public var velocity:VelocityComponent;
}
Код AS3:
class RenderNode{
  public var display:DisplayComponent;
  public var position:PositionComponent;
}
Процесс остаётся без изменений, но ради следования общепринятой терминологии мы переименовываем его в System:
Код AS3:
class MoveSystem implements ISystem{
  private var targets:Vector.<MoveNode>;
 
  public function update( time:Number ):void{
    for each( var target:MoveNode in targets ){
      target.position.x += target.velocity.velocityX * time;
      target.position.y += target.velocity.velocityY * time;
      target.position.rotation += target.velocity.angularVelocity * time;
    }
  }
}
Код AS3:
class RenderSystem implements ISystem{
  private var targets:Vector.<RenderNode>;
 
  public function update( time:Number ):void{
    for each( var target:RenderNode in targets ){
      target.display.view.x = target.position.x;
      target.display.view.y = target.position.y;
      target.display.view.rotation = target.position.rotation;
    }
  }
}
Код AS3:
interface ISystem{
  function update( time:Number ):void;
}
И наконец собственно Entity
Так как ракета ничего не делает кроме как хранит состояние, и её устройство абсолютно не отличается от всех остальных игровых объектов. Том мы переименуем её в Entity и унифицируем процесс добавления/удаления/получения данных.
Код AS3:
class Entity{
  private var components : Dictionary;
 
  public function add( component:Object ):void{
    var componentClass : Class = component.constructor;
    components[ componentClass ] = component;
  }
 
  public function remove( componentClass:Class ):void{
    delete components[ componentClass ];
  }
 
  public function get( componentClass:Class ):Object{
    return components[ componentClass ];
  }
}
теперь процесс создания ракеты будет таким:
Код AS3:
public function createSpaceship():void{
  var spaceship:Entity = new Entity();
  var position:PositionComponent = new PositionComponent();
  position.x = Stage.stageWidth / 2;
  position.y = Stage.stageHeight / 2;
  position.rotation = 0;
  spaceship.add( position );
  var display:DisplayComponent = new DisplayComponent();
  display.view = new SpaceshipImage();
  spaceship.add( display );
  game.add( spaceship );
}
Главный класс игры
Не забудем и о менеджере систем, который раньше назывался менеджером процессов
Код AS3:
class SystemManager{
  private var systems:PrioritisedList;
 
  public function addSystem( system:ISystem, priority:int ):void{
    systems.add( system, priority );
    system.start();
  }
 
  public function update( time:Number ):void{
    for each( var system:ISystem in systemes ){
      system.update( time );
    }
  }
 
  public function removeSystem( system:ISystem ):void{
    system.end();
    systems.remove( system );
  }
}
Блин, длинноватая статья :~) устала рука переводить, поэтому заключение будет на языке оригинала.
(возможно переведу позже)


This will be enhanced and will sit at the heart of our entity framework. We’ll add to it the functionality mentioned above to dynamically create nodes for the systems.
The entities only care about components, and the systems only care about nodes. So to complete the entity framework, we need code to watch the entities and, as they change, add and remove their components to the node collections used by the systems. Because this is the one bit of code that knows about both entities and systems, we might consider it central to the game. In Ash, I call this the Game class, and it is an enhanced version of the system manager.

Every entity and every system is added to and removed from the Game class when you start using it and stop using it. The Game class keeps track of the components on the entities and creates and destroys nodes as necessary, adding those nodes to the node collections. The Game class also provides a way for the systems to get the collections they require.

Код AS3:
public class Game{
  private var entities:EntityList;
  private var systems:SystemList;
  private var nodeLists:Dictionary;
 
  public function addEntity( entity:Entity ):void{
    entities.add( entity );
    // create nodes from this entity's components and add them to node lists
    // also watch for later addition and removal of components from the entity so
    // you can adjust its derived nodes accordingly
  }
 
  public function removeEntity( entity:Entity ):void{
    // destroy nodes containing this entity's components
    // and remove them from the node lists
    entities.remove( entity );
  }
 
  public function addSystem( system:System, priority:int ):void{
    systems.add( system, priority );
    system.start();
  }
 
  public function removeSystem( system:System ):void{
    system.end();
    systems.remove( system );
  }
 
  public function getNodeList( nodeClass:Class ):NodeList{
    var nodes:NodeList = new NodeList();
    nodeLists[ nodeClass ] = nodes;
    // create the nodes from the current set of entities
    // and populate the node list
    return nodes;
  }
 
  public function update( time:Number ):void{
    for each( var system:ISystem in systemes ){
      system.update( time );
    }
  }
}
Название: entity5.png
Просмотров: 2943

Размер: 14.0 Кб

To see one implementation of this architecture, checkout the source code for Ash, and see the example Asteroids implementation there too.

Conclusion

So, to summarise, entity systems originate from a desire to simplify the game loop. From that comes an architecture of entities, which represent the state of the game, and systems, which operate on the state of the game. Systems are updated every frame – this is the game loop. Entities are made up of components, and systems operate on the entities that have the components they are interested in. The game monitors the systems and the entities and ensures each system has access to a collection of all the entities that have the appropriate components.

However, systems don’t generally care about the entity as a whole, just the specific components they require. So, to optimise the architecture and provide additional clarity, the systems operate on statically typed node objects that contain the appropriate components, where those components all belong to the same entity.

An entity framework provides the basic scaffolding and core management for this architecture, without providing any actual entity or system classes. You create your game by creating the appropriate entities and systems.

An entity based game engine will provide many standard systems and entities on top of the basic framework.

Three entity frameworks for Actionscript are my own Ash, Ember2 by Tom Davies and Xember by Alec McEachran. Artemis is an entity framework for Java, that has also been ported to C#.
Всего комментариев 10

Комментарии

Старый 12.09.2012 19:11 Котяра вне форума
Котяра
 
Аватар для Котяра
картинки залей прям в блог как вложения.
Старый 12.09.2012 19:17 artcraft вне форума
artcraft
 
Аватар для artcraft
поправил
Обновил(-а) artcraft 12.09.2012 в 19:33
Старый 12.09.2012 21:47 Silicium вне форума
Silicium
 
Аватар для Silicium
Спасибо за перевод!
Старый 13.09.2012 00:50 GBee вне форума
GBee
 
Аватар для GBee
А я ничего не понял, с того момента, как начали разрушать ООП. И еще не понял, чем же так entity-framework хорош?

А еще пример ужасный, из него понятно, что все в космосе может летать, но непонятно, кто управляет взаимодействием и как? Вот летят две ентити друг на друга и?

Ну и очепятки:
Цитата:
теперь процесс создания ракеты будет таким:
public function createSpaceship():void{
Старый 13.09.2012 01:15 artcraft вне форума
artcraft
 
Аватар для artcraft
Цитата:
А я ничего не понял, с того момента, как начали разрушать ООП. И еще не понял, чем же так entity-framework хорош?
я кстати тоже перечитывал несколько раз

Цитата:
не понял, чем же так entity-framework хорош?
Эта статья не о том чем он хорош а о том что это такое,
ответ на ваш вопрос тут: Why use an entity framework for game development?

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

Цитата:
Ну и очепятки
опечатки явно есть, но вот в этих двух строчках не вижу опечатки
Старый 13.09.2012 01:37 GBee вне форума
GBee
 
Аватар для GBee
Цитата:
пример отличный и в самом конце есть ссылка на исходник работающей игры, в частности для проверки столкновений там есть CollisionSystem. В статье описано самое главное - принцип
До главного не дошли :о) Мне без CollisionSystem система вообще не интересна. Ведь непонятно, как CollisionSystem определяет из ентити ракета это или астероид или корабль. Кто дробит астероид(ентити) на более мелкие ентити после попадания? CollisionSystem? Не должна она этим заниматься по идее.

Цитата:
опечатки явно есть, но вот в этих двух строчках не вижу опечатки
Ракета и Spaceship. Стормозил, для меня ракета и космический корабль разные вещи. Я тут думал ракета, которую пускает космический корабль в астероид.
Старый 13.09.2012 01:58 GBee вне форума
GBee
 
Аватар для GBee
Код больше радует и объясняет, чем статья. Но за перевод спасибо. Возможно вам надо было от себя рассказать, как поняли.
Старый 13.09.2012 02:03 artcraft вне форума
artcraft
 
Аватар для artcraft
Да то что CollisionSystem решает что разрушить а чему жить дальше не сильно элегантное решение, хоть и прокатывает для этой игры. В идеале нужно каждому соображающему объекту создавать свою ai-подсистему, которая и будет заниматься принятием решения умереть от удара астероидом или нет.

Я не очень согласен с тем что системы не имеют права общаться между собой напрямую, но это уже совсем другой разговор.
Старый 17.09.2012 07:50 caseyryan вне форума
caseyryan
 
Аватар для caseyryan
Никто не заметил, что в примере с процессом метод с типом void возвращает булево значение?
Код AS3:
  public function addProcess( process:IProcess, priority:int ):void // wtf?
  {
    if( process.start() )
    {
      processes.add( process, priority );
      return true;
    }
    return false;
  }
Эта же ошибка и у автора оригинала
Старый 17.09.2012 13:06 artcraft вне форума
artcraft
 
Аватар для artcraft
поправил
 

 


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


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