This page lists changes in Unity 6.6 (6000.6) in these areas that can affect existing projects when you upgrade from Unity 6.5 (6000.5) to Unity 6.6 (6000.6):
This section outlines recent updates to the camera system that can affect your upgrade experience.
The com.unity.cinemachine package is now a core package and part of the Unity Editor. Any project that depends on this package will automatically upgrade to the version that is part of the Editor.
If your project uses Cinemachine 3, this change doesn’t affect you. If your project uses Cinemachine 2, Unity automatically updates it to use Cinemachine 3.
Cinemachine 3 is a major version change with a different API and data format.
To transition to Cinemachine 3, refer to Upgrade a Project from Cinemachine 2.x. To continue using Cinemachine 2, you must update the manifest of your project to reference a local copy of Cinemachine 2.
This section outlines recent updates to the Unity Editor and its general workflows that can affect your upgrade experience.
Word wrapping is disabled in YAML text files and the Reduce Version Control Noise option has been removed from the Unity Editor settings.
Reduce Version Control Noise was previously enabled by default, which disabled word wrapping of references in YAML files. Because of this, most projects already had word wrapping disabled.
The equivalent API, serializeInlineMappingsOnOneLine setter is now a no-op and the getter always returns true.
The impact of this change is that you might see changes to YAML files that appear unrelated to any changes you made to your assets.
To prevent multiple contributors from experiencing these changes, consider reserializing all your assets. For more information, refer to the ForceReserializeAssets API.
This section outlines recent updates to graphics that can affect your upgrade experience.
Dynamic batching is now obsolete. For information about other methods of optimizing draw calls, refer to Choose a method for optimizing calls.
Removed the dependency on com.unity.modules.terrain package from the com.unity.render-pipelines.core package. If your project relies on Terrain indirectly through the Render Pipeline Core, Universal Render Pipeline, or High Definition Render Pipeline packages, add the com.unity.modules.terrain package to your project through the Package Manager > Built-in tab. Terrain is still included in new projects’ dependencies by default.
The legacy Debug UI infrastructure in the com.unity.render-pipelines.core package is now obsolete. Using these APIs now triggers an [Obsolete(..., true)] attribute, resulting in compilation errors. This change affects DebugState, DebugState<T>, legacy DebugUIDrawer implementations, and their associated types. You must migrate any projects using these features to the current Rendering Debugger pattern.
The following APIs no longer compile:
DebugState, DebugState<T>, and all concrete implementations (such as DebugStateBool, DebugStateInt, DebugStateFloat, DebugStateColor, and DebugStateVector variants).DebugUIDrawer custom drawer implementations.DebugStateAttribute and LegacyStyles.To upgrade your code, follow these steps:
Implement ISerializedDebugDisplaySettings on your debug settings classes.
If your debug settings class previously relied on DebugState for serialization, you must now implement ISerializedDebugDisplaySettings. This interface allows for automatic serialization via the DebugDisplaySerializer.
The following example demonstrates implementing ISerializedDebugDisplaySettings:
[Serializable]
public class MyDebugSettings : IDebugDisplaySettingsData, ISerializedDebugDisplaySettings
{
public bool myFeatureEnabled;
void IDebugDisplaySettingsData.Reset()
{
myFeatureEnabled = false;
}
}
Implement the Create method for custom DebugUI.Widget implementations.
Custom widgets that formerly used DebugUIDrawer for rendering must now implement the abstract Create method to return their VisualElement.
The following example demonstrates a custom DebugUI.Widget implementation with the Create method:
public class MyCustomWidget : DebugUI.Widget
{
protected override VisualElement Create()
{
var container = new VisualElement();
// Use UI Toolkit to build your UI here
return container;
}
}
The Rendering Debugger now handles ToVisualElement calls internally, which in turn executes your defined Create logic.
Surface shader code generation now uses the caching preprocessor. For information about the preprocessor behavior changes, refer to the New shader preprocessor Discussions post.
This section outlines recent updates to optimization that can affect your upgrade experience.
The com.unity.test-framework.performance package is now a core package and part of the Unity Editor. Any project that depends on this package now automatically uses the version that is part of the Editor.
This section outlines recent updates to platform-specific tools and settings that can affect your upgrade experience.
This section outlines recent updates to Android-specific tools and settings.
Removed support for Legacy and Round Android icons. Unity now supports only Adaptive Android icons.
When you upgrade a project that uses Legacy and Round Android icons, Unity displays a warning message and excludes these icons from Android builds.
To update your icon settings, follow these steps:
You can remove any unused Legacy and Round icon assets from your project. The warning clears when you open the Player settings.
The legacy option for the -androidChainedSignalHandlerBehavior command-line argument has been removed from the Android Player runtime. This option was added as a workaround for older Android versions that didn’t handle native crash reporting. This behavior handled native crashes, such as SIGSEGV at JNI boundaries using setjmp and longjmp functions, and wrapped them into Java exceptions. This approach is now obsolete because app stores, such as the Google Play Console, now handle native crashes directly. Furthermore, this legacy behavior prevented third-party crash-reporting SDKs, such as Firebase Crashlytics or Bugsnag, from receiving signals. This caused background worker threads to stop responding upon crashing.
In Unity 6.6, the legacy option has been fully removed. If you explicitly pass legacy as a command-line argument, Unity now does the following:
ChainedSignalHandler: Legacy behavior has been removed. The default chained signal handler behavior will be used instead.Refer to the following based on your project setup:
-androidChainedSignalHandlerBehavior legacy, Unity doesn’t fail to boot and automatically falls back to the default behavior. However, it logs an error in Logcat. Remove this obsolete argument to keep your logs clean.The minimum OpenGL ES version on Android is now ES 3.1, raised from ES 3.0.
When you select OpenGL ES3 in the Android Graphics API list, the following occurs:
<uses-feature android:glEsVersion="0x00030001" />.You cannot revert to ES 3.0.
PlayerSettings.openGLRequireES31 is deprecated. The getter always returns true, the setter is a no-op. Referencing the property raises an obsolete warning, which becomes a build error under warnings-as-errors. The Require ES3.1 property is replaced with Use OpenGL ES 3.0 shaders in Player > Android > Other Settings.
For compatibility reasons, if your project never used Require ES3.1 or similar, the upgraded project automatically enables Use OpenGL ES 3.0 shaders. This gives you the option to upgrade OpenGL ES 3.0 shaders to version 3.1 as needed, preventing visual artifacts that might otherwise occur if Unity forced OpenGL ES 3.1 shaders. For example, OpenGL ES 3.1 shaders raise the MAX_VISIBLE_LIGHTS limit from 16 to 32, which increases the application’s workload. The Use OpenGL ES 3.0 shaders setting provides a transition period so you can adapt to these changes without causing performance regressions in your upgraded project.
To update your code for this change, follow these steps:
PlayerSettings.openGLRequireES31. ES 3.1 is now implied whenever OpenGL ES3 is selected.PlayerSettings.openGLRequireES31AEP or openGLRequireES32, or enable the Require ES3.1+AEP or Require ES3.2 checkboxes in Player > Android > Other Settings.
This section outlines recent updates to the Programming system that can affect your upgrade experience.
The UNITY_64 and DEVELOPMENT_BUILD preprocessor symbols are deprecated, and C# diagnostics now follow the Managed Code Variant setting. These updates involve the following:
Added a new Managed Code Variant setting. This per-platform setting controls which diagnostic defines are emitted into your C# code, independently of the native binary. To access it, go to Edit > Project Settings > Player > Other Settings > Managed Code Variant. For a list of the variants and the defines they emit, refer to Adding diagnostics to C# code.
UNITY_ENABLE_CHECKS replaces the extra safety checks and assertions meaning of DEVELOPMENT_BUILD, and UNITY_INCLUDE_INSTRUMENTATION replaces the profiling and diagnostic logging meaning. This enables profiling without any diagnostics checks.PlayerSettings.GetManagedCodeVariant and PlayerSettings.SetManagedCodeVariant.UNITY_ENABLE_CHECKS and UNITY_INCLUDE_INSTRUMENTATION are always defined. UNITY_ASSERTIONS and ENABLE_PROFILER have historically been defined for development builds, and continue to be defined during this transitional period.Deprecated UNITY_64 and DEVELOPMENT_BUILD scripting symbols. Using either symbol in an #if directive or a [Conditional(...)] attribute now raises a Roslyn analyzer warning (UAC0008 for UNITY_64, UAC0009 for DEVELOPMENT_BUILD) in the Console and in your IDE.
In Unity 6.6, the UNITY_64 and DEVELOPMENT_BUILD defines continue to work. Unity displays deprecation warnings but DEVELOPMENT_BUILD is still emitted for development builds, so your project still works at runtime. However, if your assemblies are compiled with warnings as errors, the warnings become build errors immediately.
In Unity 6.8, these defines will be fully removed. Using them will become a hard compilation error and DEVELOPMENT_BUILD will no longer be emitted by the compilation pipeline. If you migrate now, you can update your codebase incrementally.
If you never used these defines, your project is still impacted by these changes. Many built-in features and packages previously used DEVELOPMENT_BUILD internally and now use the Managed Code Variant setting instead. Unity includes these features based on the option you set for Managed Code Variant, instead of the Development Build setting. The default option is Release, which defines none of the diagnostic symbols, so a Development Build using the default variant no longer contains these diagnostics. The affected features include the following:
UNITY_ENABLE_CHECKS (Debug/Checked). Render Graph profiling samplers, URP’s per-pass ScriptableRenderPass.profilingSampler, and HDRP’s dynamic-resolution overlay are gated by UNITY_INCLUDE_INSTRUMENTATION (Debug/Checked/Instrumented). Build-time stripping of debug shaders and rendering-debugger resources now follows the variant too, so a non-development Checked build keeps them and a Release build strips them. As a side effect, non-development Release builds no longer ship debug-display shader variants they previously included, reducing build size and shader-variant count.UNITY_PHYSICS_DISABLE_INTEGRITY_CHECKS.DISABLE_ENTITIES_JOURNALING.To achieve the diagnostic behavior that a Development Build previously provided, set the Managed Code Variant to Checked (which enables both the check and instrumentation paths) for the platforms where you enable the Development Build checkbox. Use Debug if you also want DEBUG and unoptimized code for stepping through with a debugger.
If you have a custom Scriptable Render Pipeline with build processors that read CoreBuildData.developmentBuild, this property is now [Obsolete] and always returns false. Use CoreBuildData.useDiagnosticChecks instead.
Replace UNITY_64. The bitness of the native binary is not known when scripts are compiled, so replace any compile-time #if UNITY_64 with a runtime check on IntPtr.Size (8 = 64-bit, 4 = 32-bit), or with bitness-independent code.
For example, the following code uses UNITY_64 at compile time:
#if !UNITY_64 && UNITY_ANDROID
return HashWithoutUnalignedLoads(buffer, length);
#else
return HashWithUnalignedLoads(buffer, length);
#endif
Replace it with the following runtime check on IntPtr.Size:
#if UNITY_ANDROID
if (IntPtr.Size == 4)
return HashWithoutUnalignedLoads(buffer, length);
#endif
return HashWithUnalignedLoads(buffer, length);
Replace DEVELOPMENT_BUILD:
If the code was for safety checks, validation, assertions: use #if UNITY_ENABLE_CHECKS (Debug/Checked).
For example, the following check runs only in the Editor or a development build:
#if UNITY_EDITOR || DEVELOPMENT_BUILD
SafetyChecks.CheckAlignmentAndThrow(ptr, nameof(ptr));
#endif
Replace the condition with UNITY_ENABLE_CHECKS:
#if UNITY_ENABLE_CHECKS
SafetyChecks.CheckAlignmentAndThrow(ptr, nameof(ptr));
#endif
Compound gates such as #if UNITY_EDITOR || DEVELOPMENT_BUILD become #if UNITY_EDITOR || UNITY_ENABLE_CHECKS. Because the Editor already defines the new symbols, you can usually drop the redundant UNITY_EDITOR || part.
If the code was for profiler instrumentation, debug names, or diagnostic logging: use #if UNITY_INCLUDE_INSTRUMENTATION (Debug/Checked/Instrumented), or layer it onto a [Conditional] attribute:
For example, the following method is conditional on DEVELOPMENT_BUILD:
[Conditional("DEVELOPMENT_BUILD")]
public static void LogDiagnostic(string message) { ... }
Replace the symbol in the attribute:
[Conditional("UNITY_INCLUDE_INSTRUMENTATION")]
public static void LogDiagnostic(string message) { ... }
A runtime check for a development Player: use the runtime property Debug.isDebugBuild. This is the correct choice when the new variant defines are not equivalent. For example, a non-development Player can ship with the Checked variant, so a #if UNITY_ENABLE_CHECKS block would compile sensitive code, such as logging that might leak authentication tokens, into a production build.
For example, the following log call previously shipped only in the Editor or a development build:
#if UNITY_EDITOR || DEVELOPMENT_BUILD
Debug.LogError($"Rejecting connection: {payload}");
#endif
Replace the compile-time check with the runtime check:
if (Debug.isDebugBuild)
Debug.LogError($"Rejecting connection: {payload}");
Choose the Managed Code Variant for each platform: In Edit > Project Settings > Player > Other Settings > Managed Code Variant, select the variant whose diagnostics you want in your Player builds (Release for shipping; Checked or Debug while developing). From a build script, call PlayerSettings.SetManagedCodeVariant(namedBuildTarget, ManagedCodeVariant.Checked).
Replace BuildOptions.ForceEnableAssertions: This enum value is now [Obsolete]. Use PlayerSettings.SetManagedCodeVariant(namedBuildTarget, ManagedCodeVariant.Checked) instead.
For package and Asset Store authors whose code also targets Unity versions older than 6.6, where the new defines don’t exist, gate on UNITY_6000_6_OR_NEWER and fall back to a runtime Debug.isDebugBuild check. This lets you keep the diagnostic body written once:
#if !UNITY_6000_6_OR_NEWER || UNITY_ENABLE_CHECKS
#if !UNITY_6000_6_OR_NEWER
if (Debug.isDebugBuild)
#endif
{
// diagnostic body, written exactly once
if (library == null)
Debug.LogWarning("No library assigned.");
}
#endif
For [Conditional] methods, select the attribute per Unity version:
#if UNITY_6000_6_OR_NEWER
[Conditional("UNITY_INCLUDE_INSTRUMENTATION")]
#else
[Conditional("DEVELOPMENT_BUILD")]
#endif
static void LogDiagnostic(...) { ... }
For more information, refer to Adding diagnostics to C# code, ManagedCodeVariant, and the Unity scripting symbol reference documentation.
Unity 6.5 and earlier versions didn’t support serializing fields declared as Dictionary<TKey, TValue>. A common alternative solution was a wrapper type that derives from it, or a [Serializable] class that stores parallel key and value lists and rebuilds the dictionary in ISerializationCallbackReceiver.OnAfterDeserialize.
Unity 6.6 serializes a field whose declared type is Dictionary<TKey, TValue> when it has [SerializeField]. A field whose declared type derives from Dictionary<TKey, TValue>, or wraps it in another class, remains a regular serialized class handled by the callback code it already uses. Those solutions keep working without changes, whether the type is one you wrote or one that comes from a package. You can use serialized dictionaries in new code and leave your existing fields as they are.
If you choose to migrate a field to Unity’s dictionary serialization, you must migrate its data yourself. Add the new field alongside the old one, copy the data across from an editor script, and delete the old field only after you have checked the result. If you delete the old field first, its data is lost.
For more information, refer to Dictionary serialization.
This section outlines recent updates to Unity’s UI Toolkit that can affect your upgrade experience.
The DefaultEventSystem.LegacyInputProcessor has been removed. As a result, the obsolete method UIToolkitInputConfiguration.SetRuntimeInputBackend now produces an error in projects that use it. You can safely ignore this error, but remove any remaining calls to that method from your project. You do not need to replace it with anything, as the method no longer has an effect.
In Unity 6.0, UI Toolkit introduced a modern UI element authoring system based on UxmlElement and UxmlAttribute, powered by Unity serialization. This new system replaces the legacy UXML Factory/Traits workflow, which was deprecated and is now removed.
While the legacy system was supported for backward compatibility, maintaining dual workflows has created bugs, confusion, and long-term costs. The new system offers a cleaner, more robust, and consistent UI experience, so we’re moving forward with it exclusively.
For more information and examples, refer to Migrate custom controls from an earlier version to Unity 6, Custom controls documentation and UxmlElementAttribute API reference.