Starting in Unity 6.4, Unity replaces the 32-bit integer InstanceID with the 64-bit EntityId struct. EntityId unifies the way GameObjectsThe fundamental object in Unity scenes, which can represent characters, props, scenery, cameras, waypoints, and more. A GameObject’s functionality is defined by the Components attached to it. More info
See in Glossary 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 shaderA program that runs on the GPU. More info
See in Glossary and GPU instancing identifiers such as unity_InstanceID or SV_InstanceID.
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:
Packages directory at the root of a Unity project. This differs from most packages which you download from a package server and are immutable. More infoGetInstanceID, 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.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.
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 meshThe main graphics primitive of Unity. Meshes make up a large part of your 3D worlds. Unity supports triangulated or Quadrangulated polygon meshes. Nurbs, Nurms, Subdiv surfaces must be converted to polygons. More info See in Glossary 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 pipelineA series of operations that take the contents of a Scene, and displays them on a screen. Unity lets you choose from pre-built render pipelines, or write your own. More info
See in Glossary 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.
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.
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.
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:
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.
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.
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.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.
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.instanceID.ToString.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, analyticsAbbreviation of Unity Analytics
See in Glossary, or other durable external data, use your own stable identifier instead of a raw EntityId.
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.
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 collisionsA collision occurs when the physics engine detects that the colliders of two GameObjects make contact or overlap, when at least one has a Rigidbody component and is in motion. More info
See in Glossary. A short alias can hide whether a tree still uses int identifiers or has moved to EntityId identifiers.
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:
// Before
var 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}");
}
// After
var 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.
A package that still uses obsolete InstanceID APIs can prevent the whole project from compiling. If a package still uses obsolete InstanceID APIs:
Packages folder or cached in Library/PackageCache.EntityId.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.
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.
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) |
|---|---|---|
| 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. |
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 WebGLA JavaScript API that renders 2D and 3D graphics in a web browser. The Unity Web build option allows Unity to publish content as JavaScript programs which use HTML5 technologies and the WebGL rendering API to run Unity content in a web browser. More info
See in Glossary 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, 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.
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.
Avoid the following:
EntityId bits.EntityId fields.EntityId fields.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.
Use the following checklist to review your project:
InstanceID API calls with EntityId API calls.int fields, parameters, properties, arrays, and collection keys to EntityId.Selection.instanceIDs, Selection.activeInstanceID, and Selection.Contains with the EntityId equivalents.int IDs with EntityId callback variants, including [OnOpenAsset].TreeView code to use the correct generic identifier type.HierarchyProperty code to HierarchyIterator, including expanded-set arrays.id < 0.EntityId values.InstanceID or EntityId to recover creation order.FindObjectsByType overloads with the no-sort overloads, plus explicit sorting when needed.FindFirstObjectByType and FindObjectOfType with FindAnyObjectByType (or with explicit ordering after a batch lookup).GetHashCode as an object identifier.ToString plus integer parsing for serialization.EntityId.GetRawData with EntityId.ToULong.EntityId.ToULong values as raw data that you don’t interpret, and store them as ulong, not int.SessionState storage of object IDs from the ulong-based APIs to the new SessionState.GetEntityId and SetEntityId family.InstanceID.EntityId doesn’t fit in a pointer-sized field.InstanceID APIs.