Previous Efforts

Over the past few days, I took a bit of time to get myself acquainted with entity component systems and artemis-odb in particular. I already used it somewhat in a prototype quite a while ago but I still feel like having only scratched the very surface of what it offers.

Rectangle Eater

I started off with a very simple quasi-port of Erik Hazzard’s Rectangle Eater. He originally coded it in Javascript and delivered a brilliant tutorial alongside it. If you have never heard of entity component systems then this is a brilliant starting point.

I did only get my inspiration from the idea - I really think rectangle eater should be game development’s ‘Hello World’ - and started working on it without looking at the original code, so mine looks and plays a little different. But I am also mostly happy with the results of it all. It allowed me to get to grips with artemis-odb’s little quirks - especially since I coded the game itself in Kotlin.

The game concept lends itself beautifully to an entity component system trichotomy: Rectangles are entities, their position, size and color are components and the way they shrink and collide with the mouse are systems. It makes it easy to conceptualize what each part of an ecs does. At the same time, only following this concept, I believe care has to be taken not to get too hung up on the idea of entities always representing concrete in-game entities. This is in fact one of the problems I believe I had with my attempt at the next game.

Nibbles

Originally presented in Invent with Python to show fun stuff to do with that language (well, originally presented waaay back on my brick of a Nokia phone), I thought this might be a good opportunity as the next little project to broaden my understanding of ecs. Unfortunately, I believe that with the way I structured and coded this game I really did not do a good job making use of the strengths of ecs. There are three overarching problems plaguing this prototype: It is very unstable due to a poorly implemented parenting system, most systems run every frame even though the snake only moves every so often and my overall separation into entities and components could be better.

The Inefficiency

Nibbles only moves every .5 seconds. The app itself runs at around 60fps. That means There are 29 frames (‘ticks’) that all of the systems dealing with logic should really be able to rest. My systems however, run all the time. Every system, every frame - except for the one that actually moves the snake.

In artemis-odb you can create IteratingSystems, which simply iterate over all the entities which they contain, or IntervalIteratingSystems. These systems only run every so often and I passed in .5 seconds for the movement system. I believe a much more practical approach, without creating too much inter-dependency (which I am still struggling with) is to refactor the systems to be invoked by an event system. To that end, it might be useful to create a TurnSystem, which fires every time a ‘turn’ is up (snake is essentially slow enough to call game steps turns, right?). Maybe StepSystem or even UpdateSystem would be better, but I like calling them turns, since it clearly marks them only being called every so often and also delineating between the actual game updates (rendering every frame etc) and what happens logic-wise.

Artemis-odb features custom system Invocations that seem perfect for this sort of thing. I have not delved into them yet, however, and they also seem somewhat complicated to get your head around. Another idea might be to use the more drop-in solution of an artemis-odb eventbus which gets automatically injected into the systems. Then, all the TurnSystem has to do is dispatch an EndTurnEvent and all systems that have implemented a listener can use this as their update function, or their getting-going function or even reset function, whatever fills the needs best. (Think for example of a system that updates every frame but gets reset by a new turn - an animated clock showing when the snake next moves for example.)

The Parenting System

Currently, the game has no real notion of parenting. That is mostly because I wanted to avoid any finagling with deprecated entityIds. Here’s the quick rundown of what I gathered:

Artemis uses simple Int values as their entities. That means, it essentially turns the concept of entities containing components around and has each component contain an id that tells it which entity it belongs to (as many ecs systems do). This is very efficient, fast and makes lookup for the systems a breeze.

But what happens when an entity gets destroyed? Well, the components get notified and destroyed. (Remember, the entity itself is only an id attached to the components.) So, if one component needs to reference another - say the position of a parent entity - then simply storing that id as ‘parent’ will work as long as no entities get destroyed, but then it might point to a completely wrong entity - or null. There is a way to counteract this in artemis-odb called Managed Links and used by annotating the field referencing an id with @EntityId, which does all the referencing and nulling for you, but I wanted to keep it simple and without including too much advanced concepts for this little game. Also, it would need to include another artemis plugin to not slow down the system. This makes little difference for our little snake clone, but is hugely impactful in larger systems.

So, how am I doing it now? Nibbles is made up of many entities that all contain the Body component, as well as a GridCoordinate component representing their position in the game.

data class Body(var index: Int = -1, var shouldGrow: Boolean = false): Component()
data class GridCoordinate(var x: Int = 0, var y: Int = 0): Component()

Each body component contains an index number that tells us its position along the snake (Head is 0, Next is 1, the Tail is the last element). This generally works well and lets us create a system iterating through all the entities with a body component and do something with them. The doing something with them is where things get a little more complicated, however. Let’s take a look at the process function of the BodyFollowSystem, which acts as a kinda-sorta-parenting system.

