Imagine a game world where loads and loads of entities are dynamicly loaded all the time, I would represent that as a list of entities perhaps, but what about removing them?
While when adding i could be pushing back the new entity, i could have the need to remove anywhere in the container. To avoid searching the element to find its position for removal, what choices do i have?
I tought that i could store the entity id as being its position in the container, creating a path for direct removal, but wouldnt that generate some kind of mutual dependency 'disorder' ?
I know the right way would be something like List.RemoveAt(whereToRemove); but what if only the entity knows when it should die?
Or am I just missing something and a list container would know when an object is destructed and reduce its own size?
Answer
Here are your conditions:
Other objects may still be dependant on your removed entity, after it is removed.
You want only the entity to specify it's own removal.
You can't have both. Why? Because code at a higher level than your entity itself (see examples below) decides when that entity needs to be used. Consequently, only code at that same level can determine whether your entity is fit for removal or not.
However, what can happen is that the entity can request it's own removal, by firing off an event which the higher level code is listening for. That higher level then stores this request for removal in a list.
Example 1: without events
You are checking collisions between entities in your world. This is handled higher up, usually in your main game loop, which checks every entity against every other. In this example specifically, when an entity collides with another, only that entity's internal logic can determine how much damage it has taken, and whether or not it has "expired". So lets follow the logic flow for collisions where you have four entities in your world, A, B, C, and D. A is our entity we are concerned with.
We check A for collision with B. There is a collision. A takes damage 50%.
We check A for collision with C. There is a collision. A takes damage 50%. Because damage reaches 0, A determines that it has "died". It removes itself from the list.
We check A for collision with D. There would have been no collision, but you'll never get that far: you get a runtime exception because your entities list has been modified in the midst of a traveral operation.
Example 2: with events
Same setup as before.
We check A for collision with B. There is a collision. A takes damage 50%.
We check A for collision with C. There is a collision. A takes damage 50%. Because damage reaches 0, A determines that it has "died". It fires an event to the entity management code to say, "Remove me ASAP". The entity management code looks at the entity reference sent as part of the event, and stores that reference in a list of entities to be removed.
We check A for collision with D. There is no collision, and the check works just fine.
Now, at the very end of the current game loop iteration, run through the list of entities to be removed, and remove every one of these from your main entities list.
You can see how this avoids the problem altogether. You needn't use events, you can use signals or something else, but the principle is the same -- don't remove entities until you can safely do so. The flipside to this approach, to keep things clean and orderly, is doing the same with entities to add -- make sure you keep references to them, and only add them at the start of the next game loop iteration.
Lastly, don't forget to flush both your to-remove and your to-add lists, each time you use them to perform additions / removals on your main entity list.
PS. Don't be afraid to seek through your main list to do individual removals. It's part and parcel of entity management, and even massive lists tend to be very fast to traverse -- after all, that's what they're engineered for.
No comments:
Post a Comment