Documentation

Unity Engine


User Manual

Script Reference

Unity Engine


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
InstanceID
with the 64-bit
EntityId
struct.
EntityId
unifies the way GameObjects and entities identify Unity objects, and removes legacy assumptions about how object identifiers behave.
This 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
int
fields.
This guide is relevant for Unity object identity APIs. It doesn't apply to shader and GPU instancing identifiers such as
unity_InstanceID
or
SV_InstanceID
.

Prepare before you upgrade

Starting in Unity 6.5, obsolete
InstanceID
APIs cause compilation errors. Replace those APIs with the matching
EntityId
APIs.
An
EntityId
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.
Prepare the project before you open it in the target Unity version:
  1. Update Unity packages, embedded packages, and Asset Store packages.
  2. Search your project and embedded packages for the following identifiers and patterns:
    • GetInstanceID
      ,
      InstanceID
      ,
      instanceID
      ,
      instanceIDs
      .
    • FindObjectsSortMode
      ,
      FindObjectsSortMode.InstanceID
      .
    • FindFirstObjectByType
      ,
      FindObjectOfType
      .
    • GetHashCode
      calls on
      UnityEngine.Object
      or
      EntityId
      .
    • objectInstanceId
      ,
      objectReferenceInstanceIDValue
      , and other field or property names that contain
      InstanceID
      .
    • int.Parse
      or
      int.TryParse
      near identifier strings.
    • ToString
      on an
      EntityId
      or
      Object
      that is then stored, parsed, or compared.
  3. Fix the compilation errors.
  4. Inspect third-party package code if the package vendor hasn't released a version compatible with
    EntityId
    . For more information, refer to Handle third-party packages.
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
InstanceID
APIs such as
GetInstanceID
for you. Use compiler errors, IDE warnings, and a manual code search to find affected code, then update the surrounding data types manually.

Replace InstanceID APIs

Use
EntityId
APIs when the value represents Unity object identity. The following table lists common replacements.

Old API or pattern

Replacement

Check when you migrate

Object.GetInstanceID()
Object.GetEntityId()
Change the receiving type from
int
to
EntityId
.
Resources.InstanceIDToObject(int)
Resources.EntityIdToObject(EntityId)
Pass an
EntityId
, not a truncated integer.
Resources.InstanceIDIsValid(int)
Resources.EntityIdIsValid(EntityId)
Keep validity checks typed as
EntityId
.
Resources.InstanceIDToObjectList(NativeArray<int>, List<Object>)
Resources.EntityIdsToObjectList(NativeArray<EntityId>, List<Object>)
Change the full array or list pipeline to
EntityId
.
Resources.InstanceIDsToValidArray(...)
Resources.EntityIdsToValidArray(...)
Change both NativeArray and Span call sites to
EntityId
.
Selection.instanceIDs
,
Selection.activeInstanceID
,
Selection.Contains(int)
Selection.entityIds
,
Selection.activeEntityId
,
Selection.Contains(EntityId)
Change selection storage from
int[]
to
EntityId[]
.
EditorUtility.InstanceIDToObject(int)
EditorUtility.EntityIdToObject(EntityId)
This is an Editor API. Use runtime APIs for runtime code.
SerializedProperty.objectReferenceInstanceIDValue
SerializedProperty.objectReferenceEntityIdValue
Update both reads and writes.
EditorApplication.hierarchyWindowItemOnGUI
EditorApplication.hierarchyWindowItemByEntityIdOnGUI
Change the callback parameter from
int
to
EntityId
.
ProjectWindowCallback.EndNameEditAction
ProjectWindowCallback.AssetCreationEndAction
Update overridden method signatures from
int
to
EntityId
.
[OnOpenAsset]
callback with
int
parameter
[OnOpenAsset]
callback with
EntityId
parameter
Search your codebase explicitly because the IDE-side updater doesn't migrate the
[OnOpenAsset]
signature for you.
LazyLoadReference<T>.instanceID
LazyLoadReference<T>.entityId
Check serialized data that stored the old integer value.
Object.FindObjectsByType(..., FindObjectsSortMode)
Object.FindObjectsByType(Type)
or
Object.FindObjectsByType(Type, FindObjectsInactive)
The
FindObjectsSortMode
enum is also obsolete. Use the no-sort overloads, then sort by a property if you need order.
Object.FindFirstObjectByType
Object.FindAnyObjectByType
FindFirstObjectByType
is obsolete because it relied on InstanceID ordering. Use explicit ordering after a batch lookup if you need a specific result.
Object.FindObjectOfType
Object.FindAnyObjectByType
Same as above.
EntityId.GetRawData()
EntityId.ToULong(target)
GetRawData
was published in early Unity 6.4 builds and is now obsolete. Use the
ToULong
and
FromULong
pair.
SessionState.GetULong
,
SetULong
,
EraseULong
, and the corresponding array APIs (when used to store object IDs)
SessionState.GetEntityId
,
SetEntityId
,
EraseEntityId
, and the corresponding array APIs
The
ulong
-typed APIs were removed. Switch to the new
EntityId
family for object identity storage.
Physics.BakeMesh(int, bool)
Physics.BakeMesh(EntityId, bool)
Change any mesh ID arrays or pools that feed the call.
TreeView
,
TreeViewItem
,
TreeViewState
with implicit
int
IDs
TreeView<TIdentifier>
,
TreeViewItem<TIdentifier>
,
TreeViewState<TIdentifier>
Use
EntityId
only when the tree item ID is a Unity object identity.
HierarchyProperty
HierarchyIterator
and
IHierarchyIterator
For more information, refer to Migrate hierarchy iteration.
The table isn't exhaustive. Many other Unity APIs follow the same pattern. For example,
AssetDatabase
,
GlobalObjectId
,
ObjectChangeEvents
event arguments,
EditorUtility
,
InternalEditorUtility
,
EventMarker
,
Terrain
, and various render pipeline APIs add
EntityId
-typed overloads alongside the obsolete
int
-typed members. Any API that accepts, returns, stores, or compares an object
InstanceID
needs the same review.
When you migrate, distinguish identifier
int
variables from common temporary
int
variables. An
int
that contains a
UnityEngine.Object
reference must become an
EntityId
. An
int
that stores a temporary local value unrelated to Unity object identity can stay as
int
.

