Migrate from InstanceID to EntityId
Update code that uses InstanceID-based APIs to the EntityId APIs that Unity uses to identify objects.
Read time 17 minutesLast updated 14 days ago
Starting in Unity 6.4, Unity replaces the 32-bit integer with the 64-bit struct. unifies the way GameObjects and entities identify Unity objects, and removes legacy assumptions about how object identifiers behave.
InstanceIDEntityIdEntityIdThis 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_InstanceIDPrepare before you upgrade
Starting in Unity 6.5, obsolete APIs cause compilation errors. Replace those APIs with the matching APIs.
InstanceIDEntityIdAn is a 64-bit value. A 32-bit integer can't represent it. Code that bypasses compiler errors (for example, by suppressing them or by calling the int-based APIs from a precompiled assembly) can silently truncate identifiers at runtime.
EntityIdPrepare the project before you open it in the target Unity version:
-
Update Unity packages, embedded packages, and Asset Store packages.
-
Search your project and embedded packages for the following identifiers and patterns:
- ,
GetInstanceID,InstanceID,instanceID.instanceIDs - ,
FindObjectsSortMode.FindObjectsSortMode.InstanceID - ,
FindFirstObjectByType.FindObjectOfType - calls on
GetHashCodeorUnityEngine.Object.EntityId - ,
objectInstanceId, and other field or property names that containobjectReferenceInstanceIDValue.InstanceID - or
int.Parsenear identifier strings.int.TryParse - on an
ToStringorEntityIdthat is then stored, parsed, or compared.Object
-
Fix the compilation errors.
-
Inspect third-party package code if the package vendor hasn't released a version compatible with. For more information, refer to Handle third-party packages.
EntityId
Consider migrating through intermediate Unity versions so you can fix deprecation warnings before they become errors.
Unity's automatic script updater doesn't migrate the main APIs such as for you. Use compiler errors, 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. |
| | Update both reads and writes. |
| | Change the callback parameter from |
| | Update overridden method signatures from |
| | Search your codebase explicitly because the IDE-side updater doesn't migrate the |
| | Check serialized data that stored the old integer value. |
| | The |
| | |
| | Same as above. |
| | |
| | The |
| | Change any mesh ID arrays or pools that feed the call. |
| | Use |
| | For more information, refer to Migrate hierarchy iteration. |
The table isn't exhaustive. Many other Unity APIs follow the same pattern. For example, , , event arguments, , , , , and various render pipeline APIs add -typed overloads alongside the obsolete -typed members. Any API that accepts, returns, stores, or compares an object needs the same review.
AssetDatabaseGlobalObjectIdObjectChangeEventsEditorUtilityInternalEditorUtilityEventMarkerTerrainEntityIdintInstanceIDWhen you migrate, distinguish identifier variables from common temporary 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 only part of the value, so different objects can share the same hash code. It isn't unique, it isn't a stable serialized format, and it isn't a replacement for the old .
EntityId.GetHashCodeObject.GetHashCodeEntityIdintInstanceIDDon't convert EntityId to int
There is no general, unique, lossless to conversion. Code that stores identifiers in fields must usually change the field, parameter, property, or collection key type to .
EntityIdintintEntityIdDon't do the following:
int id = (int)target.GetEntityId();int id = target.GetEntityId().GetHashCode();int id = (int)EntityId.ToULong(target.GetEntityId());
Use directly:
EntityIdEntityId id = target.GetEntityId();
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 non-default, use the instance method. To check whether the identified object is currently loaded, use .
EntityIdEntityId.IsValidResources.EntityIdIsValidIf you encounter in legacy code, replace it with . was published in early Unity 6.4 builds and is now an obsolete-warning API. To convert the value and convert it back, use and together.
EntityId.GetRawDataEntityId.ToULongGetRawDataEntityId.ToULongEntityId.FromULongDon'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. removes those assumptions.
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. That guess was never guaranteed behavior, and it doesn't apply to . All values are positive.
instanceID < 0EntityIdEntityIdAfter Unity destroys an object, it can reuse that object's value for a different object. Because of this reuse, two objects created one after another can have values in any order.
EntityIdEntityIdFor 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
Don't rely on Unity preserving ordering during migration. The following APIs are obsolete because they relied on sort order:
InstanceIDInstanceID- and the generic equivalents.
Object.FindObjectsByType(..., FindObjectsSortMode) - and the generic equivalents.
Object.FindFirstObjectByType - The enum itself.
FindObjectsSortMode
InstanceIDFindObjectsOfTypeUse overloads that don't take when order doesn't matter:
FindObjectsSortModevar renderers = Object.FindObjectsByType<MeshRenderer>();
If order matters, sort the result by the property your code actually needs:
var renderers = Object.FindObjectsByType<MeshRenderer>() .OrderBy(renderer => renderer.transform.GetSiblingIndex()) .ToArray();
FindAnyObjectByTypeFindFirstObjectByTypeInstanceIDUpdate 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.
intEntityIdintInstanceIDDon't serialize with and parse the result later. The string format has already changed between Unity versions, and Unity reserves the right to change it again. is for display and debugging only.
EntityIdToStringEntityIdToStringIf you must convert the raw value to and back, use and together, and treat the as a raw value that you don't read or interpret:
EntityIdulongEntityId.ToULongEntityId.FromULongulongEntityId id = target.GetEntityId();ulong raw = EntityId.ToULong(id);EntityId restored = EntityId.FromULong(raw);
Store the raw value as , not . Converting an through silently truncates the high 32 bits, and reconstructs a corrupted value with no way to detect the error.
ulongintEntityIdintEntityId.FromULongDon't inspect the bits, sort by the raw value, store state in the raw value, convert it to , or build long-lived external formats around the current bit layout. The raw layout is an implementation detail and can change between Unity versions.
intFor save games, network protocols, analytics, or other durable external data, use your own stable identifier instead of a raw .
EntityIdUpdate Editor callbacks
Editor callback APIs are a common source of migration work because the callback signature changes, not just the API name.
For hierarchy GUI callbacks, replace with :
hierarchyWindowItemOnGUIhierarchyWindowItemByEntityIdOnGUIusing UnityEditor;using UnityEngine;[InitializeOnLoad]public static class CustomHierarchyStyling{ static CustomHierarchyStyling() { EditorApplication.hierarchyWindowItemByEntityIdOnGUI += OnHierarchyGUI; EditorApplication.hierarchyChanged += EditorApplication.RepaintHierarchyWindow; } static void OnHierarchyGUI(EntityId entityId, Rect selectionRect) { GameObject obj = EditorUtility.EntityIdToObject(entityId) as GameObject; if (obj == null || !obj.CompareTag("Special")) return; Rect iconRect = new Rect(selectionRect.x - 20, selectionRect.y, 18, 18); GUI.Label(iconRect, EditorGUIUtility.IconContent("d_Favorite")); }}
For project-window asset creation callbacks, replace with and update the overridden method signatures:
EndNameEditActionAssetCreationEndActionusing UnityEditor.ProjectWindowCallback;using UnityEngine;public class CreateAssetAction : AssetCreationEndAction{ public override void Action(EntityId entityId, string pathName, string resourceFile) { Object asset = UnityEditor.EditorUtility.EntityIdToObject(entityId); Debug.Log($"Created asset: {asset}"); }}
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; }}
The -parameter overload of is removed entirely in later Unity versions. Search your codebase explicitly because the IDE-side updater doesn't migrate the signature for you.
int[OnOpenAsset][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 generic APIs let you choose the identifier type:
TreeViewTreeViewusing 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.
EntityIdTIdentifierUnity's automatic script updater applies the alias upgrade for the non-generic types, so existing code that uses , , and can keep compiling against the generic versions pinned to . Use this as a temporary measure while you migrate the underlying identifier type.
usingTreeViewTreeViewTreeViewItemTreeViewState<int>usingintEntityIdMigrate hierarchy iteration
If your Editor code iterates the hierarchy with , migrate to . The class, the interface, every method that takes or returns identifiers, and the expanded-set arrays change from to :
HierarchyPropertyHierarchyIteratorIHierarchyIteratorintEntityId// 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 . The related drag-and-drop helpers and are also obsolete; use the versions, which take a .
ISceneSearchEngineISceneSearchEngineV2FilterHierarchyPropertyHierarchyIteratorSceneSearch.RegisterEngineSceneSearch.UnregisterEngineSceneSearchContext.rootPropertySceneSearchContext.rootIteratorInternalEditorUtility.HierarchyWindowDragInternalEditorUtility.ProjectWindowDragV2HierarchyIteratorHandle third-party packages
A package that still uses obsolete APIs can prevent the whole project from compiling. If a package still uses 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 compatible with .
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, sign, or integer-size behavior.
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>() .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 compiler error list.
EntityId size and binary layout
Ensure that your code does not depend on the binary representation of .
EntityIdEntityIdThe following table summarizes the differences between the old and the new :
intInstanceIDEntityIdFeature |
|
|
|---|---|---|
| Size | 4 bytes | 8 bytes |
| Sign-bit meaning | Negative meant runtime-created, positive meant persistent. | No meaning; all values are positive. |
| Fits in a pointer-sized field | On all platforms. | On 64-bit platforms only. |
| Sort order | Coincidentally reflected creation order in some cases. | No meaningful order, Unity reuses values for different objects. |
| Bit layout | Implementation detail. | Implementation detail; subject to change between Unity versions. |
Don't store identifiers in pointer-sized fields
EntityIdInstanceIDStore values in -typed fields, not in , , , or fields.
EntityIdEntityIdIntPtrvoid*nintintDon't perform arithmetic on EntityId values
Don't perform arithmetic, bitwise operations, or sign checks on values. The 64-bit raw value can't be distinguished from invalid by inspection alone, and the bit layout is an implementation detail.
EntityIdUse the API for comparisons. Use to check whether a value is non-default.
EntityIdEntityId.IsValidTruncated EntityIds are detected at runtime
If code passes a truncated value to an API such as (for example, a value whose high bits were lost through an cast), Unity detects the truncation at runtime and reports an error rather than silently returning the wrong object. This protects against the most common int-truncation bugs but isn't a substitute for migrating the underlying types.
EntityIdResources.EntityIdToObjectintDon't compare raw bytes of containing structs
Avoid the following:
- Storing state in bits.
EntityId - Comparing raw bytes of structs that contain fields.
EntityId - Depending on padding around fields.
EntityId - Assuming .
sizeof(EntityId) == 4
Containing structs can have different padding or member offsets after the size change from 4 to 8 bytes. Compare struct fields explicitly rather than using raw byte comparisons. Compare values through the EntityId API.
EntityIdMigration checklist
Use the following checklist to review your project:
- Replace API calls with
InstanceIDcalls.EntityId API - Change identity-bearing fields, parameters, properties, arrays, and collection keys to
int.EntityId - Replace ,
Selection.instanceIDs, andSelection.activeInstanceIDwith theSelection.Containsequivalents.EntityId - Replace editor callbacks that pass IDs with EntityId callback variants, including
int.[OnOpenAsset] - 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.EntityId - Replace overloads with the no-sort overloads, plus explicit sorting when needed.
FindObjectsByType - Replace and
FindFirstObjectByTypewithFindObjectOfType(or with explicit ordering after a batch lookup).FindAnyObjectByType - Stop using as an object identifier.
GetHashCode - Stop using plus integer parsing for serialization.
ToString - Replace with
EntityId.GetRawData.EntityId.ToULong - Treat values as raw data that you don't interpret, and store them as
EntityId.ToULong, notulong.int - Switch storage of object IDs from the
SessionState-based APIs to the newulongandSessionState.GetEntityIdfamily.SetEntityId - Audit serialized data and save formats that used .
InstanceID - Audit code that stores identifiers in pointer-sized fields. On 32-bit runtime platforms such as WebGL and 32-bit Android, an 8-byte doesn't fit in a pointer-sized field.
EntityId - Update or patch third-party packages that still use APIs.
InstanceID - Update automated tests that relied on old ordering, sign, or integer-size behavior.