Entity Component System

What is an entity component system (ECS)?

ECS is a programming paradigm, similar to object-oriented programming.

Why use ECS?

Computer memory stores information as a series of binary numbers. Mostly these numbers represent ‘data’ i.e. decimal numbers, text, images, sounds. However it may also represent a uniquely different type of information: machine code. This ‘code’ consists of instructions that tells the microprocessors what to do.

Programmers (‘coders’) produce code. The microprocessors run the code which tells them how to receive input data, modify data, and output data. Some data may be supplied by the programmer e.g. the graphics for a game. Some data will be supplied by the user, e.g. the text of a word processor document.

Because code is stored in memory exactly the same way as data it is possible to write code that modifies code. In the early days of programming this was considered an impressive trick. However it makes code difficult to understand if it changes while you're trying to understand it, so it came to be considered bad practice. It was also possible for one program to modify the code and hence the behaviour of another program, or for user to input special data into a program to trick it into modifying itself. Nowadays self-modifying code is considered a security risk and modern microprocessors mark sections of memory for data and code, and refuse to allow the code to be modified.

The evolution of programming languages can be seen as a search to find the best way of relating code to data. Object-oriented programming does this by grouping code and data together into ‘objects’. An object contains some data and some methods (functions, code) that operates on that data. One object is prohibited from modifying the data of another object except by using that object's methods. This makes it easier to understand one object without needing to understand the behaviour of every other object in the system.

Object-oriented programming has been found to work very well for most applications. However it has some snags when programming games:

  1. Game code must execute many millions of times per and must execute quickly. If many thousands of objects are created they will fill the available memory and necessitate unused objects being deleted. Some systems (e.g. Android) are quite poor at doing this and introduce pauses into the game.

  2. RAM access is relatively slow, so modern CPUs copy data into small high performance caches where they can operate on it quickly. Most OO systems (at least though without value types) store object data in a heap and give no guarantee that similar objects will be store near one another, so they may not be in the cache when needed. Further, even if objects are stored contiguously, you don't usually need every field of the object at once. For example, when updating physics you might need the (x, y) co-ordinates of every object, but having the objects' image data in the cache is just a waste.

  3. Traditionally OOP relies on inheritance to combine different object types together and reuse code. Objects like game sprites have such variety that they can be difficult to model in an inheritance based class hierarchy. Hence composition tends to be used instead.

ECS takes composition to its logical conclusion. Instead of objects we have entities. An entity consists of a number of components. Each component is just a piece of data, no code. So there is no code in an entity. Instead the code is placed in separate systems. But systems do not operate on entities! Systems operate on groups of components.

Now a system could be defined to operate on exactly the same group of components that make up an entity. For example an asteroid entity might consist of an image component and a position component. A drawing system operates on all entities with image components and position components to draw the image at the position on the screen. That is it operates on asteroids.  But let's add another entity: a spaceship that can move. It has image, position and velocity components. The same drawing system will draw the spaceship with no changes required to the code!

Entities are very lightweight and have no real identity in the way that objects do. An entity is just some data components stored in memory tagged with the same ID number.

What languages support ECS?

There are no ECS languages in the way there are OOP languages. But language level support is not required!

It is possible to do OOP in languages that do not provide explicit support for it. For example many C programs are written in an OOP style. But it requires the programmer to be disciplined because the language will not enforce type safety or other OOP restrictions.

Similarly ECS can be used in an OOP language. Each entity can be represented by an object composed of component data objects. Each system can be represented by an object containing code (a method). There exist several ECS libraries to make the syntax for this nicer and the internal entity/object representation more efficient.

For Java I recommend Ashley if you want an ECS library that is easy-to-use or Artemis ODB if you want one that is high-performance.

Is there an ECS equivalent to the class?

Yes, in some libraries there is the archetype, which produces entities with a certain set of components.  However components could always be added or removed from the entity after creation.

Don’t dynamic languages already do this?

Yes, languages like Ruby and Javascript can modify any object to add any component after creation.  Also using ‘duck typing’ they can address every object that has a certain component - those that don’t have the component will simply not respond.  However they are not as fast or as type safe as Java or C++ with an ECS library.

When should I use OOP and when should I use ECS?

When starting with ECS the great temptation is to use it for everything.  Anywhere you could have used an object, you use an entity.  This gives you the convenience of having the entire state of your game stored in a single ECS which can be serialised when you want to save game.

However, I have come to think that ECS should only be used for what were traditionally called ‘sprites’, that is things that exist in the universe of the game and are represented on screen.  It is sprites that are a difficult fit for OOP, and it is sprites that need to be rapidly created, modified and destroyed.  So I would use ECS for a spaceship, and it would have components for image, position, velocity, player input, weapons, health.

Initially I used ECS for other things.  For example menus (they are displayed on the screen, right?), objects representing each player in a multiplayer game, score, the game background.  Currently I use standard OOP for these.  The game background still has a system in the ECS to draw it, but it operates on a simple image object.  I don’t see the point of putting a singleton entity/component into the ECS.  For a more complicated game the background might be broken up into separate objects for every tile and then these objects could be components in the ECS.

Twin-stick shooter controls

