0
\$\begingroup\$

Say I have three entities: Player, Spikes, and Zombie. All of them are just rectangles and they can collide with each other. All of them have the BoxCollision component.

So, the BoxCollison system would look something like this:

function detectCollisions () {
  // for each entity with box collision
    // check if they collide
      // then do something
}

The issue is, the sole purpose of the BoxCollision component is to detect collision, and that's it. Where should I put the game rules, such as "if the Player collided with Spikes, diminish its health" or "if the Zombie collided with Spikes, instantly kill the Zombie"?

I came up with the idea that each Entity should have its onCollision function.

Programming languages such as Javascript and F# have high-order functions, so I can easily pass functions around. So when assembling my Player entity, I could do something like:

function onPlayerCollision (player) {
  return function (entity) {
    if (entity.tag === 'Zombie') {
      player.getComponent('Health').hp -= 1
    } else if (entity.tag === 'Spikes') {
      player.getComponent('Health').hp -= 5
    }
  }
}

const player = new Entity()
player.addComponent('Health', { hp: 100 })
player.addComponent('BoxCollision', { onCollision: onPlayerCollision(player) } 
// notice I store a reference to a function here, so now the BoxCollision component will execute this passing the entity the player has collided with
function detectCollisions () {
  // for each entity with box collision
    // check if they collide
      onCollision(entity)

onPlayerCollision is a curried/closure function that receives a player, and then returns a new function that wants another Entity.

Are there any flaws with this? Is it okay for components to store references to functions? What are other ways of avoiding game rules in components? Events?

Thanks!

\$\endgroup\$
  • 2
    \$\begingroup\$ There is no "good" or "bad" in software development. Just "fulfills our requirements" and "does not fulfill our requirements". \$\endgroup\$ – Philipp Nov 22 '20 at 16:29
  • 1
    \$\begingroup\$ Does this answer your question? Event handling in Pure Entity Component Systems, is this approach correct? (tl;dr: It's a good idea to have one system for detecting collisions and one or more other systems for reacting to them. The best way to communicate collisions from one to the other is usually through an event queue). \$\endgroup\$ – Philipp Nov 22 '20 at 16:33
  • \$\begingroup\$ Look, pure ECS says you don't store behavior in the components. We can argue if you are doing that or not. Regardless, if you are, that does not mean it is bad. It just means it is not pure ECS. \$\endgroup\$ – Theraot Nov 22 '20 at 19:39
1
\$\begingroup\$

A common pattern in ECS is to add a single-use, disposable tag component after a collision.

So you have a collision detection system. If you detect a collision between say a spikes and a zombie, you add a WalkedOnSpikes component to your zombie, and a ZombieWalkedOnMe component to your spike.

Then you have separate systems that query for any zombies with the tags or and spikes with the tags on them to handle the result. This means your collision system doesn’t “care” what happens. It just registers the collision. Then your specialized systems actually handle the result.

\$\endgroup\$
1
\$\begingroup\$

When you do "ECS by the book", then all logic belongs into systems and all data belongs into components. That means a component should never have a function.

What's a rather common architecture in ECS for collision handling is one system which detects collisions and other systems which then react on the collisions detected by the first system. This could be implemented by adding a Collisions component to your entities which contain a list of objects they collided with this tick. Another common option would be to use an event queue. Your collision detection system fills that queue and your collision response system(s) then consume them.

But another question is if "ECS by the book" is actually the right approach for you. Most of the performance advantages of ECS shine in programming languages like C++ where the developer has a lot of control over when and how memory is allocated. But JavaScript is no such a language. You can really only hope that the JS engine (of whatever browser the player prefers to use) figures out how to represent your data in a way which minimizes cache-misses. So when you feel that adding logic to components makes your code more readable and maintainable, don't let me stop you.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.