Change identity data structures to EntityId

Don't replace a Unity object identifier with an
int
hash. If the value identifies a Unity object, store the full
EntityId
.
Before:
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
HashSet<EntityId>
and
Dictionary<EntityId, TValue>
for identity maps and sets. These collections can use
EntityId.GetHashCode
internally while still comparing the full
EntityId
value for equality.
Don't use
EntityId.GetHashCode
or
Object.GetHashCode
as a stored identifier. The hash code is derived from only part of the
EntityId
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
int
InstanceID
.

Don't convert EntityId to int

There is no general, unique, lossless
EntityId
to
int
conversion. Code that stores identifiers in
int
fields must usually change the field, parameter, property, or collection key type to
EntityId
.
Don't do the following:
int id = (int)target.GetEntityId();int id = target.GetEntityId().GetHashCode();int id = (int)EntityId.ToULong(target.GetEntityId());
Use
EntityId
directly:
EntityId id = target.GetEntityId();
Some unrelated Unity APIs still use
int
IDs. For example, an IMGUI control ID isn't a Unity object identifier. Don't pass an
EntityId
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.
To check whether an
EntityId
is non-default, use the
EntityId.IsValid
instance method. To check whether the identified object is currently loaded, use
Resources.EntityIdIsValid
.
If you encounter
EntityId.GetRawData
in legacy code, replace it with
EntityId.ToULong
.
GetRawData
was published in early Unity 6.4 builds and is now an obsolete-warning API. To convert the value and convert it back, use
EntityId.ToULong
and
EntityId.FromULong
together.

Don't infer meaning from the numeric value

The old
InstanceID
value was an implementation detail, but some projects used its numeric value to infer object state.
EntityId
removes those assumptions.
Don't use
EntityId
values to infer:
  • 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
instanceID < 0
to guess whether an object was created at runtime. That guess was never guaranteed behavior, and it doesn't apply to
EntityId
. All
EntityId
values are positive.
After Unity destroys an object, it can reuse that object's
EntityId
value for a different object. Because of this reuse, two objects created one after another can have
EntityId
values in any order.
For Editor code that needs to know whether an object is persistent, use the Editor-only API
EditorUtility.IsPersistent
. To perform similar checks in runtime code, use a project-specific data model instead of interpreting the identifier value.

Sort by the property you need

Don't sort by
InstanceID
or
EntityId
to recover creation order.
EntityId
ordering is arbitrary. Comparison operators and
CompareTo
are useful only when a data structure needs a consistent ordering, such as a sorted collection or binary search.
Before:
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
InstanceID
ordering during migration. The following APIs are obsolete because they relied on
InstanceID
sort order:
  • Object.FindObjectsByType(..., FindObjectsSortMode)
    and the generic equivalents.
  • Object.FindFirstObjectByType
    and the generic equivalents.
  • The
    FindObjectsSortMode
    enum itself.