override fun process(entityId: Int) {
        val body = CBody[entityId]
        val index = body.index
        val myPos = CPosition[entityId]
        val parentPos = positions[index-1]

        positions[index] = Pair(myPos.x, myPos.y)

        if (index > 0 && parentPos != null) {
            val deltaPos =
              Pair(parentPos.first - myPos.x, parentPos.second - myPos.y)

            CMovement.create(entityId).direction = when {
                deltaPos.first > 0 -> Direction.RIGHT
                deltaPos.first < 0 -> Direction.LEFT
                deltaPos.second > 0 -> Direction.UP
                deltaPos.second < 0 -> Direction.DOWN
                // is never reached
                else -> Direction.LEFT
            }
        }

        if (body.shouldGrow == true) {
            body.shouldGrow = false
            grow = grow.copy(true, grow.second)
        }

        grow = grow.copy(grow.first, CMovement[entityId].direction)
    }

It iterates through the body parts, starting from the head and moving to the tail. It carries an internal positions Array, that is filled with the positions of the body part. Then the system looks at the position of the body part with an index one lower than itself and calculates the difference in positions. Depending on in which direction the other index differs it creates a Movement component on the entity with the position it wants to move in. For a hacky prototype this kinda works, but it has problems.

First of all, the system now carries state all of a sudden (with the positions array). Generally, you want to avoid giving internal, mutable variables to the systems itself as much as possible. Also, the system really does not know how to deal with positions of the ‘parent’ that are not right next to an entities current position. It will continue passing along a direction - even though that is not at all how the bodypart can stay attached to its parents movement.

This makes the system highly unstable, and we see a bug resulting from it in the animation above - the new body part would be placed outside the map, which is prohibited by another system (BorderSystem, which creates walls outside the play area that the snake can not move into). The other system places it toward the closest position possible instead and suddenly the child-parent connection is already broken, as it creates false movement in the part.

How could this be prevented? For one, the system could check the parent entities movement direction and set this Body parts position to ParentPosition -1 in current MovementDirection. Or, it has a fail-safe built in for detecting when the difference between positions becomes greater than 1 and then it resets the Position to a reasonable one next to its parent. These are panaceas however. I believe the problem here lies not in how this system operates itself (though it is inefficient in its own right), but in how I structured the entities and their components in general.

The Components

class GridCoordinate(var x: Int, var y: Int)

class Image(filename: String, var texture: TextureRegion)

class Body(var index: Int, var shouldGrow: Boolean)

class Apple

class KeyboardInput(var left: Int = Keys.LEFT, ... -SNIP- )

class Collideable(var effect: CollisionEffect)

These are all my components. And they are kind of a mess. Apple has been reduced to a mere tag after some rewriting of the DisplaySystem and every Body contains the shouldGrow variable, even though only the last segment of a snake really ever ‘grows’ and the snake Body and GridCoordinate kind of want to get at the same thing in different ways. Luckily, ecs really lend themselves to internal restructuring, so let’s think of some ways we could rearrange these things.

Since the snake is one complete package - whenever something happens to it, all of it is affected - it might be worth thinking about making all of the snake one entity. As mentioned above, the snake itself is made up of many entities which all contain a Body component right now. Another way to structure this would possibly be to have the whole snake be one single entity, and SnakeBody be a single component on it which contains a list of all the positions of its segments.

data class SnakeBody(var Segments:Array<Position>)

This would let us create a BodyFollowSystem which does essentially the same thing as our old system, but does not have to iterate through all the single entities twice (once getting their location, once setting their location) but do it in one go. It would thus make it easier for the snake segments to follow themselves. You just set it to the position of the previous segment for all except the first array element. The first array element should be the position of the head.

One problem that becomes quickly visible is that we are tightly coupling the snake body segments - so tightly in fact that they don’t exist other than in the complete SnakeBody Component. This also makes things like drawing and collision easy in the short run - simply draw an image for every entry in the array, same deal for collision - but makes this concept really not universally usable. If the image for rendering usually comes from an Image Component (as is the case here), we have to create exceptions and special cases within the draw system. We could circumvent that again by using another draw system just for the snake, but I don’t believe an extra system for just the one entity really pays off. Finally, what if there were to be a powerup that makes one of the snakes' segments able to be passed through? This simply would not work without restructuring the SnakeBody Component itself in our case. It goes against the idea that we want to couple as little as possible in ecs, allowing component reuse and flexibility.

Another idea would be to somehow pass the intended position to each child segment before it simply moves there. This comes close to emulating a parent child relationship without needing any Managed Links between entities. One way of doing that is letting a system iterate over all child segments and passing them a new GridCoordinate by taking the parents' position and subtracting its Movement Component direction. Movement in this game really is only a direction and the movement system moving the entity along the grid in that direction.

Questions like these are really at the core of getting more out of ecs. Sure, this is an exceedingly simple example but it demonstrates the idea of reusability and how abstract you want to go pretty well. One really good tutorial for this restructuring workflow is this bomberman tutorial, which covers some more of the conceptual design side of ecs.