Refactoring lets you rename custom inventory code while keeping the settings already stored in your databases, Item Action Sets, and saves. Add the migration information before Unity reloads the affected data. Changing a C# type, changing a database attribute’s Name, and choosing a different Item Action are separate operations.

Preserve data before renaming code

  1. Keep a recoverable copy of the affected database, action and restriction assets, prefabs, and existing save files.
  2. Record the old namespace and class name. If moving between assemblies, also record the old assembly name.
  3. Add MovedFrom to the renamed type. For a renamed serialized field, add Unity’s FormerlySerializedAs to that field. Keep the field’s data type and meaning unchanged during this step.
  4. Let the scripts compile before opening the affected Inventory Manager panels or Inspectors. Check that every original action, restriction, and attribute is present with its original values before saving the assets.
  5. Load a copy of an older save. For example, confirm that a Health Potion keeps its custom effect values and that an Iron Sword keeps its saved Durability. Save into a separate slot and reload that result before replacing the original save.

An unresolved type can disappear from the editable list. In particular, opening an Item Action Set Inspector initializes and reserializes its action collection. Restore the type or its migration mapping before opening that Inspector; reopening and saving an incomplete list can discard the original entry.

Rename a type with MovedFrom

Put UnityEngine.Scripting.APIUpdating.MovedFrom on the new type and supply its previous namespace and class name explicitly, including whichever one did not change. This makes the old full name available to both the Inventory System editor and runtime deserializer.

For example, this is the new declaration after renaming Game.Inventory.PotionStats to Game.Items.PotionEffect and renaming its Amount field to HealingAmount:

using System;
using UnityEngine.Scripting.APIUpdating;

namespace Game.Items
{
    [Serializable]
    [MovedFrom(false,
        sourceNamespace: "Game.Inventory",
        sourceAssembly: "Assembly-CSharp",
        sourceClassName: "PotionStats")]
    public class PotionEffect
    {
        [UnityEngine.Serialization.FormerlySerializedAs("Amount")]
        public float HealingAmount;
    }
}

Use the actual old assembly name in your project. false leaves API updating out of this example; update your C# callers as part of the refactor. The Inventory System type lookup uses the previous namespace and class name. It does not use the assembly name to distinguish two old types with the same full name.

The same approach applies to serializable custom ItemAction classes, the plain C# restrictions stored in an Item Restriction Set, custom Attribute<T> subclasses and attribute value types, and serializable UI drop actions and conditions. Keep each type’s existing base class, interfaces, and required constructors. Renaming a value type used by Attribute<T> is supported by resolving the generic attribute and its argument separately.

Keep migration attributes while older assets or save files can still contain the old names. A mapping is a fallback: if the old type still exists and can be loaded, that type is found before the renamed replacement. Do not assign the same old full name to several replacements.

Rename serialized fields

Use Unity’s UnityEngine.Serialization.FormerlySerializedAs for a field rename. The Inventory System’s shared field serializer checks the current field name first, then the supplied previous name. This preserves a renamed field; it does not convert an int to a different data model or move a value into a different nested object.

For a second rename or a larger data-layout change, test every older supported save format and write an explicit conversion when needed. The shared field reader retrieves one former field name; do not assume that stacking a history of field attributes provides a general migration chain. See Unity’s field-renaming reference.

Existing Opsive type aliases

Opsive.Shared.Utility.FormerlySerializedAs is a different attribute. It applies to a class and takes its old full type name:

[Opsive.Shared.Utility.FormerlySerializedAs("Game.Inventory.PotionStats")]

This provides an alternative type alias for the shared deserializer, including an old name from the global namespace. It is not the field-renaming attribute. Use fully qualified names if both namespaces are imported. This class attribute accepts one previous name.

Choose replacement only when changing behavior

To replace an action intentionally, select its row in the Item Actions list of an Item Action Set, then choose a different type from Item Action. Confirm Change Item Action Type? only after reviewing the existing settings.

The Item Action Set Inspector shows the selected Drop action, its Item Action type selector, and configured drop settings.

This creates a new action and attempts to copy fields with matching names. It does not use the old serialized record as a type migration. Renamed fields and incompatible field types need manual attention, and the dialog warns that values may be lost. Use MovedFrom when the original action was merely renamed.

The Restrictions list supports adding, removing, and configuring restrictions. It does not provide a missing-type recovery row. Restore an unavailable custom restriction before editing and saving that list.

Keep database names and identities separate

Change What to update
Rename an attribute such as Attack Use its Name field in the Inventory Manager. The rename propagates through its attribute family. Also update code, UI attribute-name fields, expressions, and property bindings that use the old string.
Change an attribute’s value type Use Type only when changing the data model. It replaces attributes across the family. Selected compatible values can be retained, including int/float conversion; conversion to int truncates the fractional part. It is not a replacement for a C# rename mapping.
Move an attribute between Category, Definition, and Item Use the attribute options menu’s Move To choices. This changes where the data belongs and what it inherits. It does not rename a C# type or convert existing player saves.
Rename or reorganize Item Definitions and Categories Keep their original asset identity and database IDs. Item saves resolve their Item Definition by ID; recreating a same-named definition does not restore that link. Update name-based lookups separately.
Rename a custom Item View or View Module component Preserve its Unity script asset and prefab/component references, and migrate its serialized fields. Views and modules are components, not entries in the Item Action serialization list.

The Category, Definition, and Item attribute collections hold the serialized attribute entries. Renaming the attribute’s data name is different from renaming its C# value type. MovedFrom and field rename attributes do not rewrite strings such as an attribute Name or an Attribute Binding’s Property Path.

Preserve save keys as well as save-data types

A custom saver has two identities to consider:

  • Its serialized payload type can use MovedFrom and field rename attributes through the shared serializer.
  • Its FullKey selects the record to load. The default combines the GameObject name, saver component type, and Key. Renaming any of these can make the old record unreachable even when its payload type has a correct migration attribute.

Keep a stable FullKey in a custom saver, or migrate records from the old key to the new key explicitly. Keep Item Definition IDs and Inventory collection order compatible with the old save, or convert them as part of the save migration. The standard Inventory Saver restores collections by index.

There is no universal unknown-type recovery step for save payloads. The shared deserializer returns null when its root type cannot be resolved, and each saver decides what happens next. For example, a non-additive Inventory Saver clears its current collections before returning when its payload cannot be loaded. Disable automatic saving while testing a failed migration so that it cannot overwrite the recoverable save.

Recover missing data

Symptom Check Fix
An action, restriction, or attribute disappeared after recompilation Check the old full type name and whether the replacement compiles. These collections skip unresolved entries instead of displaying a recoverable unknown placeholder. Restore the original asset from the recoverable copy if it was reserialized, add or correct the migration attribute, then reopen it.
A field returned to its default value Check its previous field name, data type, and nesting. Use the field rename attribute for a simple rename; use an explicit conversion for a data-layout change.
A UI drop operation fails after a type rename Check the custom drop actions and conditions in its Item View Slot Drop Action Set. An unresolved nested type can remain null. Restore or map the type before saving or invoking the drop set.
An older save loads no data for a renamed saver Compare the old and new FullKey, not just the payload class name. Preserve the old key or migrate the stored record.
The Inventory loads but a saved Item cannot find its definition Check that the original Item Definition ID is still registered in the selected Database. Restore that identity or convert the saved ID; creating another definition with the same display name is insufficient.