Just about four years ago, I led a seminar on game engine design. My inspiration that Summer was to explore the interplay between design patterns and game development. To this end, I created EEClone, a simplified clone of the incredible and addicting Every Extend. EEClone's design explicitly features patterns, although I was careful not to wedge pattern reifications where they did not belong. I consider this project a success, and it led to a series of scholarly articles and a fascinating research direction. I have been considering ES for a project with potentially much greater impact, and so I decided to revisit my cloning of Every Extend gameplay, now with the purpose of exploring ES implementations.
Here is what it looks like in action.
The entity manager is very similar to Adam's, and I'm grateful for his implementation sharing. For me, the most significant challenge was determine how to slice the problem up into components and systems. Keep in mind that in an entity system, components are "dumb" data objects and systems operate over components.
These are the components that I used. [EDIT: I did some refactoring the morning after this post, and the changes are reflected below.]
- Explodable is a tag component that indicates that the corresponding entity should explode if it hits an explosion.
- Exploding indicates that an entity is exploding. Contains the max explosion radius, rate of expansion and decay, and whether the explosion is expanding or collapsing.
- InputExplodable is a tag component that indicates that the corresponding entity can be blown up by direct player input.
- InputMovable indicates that the entity can be moved by direct player input.
- PhysicsMovable holds a vector that represents the direction an entity is moving.
- Position is a position in space, consisting of x, y, width, and height.
- PrimitiveRenderable has a shape code and a color, indicating that the entity should be drawn with graphics primitives.
- Renderable has an Image and indicates that the entity should be drawn as an image sprite.
These, then, are the corresponding systems:
- ExplodingSystem processes Exploding things, scaling them to the right size (i.e. changing their Position), handling the transition from expanding to collapsing, and removing the corresponding entity when the explosion is complete.
- ExplosionCollisionSystem looks for intersecting Exploding and Explodable entities, replacing the Explodable ones with Exploding ones.
- KeyboardInputSystem monitors the keyboard and processes InputMovable and InputExplodable entities accordingly.
- PhysicsMovementSystem goes through all the PhysicsMovable entities and updates their Position based on the their PhysicsMovable velocity vector.
- PrimitiveRenderSystem draws PrimitiveRenderable entities at their Positions based on the shape and color in the PrimitiveRenderable component.
- RenderSystem draws images at the corresponding Position baed on the image reference in the Renderable component.
Obviously, some of those names are ripe for refactoring, but this is an accurate representation of the build in the video. To give you an idea of what the implementation feels like, here is the code that creates the crazy guy you control with the keyboard (Boognish), using some method chaining:
manager.createEntity() .add(new Position().x(320).y(240)) .add(new Renderable().image("boognish.png")) .add(new InputExplodable()) .add(new InputMovable().speed(120)) .add(new KeyboardExplodable());
I started writing this code a two weeks ago or so. Then based on the comments in my other post, I decided to try the Game Object Component System (GOCS) approach described by Chris Stoy in Game Programming Gems 6. In a nutshell, GOCS hits the same pain point as ES, but in GOCS, game object components are regular "smart" objects with their own methods, so there is not the ES distinction between the component and the system that processes it. As I started working on the GOCS code, I kept coming back to the arguments for ES and ended up along the path to simply re-creating my incomplete ES demo. GOCS is now back on the shelf, and I feel like after weeks of off-and-on contemplation and a few days of coding, there's something about ES that has clicked with me.
I am considering spending another day or two with this code to try to make a more compelling example, a real playable game that I could use as a case study in my class next semester. However, even coming this far, I feel like I have a better grasp on how to decompose game elements into components and systems, perhaps well enough to begin work on the technical specification of the Morgan's Raid game.