What It Is

Ash entity framework for Flash shows some real promise for rethinking how I write code.  There is an example asteroids application to review, but there is not much documentation in the code.

Brief Description, Theory

The idea is not new, since it is, essentially, relational database concepts.  The data (aka, component) exists in only one place (d.r.y. coding) and various systems use that data in various ways.  No one system owns the data, and no system keeps the data past it’s usage.  There is a priority queue (c.f. this example and the SystemList class) that manages which system gets to use the data next.
I am having a bit of difficulty, since it is not object oriented programming.  Entity systems do not have objects that encapsulate data, so it is not passed around in the same manner as in OOP.  Like relational databases, the data does not know the systems or objects that access it, and vice versa.  The persistence of data is not held in a serialized class, but rather in a simpler list (map).  Once the program starts, data can be manipulated in any way at all: saved data can be loaded, data can be removed or added, changed in the middle of some action, and so on.

For example, level editors in entity systems are easily added, since that is just another system affecting the data.  The process would be along the lines of:

  1. Assuming these systems exist and are present, removing the GravitySystem, removing the UserInteractionSystem, removing the CollisionSystem, etc.
  2. Adding an EditorSystem, a ClickAndDragSystem, etc.
  3. Saving the Game state.
  4. Loading a Game state.
  5. Removing the EditorSystem, ClickAndDragSystem, etc.
  6. Adding GravitySystem, UserInteractionSystem, CollisionSystem, etc.

The game does not have to be restarted either, since it is actually still running during the editing phase.

My Problems

This is great in theory, but I am still having a problem with actually doing any of this.  Richard Lord includes a sample game, asteroids.  The main problem I am having is putting a menu system into the game.  I want to create a TextField showing the number of asteroids, the number of ships left, and the number of bullets.  However, though I can create the TF, I am not getting any data during the update.
I cannot debug that I can figure out, since it is not an object structure I am used to.  Any help on this would be great.

Note: Like Recursion

Thinking in an entity framework is a lot like working with recursive functions.  The function assumes that the object handed to it meet certain criteria; maybe that is that an object exists, or that it is a linked list, etc.  But it makes no reference to specific objects, except in a general way.  Any other thing of the same type can be passed, and the function will chug away at it, without knowing any specifics.  Also, each time the function runs, it has no memory of the last time it ran.  Nothing specific is stored in the class that contains the function, and all data is manipulated in some referenced component.  In that sense, the code is “clean”.

Note: Object Creation/Destruction

Object creation is still a bit of a mystery.  I can make things show up and get updated now (another post on that in the near future), but I cannot figure out a good way to destroy them, nor make sure that all references are removed, nor know if they have been garbage collected or not.

Note: There Are Non-Component Objects

An unexpected part of an entity system is all the things that are neither entities nor systems.  Mostly this falls into the idea of “creator” objects and “graphics” objects (I assume “destructor” objects will be needed as well).  There are a few other things, too, but that there is anything that is not a component or system was unexpected.  And I don’t know the best way to deal with them.  These are like psuedo-components:  they are not data that is stored, do not generally exist in linked lists, but seem to be similar otherwise.

Note: Using Other Frameworks in Ash

Many of the sites I visit hook other frameworks into the entity system code.  Not sure quite how to do that, either, though I think it will be easy to answer if I find the best way to solve the object problems I am having.  (And vice versa, actually.)

Note: Automatic System Automation

In a useful blog post, Shaun Smith writes about his working with Ash.  He points out that systems automatically work on any entity that has the right components added, “When an entity is given both a Display and a Position component a Render node will be created for that entity. If either of those components is removed from the entity the Render node for that entity will be destroyed.”

Note:  What Is Really Going On

Systems are initialized with the type of Node that it will process.  That is the part I have been missing.  To create a new System, generally you should extend a ListIteratingSystem, even if there is only going to be a total of one node, as it solves a couple of the setup things.  The type of node is sent in the super() call; that node is now the filter.  During an update() call, all current nodes — which includes anything active in the program — is filtered by the System‘s node type, and each of this subset is sent to the system‘s update() method. E.g.,

The component:

package
{
   public class TestComponent
   {
      public var value:String;

      public function TestComponent(initialValue:String) 
      {
         this.value = initialValue;
      }
   }
}

The node:

package
{
   import ash.core.Node;

   public class TestNode extends Node 
   {
      public var testComponent : TestComponent;
   }
}

and the system that updates all the instances of TestComponent:

package
{
   import ash.tools.ListIteratingSystem;

   public class TestSystem extends ListIteratingSystem 
   {

      public function TestSystem(nodeClass:Class=null, nodeUpdateFunction:Function=null, 
         nodeAddedFunction:Function=null, nodeRemovedFunction:Function=null) 
      {
         super(TestNode, updateNode);
      }

      private function updateNode(node:TestNode, time:Number):void 
      {
         // use node as any linked list.  Access the members like:

         node.testComponent.value = Math.sin(time); // or whatever.
      }
   }
}
Advertisements