Who here makes use of integers as ID systems?

I wanted to ask other programmers about best practices for things like factions and ownership of gameobjects and one of the ideas I’ve recently had believe it or not came from the old Fallout games which is very faction and ownership heavy. I looked at the save editor and it seems that the games despite being such complex RPGs on the surface actually make use of a very simple integer based ID systems where they have the player as one integer then various NPC factions and so on as other integers to indicate who owns what and what the player can do. They just have all sorts of different integers for things like perks and what faction you’re most friendly with and so on.

I’m wondering if this is the best approach though and if anyone has any other suggestions but so far for my purposes at least this is all fine. I’m currently working on a town capture system and having it so when the town is captured it switches to the player and that’s just me switching an ownership integer I have to 0 and then some graphic changes happen and so on.

*** Disclaimer Start ***
Objects don’t really communicate using primitive values, they communicate using references. The serialized data stored in save files is for reliably loading information into the various databases/systems in your game, but once it’s loaded, and the systems establish references to each object instance, you won’t be using the IDs much anymore (at least, in most common game dev scenarios that I’ve come across). I feel this is an important point to make because the format of the ID is probably one of the least important things to worry about, and while there are pros and cons depending on the format you use, in the end you can really just do whatever you want (like using HSV colors for IDs) and get a game working just fine.
*** Disclaimer End ***

Integers are absolutely fine for object IDs in most scenarios, but they have two drawbacks and one benefit over some other common solutions which may or may not apply to a given problem. Let’s talk about the three common approaches as I see them, and the pros and cons of each.

  1. Integers/Shorts. Integers are 32-bit and shorts are 16-bit, which is ~4 billion / ~64 thousand numbers, respectively. At this range, even if you use Random to randomly generate the value, you’re not guaranteed to get a unique ID. 4 billion sounds like a lot, but with a thousand or more objects, a collision is not beyond the realm of possibility, so you must have some system in place for checking to make sure ID values don’t overlap and create bugs down the line. Since you’re going to be checking anyways, some might say using short is better though, since 64 thousand is already enough never have to worry about running out of room in most scenarios, assuming you don’t mind checking against the whole lookup table for duplicates each time you hit GenerateID. There are even scenarios where byte could be better (256 values) if you don’t need more values than that, like say, as the value type for many enums (this has other problems though, as it isn’t as well-supported as I think it should be).

The main benefit is that the smaller the type used, theoretically the faster lookups should be. You’re not getting the guaranteed uniqueness of Guids or the human-readability of strings, but you get performance benefits instead. That said, IDs are overwhelmingly used in serialization, but not really used much in your core code. Once an object obtains a reference to whatever the ID is for, they tend to cache that reference and use it directly, unless there’s some reason they can’t. That being the case, the only real performance benefit you tend to get is in the initial access to obtain the reference (usually immediately after booting the game, starting a new scene, and/or deserializing save data, when performance matters the least), and in the memory allocation size of thousands and thousands of otherwise absolutely tiny objects, if memory is a huge concern. This isn’t really enough to be considered “critical” in any way, at least to me, so going with integers/shorts/bytes for the performance benefits is pretty well into premature optimization territory unless you have a really good reason to do so IMO.

  1. Guids are the industry standard for guaranteed unique IDs (5,316,911,983,139,663,491,615,228,241,121,400,000 combinations). They aren’t serialized by default by Unity, which means using them in a game is a little tricky and usually one has to resort to treating them like strings for good chunks of their lifetime, or write a property drawer for a wrapper (as I did), but this isn’t really a very big deal as long as anything that really cares knows that it can convert them to strings. Just generating the Guid and treating it as a string all of the time is perfectly viable too- though it’s slower than doing the lookups with Guids directly, and string-to-Guid conversions (and vice-versa) everywhere no doubt generates garbage.

The benefit to using Guids is that you can guarantee uniqueness across different development stations without having to cross-check them with eachother. In other words, Developer A can be working on making objects for a system, Developer B can be working on making other objects for the same system, and they can combine their work later without needing to worry about collisions. This is quite valuable in some scenarios, such as when you only want one member of the team actually using Unity, while the others simply use tools you’ve made for generating data to import (this is commonly the case with me). You can have the IDs / data generated outside of Unity without needing any real integrations until the last step of feeding that data in, because collisions simply aren’t going to happen.

The drawbacks are performance cost and the fact that the numbers are even less human-readable than ints/shorts. All systems that generate and use Guids, both in and outside of Unity, will need to be automated or only use the ID under-the-hood while dropdown selections just give the “name” of the object being selected instead. This isn’t that difficult to do, but it’s another step. People shouldn’t be expected to copy and paste Guids around in the inspectors/between spreadsheets, ever, or you’re just asking for problems.

  1. Just using strings. Strings are the slowest option, and they aren’t guaranteed unique in their simplest form, but they have one interesting benefit. That is, you can make a hierarchical structure using just the object ID, and have a single database with categorical divisions without needing to make the categories distinct objects themselves. The usual rule of thumb with this is to use Reverse Domain Name Notation, which is to say, starting with the broadest categorical level, and narrowing it down to the smallest and then object name itself. “Items.Armor.Chest.Gold-Breastplate”, as an example. or “UI.StartScene.NewGameButtonText”- the possibilities are pretty much endless. You can even write a property drawer for this so that you can use drop-downs for already-created sections, like “Items”, “Armor”, “Chest” in the first example, then only have to write the object name itself to avoid the possibility of misspelling or misremembering any particular section and creating bugs.

Of course, strings are also viable as short IDs with just the name, so long as you can check for uniqueness (which you need to be doing for anything other than Guids anyways), and while this is easily the least performant solution, lookups really shouldn’t be happening so often that it matters, or I’d argue you have bigger problems.

That’s just me though, YMMV obviously.

1 Like

Wow thanks for the really detailed response @DonLoquacious , I’ll have a proper look through it. So it looks like what I’m using will work fine for simple stuff then but if I want say a full on database of gameobjects for an RPG with an inventory system etc. then it may be a good idea to make use of the more complicated stuff for later.

I think I remember researching GUIDs ages ago when I was looking at saving and loading. I thought I’d ask because right now I’m getting a mini-ownership system up and running and I’m letting the player pickup gameobjects and drop them again but I need the gameobjects to remember and parent themselves to other gameobjects so that they know which sort of faction gameobject they belong to after the player is done messing with them.

Integers could well work fine for this but I thought I’d ask for other potential options just in case so what I’d probably do is make it so that when you pick up the gameobject it transfers ownership to the player ( In this case 0 ) and then when I drop it back down it transfers ownership to the faction gameobject it originally belonged to.