Whenever you’re making a save system or you’re making cross-scene references, you might come across a need for a unique ID for all your GameObjects. You might think Unity offers a solution for this, but there’s no clean solution offered yet.
Internally, each GameObject does have a UUID, but this value changes every run. This basically makes this value unusable.
Unity provides a git repository called guid-based-reference (https://github.com/Unity-Technologies/guid-based-reference) that provides this feature, but this comes with multiple disadvantages.
- A prefab instance’s Guid shows up as modified compared to the prefab. If you accidentally apply all changes, then this library will die.
- Guids are illegible. Wouldn’t it be better to mention an important GameObject as
- Small memory inefficiencies. There are extra 8 bytes per instance to store a reference to a byte array. The sad part is that this array is only used during Unity’s serialization/deserialization.
Here’s how CloudCanard’s UUID system handles these issues.
First, we have a lightweight serializable UUID struct
Serialization works automatically via
FieldOffsets, which make the serializable part share the same memory location as the
Guid. This only works because the
Guid is also a 16 byte-long struct.
EntityUuid‘s very last bit is used to indicate whether it is
Procedural UUIDs consist of 127 random bits.
Explicit UUIDs spell out a name. The idea is that some GameObjects are too important to have a random bitstring as its ID. CloudCanards has a debug terminal that can target specific GameObjects by their UUID. For some GameObjects like Yanna, typing
Yanna is a lot more intuitive than typing random hex.
The complexity of designing a UUID system comes from being able to duplicate GameObjects in the scene. We want the designers to freely copy-paste prefabs around the scene without breaking the rule that no two GameObject share a UUID. At the same time, we want to ensure that the newly duplicated GameObject gets a new UUID and not the previously existing GameObject.
We do this by detecting the
(1) that gets appended at the end of a GameObject name. For GameObject with procedural UUIDs, their name format is
some name @guid-tostring-value@. If the name is
some name or
some name @guid-tostring-value@ (1), then it is replaced with
some name @some-other-guid@. Note that the GameObject name change is not marked as part of a prefab modification.
Hopefully, you found this helpful! There are some other systems in play here, such as a custom drawer that can edit Explicit UUIDs in prefab mode, but I left them out for brevity.