Why do entities need unique IDs?

Started by
5 comments, last by Sik_the_hedgehog 11 years, 1 month ago

In my last project, I put unique id in my entity class, but I ended up never using it.

Why do we need it?

An invisible text.
Advertisement

Often, the unique ID lets you backtrace where something came from. You can tag it into commands being sent into the render or physics systems and use it to track misbehaving values inside that code. You can even display it on screen as a "what am I currently looking at?" marker, which is useful when you can SEE something going haywire but don't know where it came from or what it's linked to.

In terms of "needs", it depends. It's common to use unique IDs to manage message dispatching in message-based systems, for example. Or to separate physical pointers from the entity data, which can also be handy when reloading on the fly.

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

IDs can also act as an indirection layer between your objects. Say you have an enemy who has targeted a player unit. Now say, in the instant before the enemy attacks that player unit is despawned (killed by someone else, maybe, or dismissed by the player). If the enemy held a raw pointer, then if you're not careful you'll end up dereferencing a bad pointer when the enemy attacks its now-nonexistent target. Note that smart pointers can also solve this indirection problem; ie, instead of a raw pointer, you hold a weak_ptr and check it for validity before you attack.

IDs have the additional capability of making it easier to save game state. If you use pointers, whether raw or smart, then when you serialize the game data to disk and back into game state, you have to account for the fact that the pointers that are allocated in the new state won't match up to the pointers that were serialized from the old state. If an enemy had a target, for example, and the game was saved then reloaded, then you need to match up the new pointer of the target, and ensure that the enemy has this new pointer rather than the old one. With IDs, you just serialize the ID value, and ensure that when you reconstruct the game state that you simply reuse the IDs that were serialized with the old state. You don't need to write any special code to crawl the game state and match up pointers.

In such a system you ensure that the only subsystem that ever deals with an actual object pointer is the sub-system designated to manage the entities. If you want to pass a message or otherwise affect an entity, you do so through the framework, handing it an ID. If you do allow other sub-systems to deal directly with entity pointers, then just be sure that they never store a pointer. Storing a pointer is kind of bad news, since it leads you again to the indirection problems. Call the framework with an ID, request a pointer, do something to the entity pointed to, then discard the pointer. This also ensures that your object management sub-system can store and shuffle around objects as it sees fit, without worrying that if it moves an entity it will invalidate a bunch of stored pointers elsewhere in the game state.

As well, some game systems might be structured so that there is no concrete object class. In these entity systems, objects exist as widely disconnected bits of data residing in a number of different vectors, each vector owned by the sub-system that manages it. For example, the visual component of an entity might be a scene node/transformation block plus mesh or animation state in one sub-system, while the physics component might be a collision shape plus rigid body definition block held in the physics sub-system. In this sort of architecture, the ID is the only thing that ties these disconnected bits together. The bits of data are stored in vectors that can be very rapidly iterated during a sub-system update, ensuring strong cache coherence, and yet the ID gives the various bits of data the property of being a thing, and entity. Accessing any chunk of data pertaining to an entity that might be held by a subsystem would be done through use of this ID; again, storing raw pointers to the sub-system data of an object is considered a bad idea, since the sub-system needs to be able to shuffle data around as required.

They're an alternative to using pointers.

I use id's mostly for serializing/deserializing data. it's also nice to have id's for looking up objects, for telling which object is a problem in a debug output. id's are very handy for a variety of reasons, if you can't see their use, then just try writing a system which can save and load your game at any given time.

Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.

They're an alternative to using pointers.

and are also pretty useful if code interacts over a network (say, for client/server gaming), as they are typically much more "general" than pointers are, or sometimes for otherwise synchronizing between code which doesn't directly share data structures.

Don't forget the ability to move objects around in memory (with pointers you can't do this, but if they're referenced by ID then you only need to update where the ID points rather than looking for the pointer about everywhere). Doing this can matter depending on how you allocate memory (especially if you're trying to avoid cache misses).

Don't pay much attention to "the hedgehog" in my nick, it's just because "Sik" was already taken =/ By the way, Sik is pronounced like seek, not like sick.

This topic is closed to new replies.

Advertisement