Migrate from InstanceID to EntityId
Update code that uses InstanceID-based APIs to the EntityId APIs that Unity uses to identify objects.
Read time 14 minutesLast updated 5 days ago
Unity identifies every object it loads or creates with an object identifier. The struct is the type of that identifier, and it replaces the -based APIs. unifies the way GameObjects and entities identify Unity objects, and removes legacy assumptions about how object identifiers behave.
EntityIdintInstanceIDEntityIdThe -based object identity APIs are obsolete. They still compile and produce deprecation warnings that mention the API. also converts implicitly to and from , so scripts that contain object identifiers in integers keep working without changes.
intEntityIdEntityIdintStoring object identity in an assumes that the value has a meaningful sign, a reliable order, and a stable serialized form. None of that is true. Change the types, not just the API names.
intThis migration affects code that uses object identifiers directly. Common examples include Editor extensions, object lookup code, selection code, custom TreeView implementations, custom serialization, caches, object pools, and packages that store Unity object identifiers in fields.
intThis guide is relevant for Unity object identity APIs. It doesn't apply to shader and GPU instancing identifiers such as or .
unity_InstanceIDSV_InstanceIDFind affected code
Because the obsolete APIs produce warnings rather than errors, and because converts implicitly to and from , a clean compile doesn't mean your project is migrated. Use both the deprecation warnings and search for API to update manually.
EntityIdint-
Update Unity packages, embedded packages, and Asset Store packages.
-
Fix the deprecation warnings that name anreplacement.
EntityId -
Search your project and embedded packages for the following identifiers and patterns:
- ,
GetInstanceID,InstanceID,instanceID.instanceIDs - and other field or property names that contain
objectInstanceId.InstanceID - calls on
GetHashCodeorUnityEngine.Object.EntityId - or
int.Parsenear identifier strings.int.TryParse - on an
ToStringorEntityIdthat is then stored, parsed, or compared.Object - Sorting and sign checks on identifier values, such as ,
OrderBy(obj => obj.GetInstanceID()), orFindObjectsSortMode.InstanceID.id < 0
-
Inspect third-party package code if the package vendor hasn't released a version that uses theAPIs. For more information, refer to Handle third-party packages.
EntityId
Unity's automatic script updater doesn't migrate the main APIs such as for you. Use the deprecation warnings, IDE warnings, and a manual code search to find affected code, then update the surrounding data types manually.
InstanceIDGetInstanceIDReplace InstanceID APIs
Use APIs when the value represents Unity object identity. The following table lists common replacements.
EntityIdOld API or pattern | Replacement | Check when you migrate |
|---|---|---|
| | Change the receiving type from |
| | Pass an |
| | Keep validity checks typed as |
| | Change the full array or list pipeline to |
| | Change both NativeArray and Span call sites to |
| | Change selection storage from |
| | This is an Editor API. Use runtime APIs for runtime code. |
| | Change the identifier that feeds the call, not just the call. |
| | Check serialized data that stored the old integer value. |
| | Search your codebase. The attribute accepts both signatures, so the |
| | Change any mesh ID arrays or pools that feed the call. |
| | Change the field or collection that stores the result. |
| | Change the identifier source, including job data. |
| | Use |
| | For more information, refer to Migrate hierarchy iteration. |
The table isn't exhaustive. Many other Unity APIs follow the same pattern. For example, , , , , , , , , , the profiler frame data APIs, and various render pipeline APIs add -typed members alongside the obsolete -typed ones. Any API that accepts, returns, stores, or compares an object needs the same review.
GlobalObjectIdEditorUtilityInternalEditorUtilityGameObjectSceneManagerDragAndDropLightmappingTerrainContactPointEntityIdintInstanceIDWhen you migrate, distinguish identifier variables from ordinary variables. An that contains a reference must become an . An that stores a temporary local value unrelated to Unity object identity can stay as .
intintintUnityEngine.ObjectEntityIdintintChange identity data structures to EntityId
Don't replace a Unity object identifier with an hash. If the value identifies a Unity object, store the full .
intEntityIdBefore:
Dictionary<int, ObjectState> states = new();int id = target.GetInstanceID();states[id] = state;
After:
Dictionary<EntityId, ObjectState> states = new();EntityId id = target.GetEntityId();states[id] = state;
Use and for identity maps and sets. These collections can use internally while still comparing the full value for equality.
HashSet<EntityId>Dictionary<EntityId, TValue>EntityId.GetHashCodeEntityIdDon't use or as a stored identifier. The hash code is derived from the internal representation of the and isn't part of the API contract. It isn't a stable serialized format, and it isn't a replacement for the old .
EntityId.GetHashCodeObject.GetHashCodeEntityIdintInstanceIDDon't rely on the int conversion
EntityIdintint id = target.GetEntityId();
Treat this only as an intermediate state for code you haven't migrated yet. Change the field, parameter, property, or collection key type:
EntityId id = target.GetEntityId();
Don't derive an identifier from a hash:
int id = target.GetEntityId().GetHashCode();
Some unrelated Unity APIs still use IDs. For example, an IMGUI control ID isn't a Unity object identifier. Don't pass an hash to those APIs unless the API needs only a temporary, non-persistent control ID and your code doesn't depend on unique object identity.
intEntityIdTo check whether an is set to a value other than , use the instance method. To check whether the identified object is currently loaded, use .
EntityIdEntityId.NoneEntityId.IsValidResources.EntityIdIsValidDon't infer meaning from the numeric value
The old value was an implementation detail, but some projects used its numeric value to infer object state. Those inferences were never guaranteed behavior, and they don't apply to .
InstanceIDEntityIdDon't use values to infer:
EntityId- Creation order.
- Scene hierarchy order.
- Load order.
- Runtime-created versus asset-loaded state.
- Prefab instance state.
- Persistence.
In particular, don't check whether an ID is negative. Old code sometimes used to guess whether an object was created at runtime. The sign of an object identifier isn't a supported indicator of anything.
instanceID < 0After Unity destroys an object, it can reuse that object's identifier value for a different object. Because of this reuse, two objects created one after another can have values in any order.
EntityIdFor Editor code that needs to know whether an object is persistent, use the Editor-only API . To perform similar checks in runtime code, use a project-specific data model instead of interpreting the identifier value.
EditorUtility.IsPersistentSort by the property you need
Don't sort by or to recover creation order. ordering is arbitrary. Comparison operators and are useful only when a data structure needs a consistent ordering, such as a sorted collection or binary search.
InstanceIDEntityIdEntityIdCompareToBefore:
var objectsInCreationOrder = objects.OrderBy(obj => obj.GetInstanceID());
After:
var objectsByName = objects.OrderBy(obj => obj.name);
If your code needs creation order, record creation order explicitly:
readonly List<GameObject> m_CreationOrder = new();public void Register(GameObject instance){ m_CreationOrder.Add(instance);}
If your code needs hierarchy order, sort by hierarchy data such as sibling index, transform path, or another domain-specific key.
FindObjects APIs
Object.FindObjectsByTypeFindObjectsSortMode.InstanceIDObject.FindFirstObjectByTypeIdentifier sorting is also slow. The Unity engine team measured that the sort accounted for most of the time spent in .
InstanceIDFindObjectsOfTypePass when order doesn't matter:
FindObjectsSortMode.Nonevar renderers = Object.FindObjectsByType<MeshRenderer>(FindObjectsSortMode.None);
If order matters, sort the result by the property your code actually needs:
var renderers = Object.FindObjectsByType<MeshRenderer>(FindObjectsSortMode.None) .OrderBy(renderer => renderer.transform.GetSiblingIndex()) .ToArray();
Object.FindAnyObjectByTypeUpdate serialization and saved data
Audit any code that stores values in serialized fields, save files, Editor preferences, caches, or custom asset formats. Examples include:
InstanceID- .
[SerializeField] int m_InstanceId - serialized through a custom format.
Dictionary<int, TValue> - String data created from .
instanceID.ToString - Cache files that store object IDs as numbers.
- Public plugin APIs that expose object IDs as .
int
Changing an field to changes the structure of serialized data. Unity can't know that an arbitrary serialized field contained an old . Plan a data migration if the data must survive the upgrade.
intEntityIdintInstanceIDObject identifiers aren't stable across sessions. Unity assigns them when it loads or creates an object, and the same object gets a different identifier the next time you open the project or start the Player. Don't persist an object identifier and expect it to resolve later, in either its or its form.
intEntityIdDon't serialize with and parse the result later. The string format is an implementation detail that can change between Unity versions. is for display and debugging only.
EntityIdToStringToStringFor save games, network protocols, analytics, or similar data, use your own stable identifier. In the Editor, use when you need a persistent reference to an asset or scene object.
GlobalObjectIdUpdate Editor callbacks
For asset-open callbacks, change the callback parameter from to :
[OnOpenAsset]intEntityIdusing UnityEditor;using UnityEditor.Callbacks;using UnityEngine;public static class OpenAssetHandler{ [OnOpenAsset] public static bool OnOpenAsset(EntityId entityId, int line) { Object asset = EditorUtility.EntityIdToObject(entityId); // Custom open behavior. return false; }}
[OnOpenAsset]intEntityIdint[OnOpenAsset]Some user-defined callback methods can't be marked obsolete at the declaration site. Search for old callback signatures and fix analyzer warnings from your IDE or Unity tooling.
Update TreeView code
If your Editor extension uses IMGUI APIs, migrate the identifier type deliberately. The non-generic , , and types are obsolete. The generic versions let you choose the identifier type:
TreeViewTreeViewTreeViewItemTreeViewStateusing UnityEditor.IMGUI.Controls;using UnityEngine;class ObjectTreeView : TreeView<EntityId>{ public ObjectTreeView(TreeViewState<EntityId> state) : base(state) { } protected override TreeViewItem<EntityId> BuildRoot() { return new TreeViewItem<EntityId> { id = EntityId.None, depth = -1, displayName = "Root" }; }}
Use as the only when the tree item represents a Unity object. If the tree item represents another concept, use a stable identifier that belongs to that concept. Existing code that never stored object identity in the tree item ID can move to , , and unchanged.
EntityIdTIdentifierTreeView<int>TreeViewItem<int>TreeViewState<int>Pinning the generic types to with a alias reduces the number of edits in large files. Check for type-name collisions. An alias also makes it harder to see which identifier type each tree uses.
<int>usingMigrate hierarchy iteration
If your Editor code iterates the hierarchy with , migrate to . is obsolete. The class, every method that takes or returns identifiers, and the expanded-set arrays change from to :
HierarchyPropertyHierarchyIteratorHierarchyPropertyintEntityId// Beforevar prop = new HierarchyProperty(HierarchyType.GameObjects);int[] expanded = Array.Empty<int>();while (prop.Next(expanded)){ int id = prop.instanceID; Debug.Log($"Object: {prop.name}, id={id}");}// Aftervar iter = new HierarchyIterator(HierarchyType.GameObjects);EntityId[] expanded = Array.Empty<EntityId>();while (iter.Next(expanded)){ EntityId id = iter.entityId; Debug.Log($"Object: {iter.name}, id={id}");}
If your code stores expanded-state arrays as , change the storage type to .
int[]EntityId[]For custom scene search engines, replace with , update the method signature from to , and call the matching and overloads. If your code reads , switch to .
ISceneSearchEngineISceneSearchEngineV2FilterHierarchyPropertyHierarchyIteratorSceneSearch.RegisterEngineSceneSearch.UnregisterEngineSceneSearchContext.rootPropertySceneSearchContext.rootIteratorHandle third-party packages
A package that still uses the obsolete APIs compiles, but its deprecation warnings can hide warnings in your own code. If a package still uses the obsolete APIs:
InstanceIDInstanceID- Update the package through Package Manager or the Asset Store.
- Check whether the package is embedded in the folder or cached in
Packages.Library/PackageCache - Remove the package if the project doesn't use it.
- Contact the vendor for a version that uses the APIs.
EntityId - Patch an embedded copy if you must keep using the package before the vendor ships an update.
If you maintain code that must support multiple Unity versions, use version guards around the old and new API paths. Choose the version symbol for the first Unity version that contains the replacement API you call. Don't assume one symbol covers every replacement.
EntityIdUpdate automated tests
If your project or package includes automated tests, update tests that depend on ordering or sign.
InstanceIDTests that fail after the migration often rely on old accidental ordering. Don't restore the old behavior by sorting on . Change the test to express the actual requirement.
EntityIdUse order-independent assertions when order isn't part of the contract:
CollectionAssert.AreEquivalent(expectedObjects, actualObjects);
Use explicit ordering when order is part of the contract:
var actualObjects = Object.FindObjectsByType<MyComponent>(FindObjectsSortMode.None) .OrderBy(component => component.name) .ToArray();
Test Editor extensions and package code after the project compiles. Callback migrations, serialized data migrations, and package patches can fail outside the initial warning list.
EntityId binary layout
Ensure that your code doesn't depend on the binary representation of .
EntityIdThe struct doesn't expose its internal fields, and its layout is an implementation detail that can change between Unity versions. Avoid the following:
- Storing state in bits.
EntityId - Performing arithmetic, bitwise operations, or sign checks on values.
EntityId - Comparing raw bytes of structs that contain fields.
EntityId - Depending on padding around fields.
EntityId - Hard-coding an assumption about .
sizeof(EntityId)
Compare struct fields explicitly rather than using raw byte comparisons, and compare values through the API. Use to check whether a value is set to a value other than .
EntityIdEntityIdEntityId.IsValidEntityId.NoneStore values in -typed fields, not in , , , or fields. Aliasing an object identifier with a pointer-sized or integer field makes an assumption about the size of the identifier that isn't part of the API design. The pointer size itself differs between 64-bit platforms such as the Unity Editor and 32-bit runtime platforms.
EntityIdEntityIdIntPtrvoid*nintintMigration checklist
Use the following checklist to review your project:
- Replace API calls with the equivalent
InstanceIDAPI calls.EntityId - Change identity-related fields, parameters, properties, arrays, and collection keys to
int, rather than relying on the implicitEntityIdconversion.int - Replace callbacks that take an
[OnOpenAsset]parameter with theintsignature.EntityId - Update code to use the correct generic identifier type.
TreeView - Migrate code to
HierarchyProperty, including expanded-set arrays.HierarchyIterator - Remove sign checks such as .
id < 0 - Don't perform arithmetic or bitwise operations on values.
EntityId - Stop sorting by or
InstanceIDto recover creation order, and passEntityIdwhen order doesn't matter.FindObjectsSortMode.None - Stop using as an object identifier.
GetHashCode - Stop using plus integer parsing for serialization.
ToString - Audit serialized data and save formats that used .
InstanceID - Audit code that stores identifiers in pointer-sized fields (,
IntPtr,void*,nint).int - Update or patch third-party packages that still use APIs.
InstanceID - Update automated tests that relied on old ordering or sign behavior.