InstanceID
sorting was also slow. The Unity engine team measured that the InstanceID sort accounted for around 93% of the time spent in
FindObjectsOfType
, depending on the project.
Use overloads that don't take
FindObjectsSortMode
when order doesn't matter:
var 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();
FindAnyObjectByType
is the only single-result API that doesn't depend on identifier ordering. Use it when any matching object is acceptable. If your code needs a specific result, do an explicit ordered lookup on a batch result rather than relying on
FindFirstObjectByType
returning the first object in a previous
InstanceID
ordering.

Update serialization and saved data

Audit any code that stores
InstanceID
values in serialized fields, save files, Editor preferences, caches, or custom asset formats. Examples include:
  • [SerializeField] int m_InstanceId
    .
  • Dictionary<int, TValue>
    serialized through a custom format.
  • 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
int
field to
EntityId
changes the structure of serialized data. Unity can't know that an arbitrary serialized
int
field contained an old
InstanceID
. Plan a data migration if the data must survive the upgrade.
Don't serialize
EntityId
with
ToString
and parse the result later. The
EntityId
string format has already changed between Unity versions, and Unity reserves the right to change it again.
ToString
is for display and debugging only.
If you must convert the raw
EntityId
value to
ulong
and back, use
EntityId.ToULong
and
EntityId.FromULong
together, and treat the
ulong
as a raw value that you don't read or interpret:
EntityId id = target.GetEntityId();ulong raw = EntityId.ToULong(id);EntityId restored = EntityId.FromULong(raw);
Store the raw value as
ulong
, not
int
. Converting an
EntityId
through
int
silently truncates the high 32 bits, and
EntityId.FromULong
reconstructs a corrupted value with no way to detect the error.
Don't inspect the bits, sort by the raw value, store state in the raw value, convert it to
int
, or build long-lived external formats around the current bit layout. The raw layout is an implementation detail and can change between Unity versions.
For save games, network protocols, analytics, or other durable external data, use your own stable identifier instead of a raw
EntityId
.

Update 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
hierarchyWindowItemOnGUI
with
hierarchyWindowItemByEntityIdOnGUI
:
using 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
EndNameEditAction
with
AssetCreationEndAction
and update the overridden method signatures:
using 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
[OnOpenAsset]
callback parameter from
int
to
EntityId
:
using 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
int
-parameter overload of
[OnOpenAsset]
is removed entirely in later Unity versions. Search your codebase explicitly because the IDE-side updater doesn't migrate the
[OnOpenAsset]
signature for you.
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
TreeView
APIs, migrate the identifier type deliberately. The generic
TreeView
APIs let you choose the identifier type:
using 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
EntityId
as the
TIdentifier
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.
Unity's automatic script updater applies the
using
alias upgrade for the non-generic
TreeView
types, so existing code that uses
TreeView
,
TreeViewItem
, and
TreeViewState
can keep compiling against the generic versions pinned to
<int>
. Use this as a temporary measure while you migrate the underlying identifier type.
using
aliases can reduce the number of edits in large files, but check for type-name collisions. A short alias can hide whether a tree still uses
int
identifiers or has moved to
EntityId
identifiers.

Migrate hierarchy iteration

If your Editor code iterates the hierarchy with
HierarchyProperty
, migrate to
HierarchyIterator
. The class, the
IHierarchyIterator
interface, every method that takes or returns identifiers, and the expanded-set arrays change from
int
to
EntityId
:
// 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
int[]
, change the storage type to
EntityId[]
.
For custom scene search engines, replace
ISceneSearchEngine
with
ISceneSearchEngineV2
, update the
Filter
method signature from
HierarchyProperty
to
HierarchyIterator
, and call the matching
SceneSearch.RegisterEngine
and
SceneSearch.UnregisterEngine
overloads. If your code reads
SceneSearchContext.rootProperty
, switch to
SceneSearchContext.rootIterator
. The related drag-and-drop helpers
InternalEditorUtility.HierarchyWindowDrag
and
InternalEditorUtility.ProjectWindowDrag
are also obsolete; use the
V2
versions, which take a
HierarchyIterator
.

Handle third-party packages

