Refactoring
Refactoring a custom ability, item module, or camera type can change the identity saved in a character prefab or scene. Preserve that identity during the rename so configured instances keep their values. This page describes Ultimate Character Controller 3 and the different serialization paths used by its extension types.
Identify what is being renamed
| Custom code or data | Where its identity is stored | What to preserve |
|---|---|---|
Ability, ItemAbility, character Effect, and MovementType |
Managed references on Ultimate Character Locomotion | The old class, namespace, and assembly identity, plus serialized field names. |
ViewType |
Managed references on Camera Controller | The old type identity and the active/default view selections. |
ActionModule, ItemEffect, ImpactAction, and ImpactActionCondition |
Managed references inside their owning module/effect/action groups | The old type identity, field values, order, and configured identifiers. |
A custom CharacterItemAction component or another MonoBehaviour |
A Unity script reference on its GameObject or prefab | The script asset’s .meta file and valid component type. |
| A State System preset | A preset asset containing serialized public property values | Its asset reference and the names and types of the properties it controls. |
A script asset GUID and a managed reference’s type identity solve different problems. Keeping a script’s .meta file does not, by itself, migrate a renamed ability or module. Unity describes managed references in its SerializeReference documentation.
Rename a managed type and its fields
Before changing a class name, namespace, or assembly definition, record the old fully qualified class name and assembly name. Add UnityEngine.Scripting.APIUpdating.MovedFromAttribute to the new declaration as part of the same change. Use the original assembly name, without .dll; for scripts without an assembly definition this is commonly Assembly-CSharp.
For example, suppose your existing Game.Characters.BurstMove ability was in Assembly-CSharp, and its serialized float m_Distance field is becoming m_DashDistance:
using System;
using Opsive.UltimateCharacterController.Character.Abilities;
using UnityEngine;
using UnityEngine.Scripting.APIUpdating;
namespace Game.Abilities
{
[Serializable]
[MovedFrom(false,
sourceNamespace: "Game.Characters",
sourceAssembly: "Assembly-CSharp",
sourceClassName: "BurstMove")]
public class DashAbility : Ability
{
[SerializeField]
[UnityEngine.Serialization.FormerlySerializedAs("m_Distance")]
protected float m_DashDistance = 3f;
public float Distance
{
get => m_DashDistance;
set => m_DashDistance = value;
}
// Keep the existing ability implementation here.
}
}
This illustrates the migration declarations; it does not implement dash movement. MovedFrom(false, ...) describes the old serialized type identity without requesting automatic source-code updates. Update your own using directives, type references, assembly references, and string-based lookups separately. Unity’s MovedFrom implementation distinguishes serialization migration from API updating.
UnityEngine.Serialization.FormerlySerializedAsAttribute belongs on the renamed field and takes its exact old field name. Keep the field’s data type compatible. The attribute is a field-rename mapping, not a conversion from one data representation to another. See Unity’s field-rename example.
Keep migration attributes while assets or branches containing the old names still need to load. Test a prefab and a prefab variant containing non-default values before migrating the remaining assets.
Move component scripts without losing references
A custom item action derived from CharacterItemAction is a component. Its modules are separate managed objects serialized inside it. Renaming the component script therefore requires preserving the script reference as well as any managed types it owns.
Move or rename the script in Unity’s Project window so Unity moves its .meta file with it. When moving files outside Unity, move the matching .meta file too. Keep the component class and script filename consistent, and resolve compilation errors before checking the affected prefabs. Unity documents the GUID and .meta relationship in Asset metadata.
Do not remove and recreate a configured component as a rename procedure. Preserve its existing script reference and verify its serialized fields, Unity events, module groups, and prefab overrides after recompilation.
Preserve State System presets
A State System preset matches saved values to the target’s public properties using both the property name and property type. The example above keeps Distance as a float property even though its backing field changes name. Existing presets that control Distance can continue to address that property.

Renaming Distance to DashDistance, or changing its type, is a separate preset migration. Adding FormerlySerializedAs to the backing field does not map the old preset property to the new one. A preset value whose property no longer matches is skipped during initialization. Record the affected preset values before changing the API, then explicitly rebuild or migrate those presets and verify the states that use them.
There are two similarly named attributes:
| Attribute | Purpose |
|---|---|
UnityEngine.Serialization.FormerlySerializedAsAttribute |
Maps an old serialized field name. |
Opsive.Shared.Utility.FormerlySerializedAs |
Records an old fully qualified class name for Opsive Shared’s type resolver. |
Shared’s TypeUtility also recognizes type aliases supplied by MovedFrom. This helps the Shared serialization and type-name lookup paths that use that resolver. The Opsive class attribute is not a replacement for Unity’s managed-reference migration, and neither type attribute migrates a renamed preset property.
See Presets for creating and assigning State System presets.
Check movement and view selections
The controller also stores type-name strings for the active and first-/third-person movement types. The camera stores equivalent view-type selections. Once the renamed managed instances load successfully, verify Movement Types on Ultimate Character Locomotion and View Types on Camera Controller. Reselect the intended Active row and check the perspective defaults so the asset records the current types.
Search your own code and configuration for the old full name. Restoring a list entry does not update arbitrary strings in custom scripts or data files.
Recover an unknown entry
The controller Inspector can show (Unknown Ability), (Unknown Effect), or (Unknown Movement Type). The camera can show (Unknown View Type). An item action’s Modules list displays a null module as (null).
When Unity 2022.3 cannot resolve a saved managed-reference type, the C# reference is null, but Unity retains the serialized object data. Restoring the missing type can make that data recoverable. This is Unity’s deserialization behavior, separate from the controller’s later list initialization. See HasManagedReferencesWithMissingTypes.
First restore the missing class, required assembly/package, or correct migration metadata. Recompile and reopen a copy of the affected asset. Avoid using removal and addition as the first recovery step: adding a type creates a new configured instance and does not recover the previous instance’s values.
Resolve these entries before entering Play mode. The controller removes null abilities, item abilities, effects, and movement types from its initialized arrays; the camera removes null view types. A module group skips null modules with a warning. These behaviors do not restore the missing configuration and can change which functionality is available.
If the original type cannot be restored, use the recorded values or a version-control copy to configure a deliberate replacement. Compare the resulting asset against that copy before saving over the only recoverable version.
Verify the result
- Open affected scenes, character/item prefabs, and prefab variants after compilation. Check list counts, order, enabled flags, identifiers, object references, and custom values.
- Open each affected preset and exercise its states. Check both activation and return to the default state.
- Verify the active movement/view selections and perspective switching used by your character.
- Exercise the renamed ability or item action through its normal input or scripting path. Check the Console for missing-type and null-module messages.
- Save, close, and reopen representative assets, then review the version-control diff before repeating the migration across the project.
See Programming Concepts for choosing an extension point and keeping custom code outside the controller package.