Currently UniBlaster (the main engine of RetroWar) supports four different games / character types. They are similar in many ways, but the controls vary slightly.

  • Droids use the right stick to aim, and automatically shoot a stream of low powered bullets (A)
  • Tanks use the right stick to aim, but require the button to pressed to shoot a single high powered projectile (B)
  • Ships cannot aim, fire straight forward when the button is pressed (C)
  • BombBots have no gun so cannot aim, drop bombs when button is pressed (C)

I've been thinking how to create a more general abstraction of these differences.

Instead of one weapon, each character would have three weapon slots:

  • A fires when right stick is moved, unless button is depressed
  • B fires when right stick is moved and button is depressed
  • C fires when right stick is neutral and button is depressed

This would allow characters to carry multiple weapons and use all three without any switching and only needing one fire button.

Possible uses:

  • The current shooting-type A/B characters could gain a close combat C weapon.
  • Player has a choice between firing a stream of low power bullets, or one high power bullet that will take time to reload. (Also can make reload not begin until player has finished aiming - see CrossCode for example of this.)
  • Player could be given a choice between firing low power bullets, or pressing the button and firing high power bullets but reducing his speed, perhaps to zero. (Need to add speed reduction option to weapons)
  • Player could be given a choice between shooting and dropping bombs, depending on whether he moves right stick.
  • An A type weapon that does no damage, but merely assists in getting the aim right before the B type weapon is fired.
  • A B type weapon that doesn't fire when the button is pressed, but when it is released. Thus holding the button down does nothing, so fire rate entirely depends on how quickly the button is mashed. The amount of time the button is held before release could determine how charged up the weapon is. Why not fully charge every shot? Because charging reduces your movement speed.

Finally, I've been thinking about how RetroWar could be played on actual 8-bit joysticks. Currently 8-way d-pad movement is supported in all games as a virtual stick, (although it puts you at a disadvantage in most of them that aren't grid based.)

But then how to aim when there is no second stick for aiming? You could just fire in whatever direction the sprite is facing when the button is pressed. But that would fire the C type weapon, which is likely to be non-directional bomb anyway, and leave no way to fire the A directional gun. Games like Paradroid handled this by requiring you to move a bit before pressing fire. So the virtual input would create a second stick pointing in the same direction as the real left stick when you press the button with no input from any right stick. Many games also had a locking mechanic, so once you started shooting you held down the button to lock the firing direction while you could move in a different direction. Not sure how that could work in my scheme.

Discord!

Everyone who is anyone seems to be on Discord now, and so are we.

Please join our channel!

Currently listening to GamesQuest podcast by Matt Bauer. Interesting stuff when we are about to enter Early Access.

8-bit vs 16-bit

RetroWar deliberately limits itself to what would be possible to run on an 8-bit computer or console. We believe limitations in visuals produce innovations in gameplay.

Occasionally, after much thought, we break this limits when the benefit offered by modern systems seems worthwhile.

But what exactly is an 8-bit console capable of? Well, in addition to the 8-bit CPU, each system had different chips for graphics and sound, and so capabilities varied greatly, and even overlapped what was possible on 16-bit systems. We have to arbitariliy decide where to draw our lines

Scrolling

Most 8-bit systems could scroll to some degree, but smooth multi-directional scrolling didn't become universal until the 16-bit era. Currently we are not including it, but this is a good candidate to add if we decide to implement more 16-bit type features.

Screen shake

Any 8-bit system that was capable of scrolling was also capable of screen shake, so we include it.

Colour palette

We are using a very simple 16 colour fixed palette. We also try to limit each sprite to 4 colours. Some 8-bit systems had more colours and/or could switch to different palettes. 16-bit systems varied greatly as graphics chips improved. I don't really see the point in doing pixel art with, say, a 4096 colour palette - I think you may as well go right up to 'modern pixelart' in that case and use a 32-bit pallete.

Alpha channel transparency

Makes many nice effects possible, but this was quite advanced even for 16-bit systems. The only transparency 8-bit games had was done by flickering a sprite on and off quickly.

Particles

Surprisingly 8-bit systems could do particles! Just look at Defender. They couldn't do vast numbers of them, they couldn't make them glow with transparency, but they could do them. We break the rules slightly here and provide unlimited particle numbers.

Texture scaling

Probably used most famously on the SNES, 2d textures could be resized dynamically and even drawn as pseudo 3d. You could zoom in the display. You could make a sprite bigger or smaller. We are not including it. (Although Retrowar-common library does include one scale effect.)

Dynamic lighting

If you have a large palette and an alpha channel you can dynamically change the light levels, e.g. have a player carrying a torch. It makes a huge improvement to the look of modern pixelart games, but we won't be including it.

Phones as game controllers

The game I am working on is a local multi-player party game for up to 16 players. However not many people have 16 controllers connected to their PC. So I was thinking about using phones as additional controllers.

I can think of a couple of different ways to do this:

  • Create and publish a native app for Android and iOS. The PC game runs a server, the app scans for servers on the LAN and automatically connects. Touch input is sent in UDP packets. Bonus: server could announce "this is a d-pad game" or "this is a twin stick game" and the app could display appropriate input controls.

  • Write the app in JavaScript. Have the game run a webserver, which serves a page containing the app. Tell players to open the IP address of the server in their mobile browser. The browser runs the JavaScript app and uses Websockets to send the touch input to the server.

This seems such a simple, useful re-usable project that I would expect it has been done before. Does anyone know? If not, is anyone interested in working on it? It might make a nice little project for a student.