Categories
entity systems games design programming

Designing Bomberman with an Entity System: Which Components?

Today I found a new article on implementing a Bomberman clone using an Entity System (in C#) – but I feel it doesn’t quite work.

It’s a well-written article, with some great illustrations. But the authors “Components” struck me as over-complicated and too behaviour-focussed. If you’re designing a game based on an ES, this isn’t a great way to do it – it feels too much towards old-style OOP gameobjects.

This is an opportunity to talk about good and bad directions in Component design. I really like the author’s idea of using Bomberman as a reference – it’s simple, it’s a well-known game, but it’s got a lot of depth. Read the original article to refresh your memory on Bomberman and how it works. Then come back here, and we’ll do a worked example on choosing components for a game. I’ll completely ignore the programming issues, and only look at the design issues.

Choosing your Components

Design rules for Components (rules of thumb):

  1. Components are data, not code
    • …game-state, not functionality
  2. Components are as small as possible
    • …atoms: the smallest set of data possible, for a particular aspect of the entity
  3. Components never share data
    • …in Database terms: everything is Normalized (Google it)
    • …if data should be in two components, but you can’t decide which: create a third component for it

Looking at Bomberman

List the behaviours, list the data

This is an iterative process. I start with two lists written at the same time. Writing items in one list often makes you think of something that’s needed in the other list, so you keep jumping back and forth.

Here’s a first draft (probably incomplete, but it’s a good start):

Behaviours: Bomberman

  1. move a player up/down/left/right
  2. drop a bomb
  3. detonate a radio-controlled bomb
  4. kick a bomb
  5. jump one space (some versions)
  6. teleport a player (environmental)
  7. kill a player (environmental)
  8. upgrade a player’s FUTURE bombs (don’t affect the ones already on floor)
  9. dissolve a block
  10. spread/block the explosion according to several possible rules (depends upon multiple factors)

Data: Bomberman

  1. Position of each player in pixels (for visual representation)
  2. Cell that each player is standing in (for the game logic)
  3. bombs in play
  4. fuse-time left on each bomb
  5. explosion logic for each bomb (tiles covered)
  6. explosion logic for each bomb (tiles affected)
  7. explosion logic for each bomb (tiles that block / shadow the coverage)
  8. score for each player (number of kills, number of deaths)
  9. positions of all solid blocks
  10. positions of all soft blocks
  11. positions of all powerups
  12. positions of all special features of environment (e.g. teleporters)

Convert Data into Components

(remember: stick to the rules for designing a component)

My preferred way to do this is convert ALL the data into primitive data types (i.e.: int, float, string, boolean, enum, list, map).

For a simple game, you should never need “list” and “map”

The ones I tend to use most often: enum, float, int, boolean, string

This list often contains duplicates; that’s fine, we’ll come back to it in a sec.

All data, converted into primitives

  1. Position of each player in pixels (for visual representation)
    • position.xpixels (int)
    • position.ypixels (int)
  2. Cell that each player is standing in (for the game logic)
    • position.xcell (int)
    • position.ycell (int)
  3. bombs in play
    • position.xcell for each
    • position.ycell for each
  4. fuse-time left on each bomb
    • fuse.secondsLeft (float)
  5. explosion logic for each bomb (tiles covered)
  6. explosion logic for each bomb (tiles affected)
  7. explosion logic for each bomb (tiles that block / shadow the coverage)
    • this is where it gets tricky: the data you store is heavily dependent on how you intend to write the “behaviour” code later on. Let’s skip it for now
  8. score for each player (number of kills, number of deaths)
    • score.kills (int)
    • score.deaths (int)
  9. positions of all solid blocks
    • position.xcell
    • position.ycell
    • we could assume that “default” squares are solid, or empty. Let’s assume “solid”, so that anything off the edge of the playing area is inaccessible by default
  10. positions of all soft blocks
    • position.xcell
    • position.ycell
    • solid.isSolid (boolean)
    • material.hitpointsRemaining (int)
  11. positions of all powerups
    • position.xcell
    • position.ycell
    • powerup.type (enum)
  12. positions of all special features of environment (e.g. teleporters)
    • position.xcell
    • position.ycell
    • solid.collisionEffect (enum)

Components, after some wrangling

Now … spend some time looking at that list and thinking about it. Think about duplicates: all those “position.xcell” items – ARE THEY REALLY THE SAME THING?

(if they’re not, if some of them are “sort of the same, but actually different – and might change in future”, then you need to have two types of Position component. This already happened right at the start when I realised that the “on screen” position of the sprites is similar-but-different from the cells in which each bomb sits. One is finer granularity than the other. A very simple implementation of Bomberman could have them the same component – but it’s obvious that you’d want to “upgrade” this after only a few builds, and add pixel-perfect positions, because it looks so much better)

  1. CellPosition
    • x (int)
    • y (int)
  2. ScreenPosition
    • x (int)
    • y (int)
  3. TimedEffect
    • timeRemaining (float)
    • effectType (enum: EXPLODE1, EXPLODE2, VANISH)
  4. Explosion
    • distance (int)
    • affectsSolids (boolean)
    • affectsSofts (boolean)
    • affectsPlayers (boolean)
    • penetratesSolids (boolean)
    • penetratesSofts (boolean)
    • penetratesPlayers (boolean)
    • damage (int)
  5. PlayerBombs
    • …everything from Explosion?
  6. Score
    • kills (int)
    • deaths (int)
  7. Destroyable
    • hitPoints (int)
  8. Collideable
    • blocksPlayers (boolean)
    • blocksFlameDamage (int)
    • addsFeature (enum: FLAME_LENGTH, BOMB_AMOUNT)
    • triggersEffect (enum: DEATH, SELF_BOMBS, TELEPORT)

That was my first attempt. It is poor: by the time I got to the end, I’d changed the way I was thinking about concepts compared to the beginning. The list above is self-contradictory, it has to be re-done

By the end, I’d realised a couple of things. Firstly, the “data” of explosions can be used to model most of the flame Behaviours, so long as we treat each “flame” square like a depth of water that “flows” to either side, getting weaker by one point each time it moves an extra square.

Secondly, I noticed that my “Explosion” component was getting very large – remember: Components should be as small as possible.

Thirdly, the enums inside my Collideable were very non-homogenous. i.e. they seemed to be serving a lot of purposes at once. This is a warning sign with ES design: “heterogeneous data” is what your Components are for: heterogeneous enums suggest you need to split your Component

So, we have two big changes to make on a second draft. We’re going to re-define everything to do with explosions in terms of “water that flows”, and we’re going to split two of the components into smaller pieces (one because it’s “too big”, the other because it’s “too heterogeneous”).

Components, Attempt 2

  1. Explosion
    • power (int) — decreases by 1 each square it spreads. At 0, it runs out.

I got this far, and realised: “power” suggests “damage” — but what I really mean here is “spreading power”. I thought for a moment: could I ever want to use this spreading effect for something other than damage?

Yes! What about a powerup that “explodes” in a radius? Cool. It would be EXACTLY THE SAME thing, but it proves the name I’d chosen at first was a bad one.

So, I hastily renamed both the Component and that attribute:

  1. CellPosition
    • x (int)
    • y (int)
  2. ScreenPosition
    • x (int)
    • y (int)
  3. TimedEffect
    • timeRemaining (float)
    • effectType (enum: SPREAD, VANISH) — simplified now: spreading is neatly contained inside Spreadable below.
  4. Spreadable
    • depth (int) — decreases by 1 each square it spreads. At 0, it runs out.
    • spreadPattern (enum: FLOW_AROUND_OBSTACLES, FLOW_CARTESIAN, FLOW_IGNORE_OBSTACLES ) — the Behaviour will interpret these
  5. Score
    • kills (int)
    • deaths (int)
  6. Destroyable
    • hitPoints (int)
  7. Collideable
    • height (int) — if height > Spreadable.depth, we flow AROUND instead of OVER. Also, we can give players a “height” they can move over. This also lets us implement “jumping” in terms of increasing (for a single step) the height you can move over (makes it easy for us to have “jumpable” and “non-jumpable” squares).
  8. Damager
    • inflictDamage (int)
  9. BombLayer — (something that lays bombs … not a rendering “layer”)
    • spread (int)
    • depth (int)
    • damage (int)
  10. Teleporter
    • cellsDeltaX (int) — +1 = right one square, -1 = left one square
    • cellsDeltaY (int) — +1 = down one square, -1 = up one square
  11. PowerupPlayer
    • addsFeature (enum: FLAME_LENGTH, BOMB_AMOUNT)
    • amount (int) — +2 has double the effect, +3 has triple, etc

Exercises for the reader…

There’s a bunch of holes in what I’ve written above; this post is meant to be indicative of what I’d be doing, rather than a complete game-design document. At this point, I think you have enough to fill in the blanks easily – if not, post in the commments and I’ll add some more info and guidance.

In particular, I look at this and ask the following questions:

  1. Without changing anything, can you implement powerups that disappear if not collected quickly enough? (easy)
  2. …what about: powerups that disappear when the player walks on them? (less easy)
  3. How do make a powerup that’s very powerful, but has a risk of doing something nasty to the player – e.g. teleporting them when they collect it?
  4. Or a powerup that has a chance to kill the player outright?

Consider the possibilities of your new Components

I like to do this as a sanity-check that my Components are as flexible as they should be. But … it’s also inspirational to look at your system and see what emergent features it’s already created.

We look at our components and take odd pairings we didn’t intentionally design, and ask what would happen if we put them together.

e.g.: “TimedEffect { timeRemaining = 2, effectType = SPREAD }” + “TimedEffect { timeRemaining = 4, effectType = VANISH }” + “Spreadable { depth = 3, spreadPattern = FLOW_CARTESIAN }” + “PowerupPlayer { addsFeature = FLAME_LENGTH, amount = 2 }”

  • Armageddon!
  • Drop one of these when the level reaches a timeout (e.g. 5 minutes), and it will spread through the level, powering-up every player it hits, and fading away as it passes, like a wave rippling outwards
    • This behaviour/feature is entirely automatic, requires zero additional code!
  • I set the VANISH to take two more ticks than SPREAD, on the assumption that my “spread” behaviour will only spread into tiles where the effect doesn’t already exist. That’s a sensible implementation.
    • …this way, the spread is always outwards, because the inner ring of “about to fade” blocks prevent the spread going backwards into the middle again
    • If you tweaked that Behaviour (maybe add an extra flag to Spreadable, e.g. “(boolean) spreadsIntoTilesAlreadyPresent”), you could make this into a reverberating wave, that bounces back and forth forever across the level.
    • You could make a whole level around this: have a couple of waves that bounce back and forth giving powerups, and have no other powerups in the level. Players get powered up rapidly by the waves washing over them…

Did this post help you?

You can say “thank you” by giving me your email address, and letting me contact you next time I make a game of my own:

[smlsubform emailtxt=”” showname=” mailinglist=”pif-entity-systems”]

70 replies on “Designing Bomberman with an Entity System: Which Components?”

@Adam
**Mike’s gone a long way in several directions of his own – I suspect he’s gone so far it’s no longer an ES as I’d see it, but there’s plenty of good stuff in there.

Thanks for the nod. In a way yes, but what I did is promote the good ideas that work which is why I advocate for the component architecture potentially backing an ES to be a superset of it; it’s useful architecture wide well beyond game design purposes, but quite at home for that too.

**It was easy to tack-on functionality and richness, but it added more and more complexity to the core ES usage patterns. From what I’ve heard, usually every small feature seemed trivial at the time, but they gradually built up to be too much weight.

If you have the time I think it would be interesting if you can describe the characteristics of the ES implementations used. Were these in-house architectures? Did this “extra weight” come from too many direct links between systems / components; a more basic way to wire up an ES / component architecture. It seems additional architecture that can help with modularization of a code base would come in handy. IE moving toward an asynchronous event based direction instead of direct links helps, etc. I suppose it could be a matter of tooling too.

@Dusan
**However I believe that one crucial thing that you left out are something I call queries, i.e. various pre-sorted lists that remove wasteful iteration and pre-calculated values based on components. You say Systems, Entities and Components, I say Systems, Entities, Components and Queries.

Indeed that is how my efforts essentially work.. I’d say that queries is incorrect terminology for it though since there already are queries in most ES / component architectures; IE the query interface of a component manager. In my effort and most Java efforts using the Class type information among other means like extensible enums.

I think a better description to what you are describing is “self modifying behavior”. Here is where the ever popular “extension by iteration” pattern pops up once again in my efforts. By implementing the component manager API for certain methods internally delegating to a list of systems to run that can alter behavior this accomplishes self modifying behavior. This could be used for automatic categorization optimizing iteration, etc.

** However I would really appreciate if you could write something up later. You seem to have caused quite a stir with yours first ES post and a lot of people started looking SERIOUSLY at ES concept.

This blog is not the only source of good info. I’d suggest looking into data oriented design and in particular this new online book on the topic:
http://www.dataorienteddesign.com/dodmain/

I don’t agree with everything put forward in the book, but Richard Fabian put in some effort to get this info together. Adam, you might want to add this to the ES wiki or even make a blog post highlighting it.

Very good read here – I’ve been reading your posts on ES’s and am wondering if you have any code examples of how to implement one? Maybe I just have not found the post – but this all feels very abstract (entities are not classes – how do I construct this in code then?). If I am hard-coding these classes (such as the guy who did bomberman) – then is my implementation still not somewhat rigid?

I’ve posted several conplete implementations. If google is failing you try “entity systems wiki”.

Hello Adam,

I have a question about design ideas of an entity component system. In this system Entities have NO code. They are just collections of components. The components contain no code. They just hold data that is relevant in describing the entity.

The systems iterate over specific groups of components by their component type. So a physics system would iterate over physics components. The systems take the data held within the components and use code to update the state of the entities that are composed by these components. In this regard all the code for updating the entities is encapsulated in the systems. ie. The systems operate on the data held by the components that make up the entity.

Now for an example. Suppose there are enemies in the world and an explosion goes off. Some of the enemies will die and others will fly through the air or play fall down animations. The explosion will send a damage message to each of the entities in the proximity of the explosion. So the entities will receive the message that an explosion has gone off in their vicinity.

In this case perhaps there is a DAMAGE COMPONENT registered with the entity that listens to any damage events that the entity receives. Now my confusion is with the processing of this damage event by the component. I want to try and avoid having code present in the components so there are perhaps two options:

[1] The damage component just records the damage event that happened. In the update loop a DAMAGE SYSTEM will iterate over the
DAMAGE COMPONENTS. The damage system code will be responsible for decrementing the HEALTH COMPONENT and enabling
whatever death\pain animations are necessary on the ANIMATION COMPONENT. All the logic for dealing with the damage event is
held in the damage system. The damage component merely records the damage events it receives. Perhaps after the damage
component is serviced it may remove itself from system updates until it receives another damage event and then register itself
again with the damage system.

[2] The damage component processes the damage event directly. It decrements the health component and tells the animation
component what animations to play based on the damage it received. I don’t know if this type of damage handling is appropriate.
Now the damage component knows there is a health and animation component. Also since it is updating the state
of the entity directly, there is no need for a damage system. Perhaps this is not the correct method of handling damage events?

Perhaps there are other ways of dealing with this that I am not aware of. How would you suggest that an explosion event be handled by the components that make up the entities being affected? Thanks for any help you can give me and for posting such informative blog posts on this topic and gaming in general.

Hey Adam, I’ve read most of your articles and believe I have a fairly decent understanding of ecs’s and was just wondering if you could steer me in the right direction about emulating similar behaviour to a finite state machine in an entity component system?
My problems with the general fsm structure is that they seem to be too rigid for an ecs.

I plan on making a framework / tool that can be used in runtime to edit my entities and the components / states they can be in so I want to be able to create something similar to states on the fly as opposed to hard coding them as components and systems.

FSM’s aren’t a great mix with an ES, in fact: they’re generally a bad mix for game development. They’re more of a “I have a problem, I can’t be bothered to solve it properly, so … what’s a quick hack I can use??? OH YEAH! FSM, BABY!”.

They “work” for almost everything, but rarely make a good fit.

If you have to use one, I’d put it all in a single System/Processor, with data in N fields in a single Component.

If you want to soft-code it, either make your System/Processor softcoded (outside the remit of the ES, do it however you want), or make the states themselves Components. At that point, you’ve lost pretty much every single benefit of an FSM, and I’d very strongly recommend dropping the FSM.

@James…

Err.. I disagree with Adam regarding FSM and ES integration.

particularly this part
>with data in N fields in a single Component

For example when using Java there is a collection EnumSet which is an optimized way with a backing bit set to store a set of enums from a single type. A data component could have one public member / EnumSet and store any combination of state based enums. A single system / processor could dynamically respond to various combinations of state in the EnumSet via conditional logic or a FSM. You can of course use HashSet too and store enums, but it’s slower.

In my Java middleware / component architecture / entity system I have a custom collections API implementation where the all the collections are also typed data components not requiring them to be stored in a separate data component holding a bare member variable (like how you would have to do it with standard Java collections API / EnumSet).

I take it one step further and have a extensible EnumSet implementation which uses “extensible enums”. I probably yap about extensible enums in comments above, but they come to the rescue again in defining state across module boundaries meaning that you can mix and match different enum types still in an efficient bit set based collection without dependencies between them.

You can do:
entity.getAs(DATA, IExtCopEnumSet.class).contains(FLYING);

or even better using extensible enums as the key itself (I’m sure I must have yammered about this above in comments!):

entity.getAs(ACTION_STATES).contains(FLYING);

I use this in input control of entities allowing external XML config file to indicate what extensible enum state gets added to the entity state depending on current control input from the user regardless if it’s keyboard, mouse, controller input. A system / processor can then use that extensible enum set and respond to it via conditional code or even a FSM if desired.

I’d also suggest checking out this small section of the DOD book which confirms that FSM are used with data oriented design (a basis for high level entity system architecture):
http://www.dataorienteddesign.com/dodmain/node8.html

I’m not sure where Adam is pulling FSM is bad for entity systems / game development in general. In the simplest way of organizing data components with discrete member variables as suggested by Adam above it’s a bad thing given how rigid that would be. However just add an EnumSet or whatever equivalent collection for language at hand and be on your way with a FSM.

While I’m no AI expert I’d expect them to be the basis for most AI systems.

Yep, there are much better ways on a language by language basis. Java went from “terrible support for enum” to “possibly the best support of any mainstream language”.

FSMs are a bad code-smell, IMHO. Usually there’s a better way of modelling whatever they represent. YMMV

@Adam
* FSMs are a bad code-smell, IMHO. Usually there’s a better way of modelling whatever they represent. YMMV

I think leaving out the IMHO in your original response perhaps put me a bit more on edge.. :D

And I agree that defaulting to FSM for general use cases is not the best. It certainly is not easier to build a good one versus bespoke system creation for adding logic incrementally.

However, there is nothing inherent about a FSM that clashes with ES architectures (ESA) when data is properly formatted for the FSM implementation. Using the ESA itself (composition of data components to define type or state) to drive a FSM is inefficient, but that is because that is an inefficient way to define types for any ESA use case.

A FSM certainly has more weight in middleware that provides a GUI based editor and is specific to say AI or input control (any state used in the AI system, etc.). It’s reasonable in the domain of building a generalized framework / tool like @James mentioned.

As you mention creating specific processors / systems to explicitly service particular logic is generally the right approach especially when incrementally building / modifying a project.

I’m trying to understand the process of developing a game under ECS, and I have a question. Does it make sense to create a component for relating two entities? Say you’re making a Pong clone, and you’re defining the game’s Players and the game’s Paddles. Each Player controls a Paddle, meaning that a Player has a one-to-one relationship with a Paddle. In this case, does it make sense to define a “PlayerPaddle” component with the properties “player id” and “paddle id”? It would allow you to associate a player with a paddle, which lets a player control their specific paddle and make it move up and down. You could also do two-way association by looking up a player by paddle, or a paddle by player. I’m otherwise not entirely sure how to model this association otherwise, which I assume I need in order to make P1’s paddle move up and P2’s move down at the same tick.

” a Player has a one-to-one relationship with a Paddle”

so you make them both components on same entity.

So, wait, in this case, a player and a paddle should be components, not entities? What would a player component have? (Well, “score” at the very least…but I would have expected a player to be a particular entity rather than a component.)

why?

Unity would force you to, because Unitys architecture sucks donkey.

Simple game, keep.it simple.

I think I’m starting to understand. Originally, I came up with a couple components that looked like this:

– Player Paddle
– player id: int? (however the ID is implemented)
– paddle id: int? (however the ID is implemented)
– Score
– value: int

This seemed strange to me. A component called Score with a single property that pretty much just describes and maps directly to its type didn’t sound very convincing, especially when that property was just called “value”. Player Paddle was even weirder: it was a component that was literally just a collection of entities (i.e. IDs). This didn’t really have much meaning and I had trouble understanding what a Player Paddle system/process would even do.

When I thought about it, a “Player” isn’t really a “thing” in terms of the game. It’s more like metadata, it isn’t an independent actor on the screen. It isn’t actually composed of other components. Like, I originally thought a Player component would have to hold a reference to a Controllable component of some sort, but what if the right paddle is controlled by an AI instead? It’s still a Player, but it’s not really a Controllable in the takes-input-from-gamepad sense.

Also, what if I wanted the *ball* to be a player? What if there was a third player associated with the ball, just for the heck of it? In ECS, there’s no reason this can’t happen – just add a Player to the ball entity. But in that case, there’s no point in adding a “Paddle” component to the ball, because that’s not at all descriptive of the ball, and is in fact contradictory. A “Paddle” is actually just a combination of components – it’s a Position, a Velocity, a Player, and eventually a Renderable (e.g. a sprite or a direction to draw a rectangle). So, a Player really has nothing to do with a Paddle at all, in isolation.

I ended up replacing Player Paddle and Score with a single component called Player, with a score property. That made a lot more sense, and you can just attach that component to the entity representing a paddle. Going a bit further, I renamed it to “Playable”, so that the component is more of a description than a thing. I think that represents the intent of components a lot more accurately.

Incidentally, I’ve also found that it helps to think of an entity as a sort of “node” that components are connected to. Each component has a string that ties it to the entity, kind of like a web. I hope that makes sense.

@rpaz
I’m probably a bit late to this since it has been a month but to clear it up for others (and if you still haven’t quite grasped it all then for you as well), your mindset was definitely a little too focused on OOP approaches in comparison to ECS in the way you were thinking about creating your game. It sounds like you were getting the idea with your last post with how you changed your perspective on your approach and how it all works.
With OOP, we relate things to as they are in reality where you have a player who owns and controls a paddle, etc; whereas, with ECS you have to think more functionally about the game – it is being driven by data – where ‘Components’ are specific sets of data for specific ‘Systems’ to manipulate to achieve a result, and ‘Entities’ are simply a ‘Component’ container for a related group of ‘Components’.

For player input, you are right that you would have a component for input like ‘Player Controllable’ or ‘Gamepad Input’ (or whatever you want to call it in relation to what sort of input it achieves) to allow for the player/user in real life to interact with an entity (any entity really). Remember, entities can have components added or removed to them at any time; they are not locked down to any static setup.

In terms of scoring – games of pong have a score for each side. If you really think about it, and keep it simple for a simple game, the scoring is really just related to each paddle. A score is generated for the left paddle and a score is generated for the right paddle; you don’t need to apply ownership for the score to a player or AI. All you need is a Score component which would be attached to each paddle entity.

To provide a breakdown of how ECS would look for a pong game, this is along the lines of what I would implement (it’s rough and just covers the general idea instead of absolutely everything you could have):
Render System
– Render’s all entities’ Sprite component’s ‘sprite texture’ at their Position component’s ‘x’ and ‘y’ position.
Collision System
– Checks for collision between entities which have a Position component and a Box Collider component or a Circle Collider component.
Input System
– Processes user input for any entities that have a Player Controllable component, setting the ‘y target position’ ready for the Movement System to make use of (you could remove the component from one paddle and switch it to the other, or the ball, at any time mid-game if you wanted; ECS makes it very easy for you to change up how things work).
AI System
– Runs some basic prediction/simulation logic for entities with an AI Controllable component and assigns a ‘y target position’.
Movement System
– Updates position for entities with a Position component and a Player Controllable component, AI Controllable component (using their ‘y target position’) or Velocity component by interpolating the entity’s position to the target position.
Score System
– Displays current score for each entity that has a Score Component and Match Score Position Component.
– Uses Match Score Position Component to set the score’s screen location to be displayed at.
– Uses Score Component to display the score ‘win count’.

Player Controllable Component
– y target position : float
AI Controllable Component
– y target position : float
Sprite Component
– sprite texture
Position Component
– x : int
– y : int
Velocity Component
– x velocity : float
– y velocity : float
Circle Collider Component
– radius : float
Box Collider Component
– x min : float
– x max : float
– y min : float
– y max : float
Score Component
– win count : int
Match Score Position Component
– x : int
– y : int

Paddle Entities would have the following:
– Player Controllable Component (for one paddle, both or don’t have it at all; could have the AI play each other instead)
– AI Controllable Component (for one paddle, both or don’t have it at all; could play two-player instead)
– Position Component
– Sprite Component
– Box Collider Component
– Score Component
– Match Score Position Component

Ball Entity would have the following:
– Position Component
– Velocity Component
– Sprite Component
– Circle Collider Component

As the game is very simple, you could technically merge some of the components, like the Score Component with the Match Score Position Component, but I like to build systems out as modular as possible so it’s as easy as possible to incorporate new systems and components, or remove them.
It is best to think of a component as a set of data for ONE specific thing, the separation of the Score data reinforces this. If another system wishes to query the ‘win count’ later for doing something like adding it to a highscore of ‘total wins overall’, having the Match Score Position data there is data the specific system would not need to know about, let alone have access to.

You would likely want to setup a bounds for the game that the paddles stayed within – this could easily be achieved just by creating an entity with the Position Component and the Box Collider Component; you could add a Sprite Component to it too, if you want to give the game a textured background.
You can already see the potential for re-using components and chucking them together in seconds to add something completely new.

Very comprehensive reply, interesting stuff.

I’d lean towards discarding “Match Score Position Component” (it’s implicit that an entity with a ScoreComponent and a Position … means that thing).

Similarly remove the y-target-position from “AI Controllable Component” – the AI component needs only a “AI ID” (so you know its an AI, and because its very likely you need to distinguish “which” AI), but it should be using the same component as the player for y-target position.

…but, as you point out: “I like to build systems out as modular as possible so it’s as easy as possible to incorporate new systems and components, or remove them.” — which is a perfectly good reason for not doing as I describe. In general, I’ve found normalised components (c.f. SQL normal form) works better for me, but YMMV. Need to experiment to find what balance of flexibility fits for you.

Isn’t ECS effectively going back to procedural programming?

Systems – procedures.
Components – records.

I’m pretty sure that was explicitly stated in the original articles.

Although it’s not quite that simple.

Comments are closed.