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 14 minutesLast updated 5 days ago

Unity identifies every object it loads or creates with an object identifier. The
EntityId
struct is the type of that identifier, and it replaces the
int
-based
InstanceID
APIs.
EntityId
unifies the way GameObjects and entities identify Unity objects, and removes legacy assumptions about how object identifiers behave.
The
int
-based object identity APIs are obsolete. They still compile and produce deprecation warnings that mention the
EntityId
API.
EntityId
also converts implicitly to and from
int
, so scripts that contain object identifiers in integers keep working without changes.
Storing object identity in an
int
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.
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
.

Find affected code

Because the obsolete APIs produce warnings rather than errors, and because
EntityId
converts implicitly to and from
int
, a clean compile doesn't mean your project is migrated. Use both the deprecation warnings and search for API to update manually.
  1. Update Unity packages, embedded packages, and Asset Store packages.
  2. Fix the deprecation warnings that name an
    EntityId
    replacement.
  3. Search your project and embedded packages for the following identifiers and patterns:
    • GetInstanceID
      ,
      InstanceID
      ,
      instanceID
      ,
      instanceIDs
      .
    • objectInstanceId
      and other field or property names that contain
      InstanceID
      .
    • GetHashCode
      calls on
      UnityEngine.Object
      or
      EntityId
      .
    • int.Parse
      or
      int.TryParse
      near identifier strings.
    • ToString
      on an
      EntityId
      or
      Object
      that is then stored, parsed, or compared.
    • Sorting and sign checks on identifier values, such as
      OrderBy(obj => obj.GetInstanceID())
      ,
      FindObjectsSortMode.InstanceID
      , or
      id < 0
      .
  4. Inspect third-party package code if the package vendor hasn't released a version that uses the
    EntityId
    APIs. For more information, refer to Handle third-party packages.
Unity's automatic script updater doesn't migrate the main
InstanceID
APIs such as
GetInstanceID
for you. Use the deprecation warnings, 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 an integer that you reconstructed by hand.
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.
EditorUtility.IsDirty(int)
,
EditorUtility.GetDirtyCount(int)
EditorUtility.IsDirty(EntityId)
,
EditorUtility.GetDirtyCount(EntityId)
Change the identifier that feeds the call, not just the call.
LazyLoadReference<T>.instanceID
LazyLoadReference<T>.entityId
Check serialized data that stored the old integer value.
[OnOpenAsset]
callback with an
int
parameter
[OnOpenAsset]
callback with an
EntityId
parameter
Search your codebase. The attribute accepts both signatures, so the
int
version compiles without a warning.
Physics.BakeMesh(int, bool)
Physics.BakeMesh(EntityId, bool)
Change any mesh ID arrays or pools that feed the call.
RaycastHit.colliderInstanceID
RaycastHit.colliderEntityId
Change the field or collection that stores the result.
TransformAccessArray.Add(int)
TransformAccessArray.Add(EntityId)
Change the identifier source, including job data.
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
For more information, refer to Migrate hierarchy iteration.
The table isn't exhaustive. Many other Unity APIs follow the same pattern. For example,
GlobalObjectId
,
EditorUtility
,
InternalEditorUtility
,
GameObject
,
SceneManager
,
DragAndDrop
,
Lightmapping
,
Terrain
,
ContactPoint
, the profiler frame data APIs, and various render pipeline APIs add
EntityId
-typed members alongside the obsolete
int
-typed ones. Any API that accepts, returns, stores, or compares an object
InstanceID
needs the same review.
When you migrate, distinguish identifier
int
variables from ordinary
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 the internal representation of the
EntityId
and isn't part of the API contract. It isn't a stable serialized format, and it isn't a replacement for the old
int
InstanceID
.

Don't rely on the int conversion

EntityId
converts implicitly to and from
int
, so the following code still compiles, and Unity does not display a deprecation warning:
int 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
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 set to a value other than
EntityId.None
, use the
EntityId.IsValid
instance method. To check whether the identified object is currently loaded, use
Resources.EntityIdIsValid
.

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. Those inferences were never guaranteed behavior, and they don't apply to
EntityId
.
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. The sign of an object identifier isn't a supported indicator of anything.
After 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
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

Object.FindObjectsByType
can sort its results by object identifier when you pass
FindObjectsSortMode.InstanceID
, and
Object.FindFirstObjectByType
returns the first result in that ordering. Neither ordering reflects creation order, hierarchy order, or load order.
Identifier sorting is also slow. The Unity engine team measured that the
InstanceID
sort accounted for most of the time spent in
FindObjectsOfType
.
Pass
FindObjectsSortMode.None
when order doesn't matter:
var 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.FindAnyObjectByType
is the 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, implement an explicit ordered lookup on a batch result.

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.
Object 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
int
or its
EntityId
form.
Don't serialize
EntityId
with
ToString
and parse the result later. The string format is an implementation detail that can change between Unity versions.
ToString
is for display and debugging only.
For save games, network protocols, analytics, or similar data, use your own stable identifier. In the Editor, use
GlobalObjectId
when you need a persistent reference to an asset or scene object.

Update Editor callbacks

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; }}
[OnOpenAsset]
recognizes both the
int
and the
EntityId
parameter signatures, so the
int
version compiles without a deprecation warning. Search your codebase explicitly, because neither the compiler nor the IDE-side updater flags 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 non-generic
TreeView
,
TreeViewItem
, and
TreeViewState
types are obsolete. The generic versions 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. Existing code that never stored object identity in the tree item ID can move to
TreeView<int>
,
TreeViewItem<int>
, and
TreeViewState<int>
unchanged.
Pinning the generic types to
<int>
with a
using
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.

Migrate hierarchy iteration

If your Editor code iterates the hierarchy with
HierarchyProperty
, migrate to
HierarchyIterator
.
HierarchyProperty
is obsolete. The class, 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
.

Handle third-party packages

A package that still uses the obsolete
InstanceID
APIs compiles, but its deprecation warnings can hide warnings in your own code. If a package still uses the 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 that uses the
    EntityId
    APIs.
  • 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 or sign.
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>(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
EntityId
.
The 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
    EntityId
    bits.
  • Performing arithmetic, bitwise operations, or sign checks on
    EntityId
    values.
  • Comparing raw bytes of structs that contain
    EntityId
    fields.
  • Depending on padding around
    EntityId
    fields.
  • Hard-coding an assumption about
    sizeof(EntityId)
    .
Compare struct fields explicitly rather than using raw byte comparisons, and compare
EntityId
values through the
EntityId
API. Use
EntityId.IsValid
to check whether a value is set to a value other than
EntityId.None
.
Store
EntityId
values in
EntityId
-typed fields, not in
IntPtr
,
void*
,
nint
, or
int
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.

Migration checklist

Use the following checklist to review your project:
  • Replace
    InstanceID
    API calls with the equivalent
    EntityId
    API calls.
  • Change identity-related
    int
    fields, parameters, properties, arrays, and collection keys to
    EntityId
    , rather than relying on the implicit
    int
    conversion.
  • Replace
    [OnOpenAsset]
    callbacks that take an
    int
    parameter with the
    EntityId
    signature.
  • 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, and pass
    FindObjectsSortMode.None
    when order doesn't matter.
  • Stop using
    GetHashCode
    as an object identifier.
  • Stop using
    ToString
    plus integer parsing for serialization.
  • 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
    InstanceID
    APIs.
  • Update automated tests that relied on old ordering or sign behavior.

Additional resources