A package that still uses obsolete
InstanceID
APIs can prevent the whole project from compiling. If a package still uses obsolete
InstanceID
APIs:
  • Update the package through Package Manager or the Asset Store.
  • Check whether the package is embedded in the
    Packages
    folder or cached in
    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
EntityId
replacement.

Update automated tests

If your project or package includes automated tests, update tests that depend on
InstanceID
ordering, sign, or integer-size behavior.
Tests that fail after the migration often rely on old accidental ordering. Don't restore the old behavior by sorting on
EntityId
. Change the test to express the actual requirement.
Use 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
EntityId
.
EntityId
is 8 bytes. The struct doesn't expose its internal fields, and the bit layout is an implementation detail that can change between Unity versions.
The following table summarizes the differences between the old
int
InstanceID
and the new
EntityId
:

Feature

InstanceID
(
int
)

EntityId
(struct)

Size4 bytes8 bytes
Sign-bit meaningNegative meant runtime-created, positive meant persistent.No meaning; all values are positive.
Fits in a pointer-sized fieldOn all platforms.On 64-bit platforms only.
Sort orderCoincidentally reflected creation order in some cases.No meaningful order, Unity reuses values for different objects.
Bit layoutImplementation detail.Implementation detail; subject to change between Unity versions.

Don't store identifiers in pointer-sized fields

EntityId
is 8 bytes, so it fits in a pointer-sized field on 64-bit platforms, including the Unity Editor. It doesn't fit in a pointer-sized field on 32-bit runtime platforms such as WebGL or 32-bit Android. Code that aliased the old 4-byte
InstanceID
with a pointer-sized field works by coincidence on 64-bit platforms and fails silently or corrupts data on 32-bit platforms.
Store
EntityId
values in
EntityId
-typed fields, not in
IntPtr
,
void*
,
nint
, or
int
fields.

Don't perform arithmetic on EntityId values

Don't perform arithmetic, bitwise operations, or sign checks on
EntityId
values. The 64-bit raw value can't be distinguished from invalid by inspection alone, and the bit layout is an implementation detail.
Use the
EntityId
API for comparisons. Use
EntityId.IsValid
to check whether a value is non-default.

Truncated EntityIds are detected at runtime

If code passes a truncated
EntityId
value to an API such as
Resources.EntityIdToObject
(for example, a value whose high bits were lost through an
int
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.

Don't compare raw bytes of containing structs

Avoid the following:
  • Storing state in
    EntityId
    bits.
  • Comparing raw bytes of structs that contain
    EntityId
    fields.
  • Depending on padding around
    EntityId
    fields.
  • 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
EntityId
values through the EntityId API.

Migration checklist

Use the following checklist to review your project:
  • Replace
    InstanceID
    API calls with
    EntityId API
    calls.
  • Change identity-bearing
    int
    fields, parameters, properties, arrays, and collection keys to
    EntityId
    .
  • Replace
    Selection.instanceIDs
    ,
    Selection.activeInstanceID
    , and
    Selection.Contains
    with the
    EntityId
    equivalents.
  • Replace editor callbacks that pass
    int
    IDs with EntityId callback variants, including
    [OnOpenAsset]
    .
  • Update
    TreeView
    code to use the correct generic identifier type.
  • Migrate
    HierarchyProperty
    code to
    HierarchyIterator
    , including expanded-set arrays.
  • Remove sign checks such as
    id < 0
    .
  • Don't perform arithmetic or bitwise operations on
    EntityId
    values.
  • Stop sorting by
    InstanceID
    or
    EntityId
    to recover creation order.
  • Replace
    FindObjectsByType
    overloads with the no-sort overloads, plus explicit sorting when needed.
  • Replace
    FindFirstObjectByType
    and
    FindObjectOfType
    with
    FindAnyObjectByType
    (or with explicit ordering after a batch lookup).
  • Stop using
    GetHashCode
    as an object identifier.
  • Stop using
    ToString
    plus integer parsing for serialization.
  • Replace
    EntityId.GetRawData
    with
    EntityId.ToULong
    .
  • Treat
    EntityId.ToULong
    values as raw data that you don't interpret, and store them as
    ulong
    , not
    int
    .
  • Switch
    SessionState
    storage of object IDs from the
    ulong
    -based APIs to the new
    SessionState.GetEntityId
    and
    SetEntityId
    family.
  • 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
    EntityId
    doesn't fit in a pointer-sized field.
  • Update or patch third-party packages that still use
    InstanceID
    APIs.
  • Update automated tests that relied on old ordering, sign, or integer-size behavior.

Additional resources