FreshRSS

Normální zobrazení

Jsou dostupné nové články, klikněte pro obnovení stránky.
PředevčíremHlavní kanál
  • ✇Recent Questions - Game Development Stack Exchange
  • Is it a bad idea to store functions inside components in ECS?kibe
    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
     

Is it a bad idea to store functions inside components in ECS?

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!

How can I efficiently make a delta snapshot that accounts for entities/components that have been added/removed?

In reference to the snapshot and delta compression approach popularized by Quake 3, but with ECS.

Understand the delta should only contain changes — makes sense.

However, if a snapshot delta no longer contains some component x, how does the client know if it’s because there is no state change or because the server removed the component? Same for entities, an entity may just not have any state change over the last few ticks, or, it may have been destroyed on the server, perhaps it was killed but we missed that state update.

Now, I could:

  1. Encode this into the actual diff as proposed in the comments, e.g. component x from previous snapshot no longer exists in latest snapshot. This of course would not be compatible with how my deltas are currently being auto generated by XORing two snapshots.
  2. Include ALL — in view — entity IDs and component types in a given snapshot delta packet even if the corresponding components are zero'd out (no change). Packet size concerns here as we’re no longer sending ‘only changes’ — e.g. if entity ID is 1 byte, component tag is 1 byte; 100 entities each with 5 networked components is already 600 bytes uncompressed even if the component data is unchanged and zeroed out (such as XOR).
  3. Use a separate command system to relay key entity or component events e.g. entity x destroyed/spawned, etc., but that seems like overkill since it could be handled implicitly via state updates.

Example: P2 -- standing in view of P1 -- goes AFK for a few seconds and so the last x snapshots for P1 didn't contain the P2 entity. P1 doesn't know if P2 is dead and so it’s entity needs destroying, or it's actually just doing nothing. The reverse is also true, i.e. P2 could have been killed and the server destroyed that entity, but P1 missed the state update that indicated the death.

A somewhat related note but please feel free to stop reading: Quake 3 as far as I can see here will include all entities in the snapshot including an indicator if the entity is null (destroyed I assume). In the ECS world where we don’t have one struct representing an entity, but instead have many (components), if the same approach were to be followed for an entity’s components it could lead to a bulky delta — as per my original concern. Whilst not related to my question, diff is also much easier with the single struct approach since it would have many fields and you could simply XOR with a previous version and RLE out the zeros — also doesn’t work for components which would defeat the RLE.

❌
❌