Unity Stable Reference
A Unity package that provides stable `[SerializeReference]` to objects through source generation, allowing for reliable serialization even after type changes.
com.fullmetalbagel.unity-stable-reference Install via UPM
Add to Unity Package Manager using this URL
https://www.pkglnk.dev/track/unity-stable-reference.git?path=Packages/com.fullmetalbagel.unity-stable-reference README
Unity Stable Reference
A Unity package that provides stable [SerializeReference] support through source generation, ensuring your polymorphic serialized references survive refactoring, namespace changes, and type modifications.
Table of Contents
- Overview
- Requirements
- Installation
- Features
- How It Works
- Usage
- Advanced Examples
- Comparison with Unity's MoveFromAttribute
- Troubleshooting
- API Reference
- Contributing
- License
Overview
Unity's [SerializeReference] attribute enables polymorphic serialization, but it has a critical limitation: serialized references break when you refactor your code. Moving a class to a different namespace, renaming it, or restructuring your type hierarchy can cause Unity to lose track of serialized data, resulting in null references and data loss.
Unity Stable Reference solves this problem by using GUIDs to create permanent, refactoring-safe references. Your serialized data remains intact regardless of how you reorganize your code.
The Problem
// Original code
namespace MyGame.Characters {
public class Warrior : ICharacter { }
}
// After refactoring - Unity loses the reference!
namespace MyGame.Entities.Characters {
public class WarriorClass : ICharacter { }
}
The Solution
With Unity Stable Reference, your references survive any refactoring:
[Guid("8F9CC34B-A30B-48AB-967C-5B3F0CE0793A"), StableWrapperCodeGen]
public class WarriorClass : ICharacter { }
// GUID ensures the reference is maintained
Requirements
- Unity 2022.3 or newer
- .NET Standard 2.0 compatibility
- Roslyn compiler support (included in Unity 2022.3+)
Installation
Option 1: Install via OpenUPM (Recommended)
The package is available on the OpenUPM registry. You can install it via openupm-cli:
openupm add com.fullmetalbagel.unity-stable-reference
Option 2: Install via Git URL
- Open the Package Manager in Unity (Window → Package Manager)
- Click the "+" button in the top-left corner
- Select "Add package from git URL..."
- Enter the following URL:
https://github.com/quabug/unity-stable-reference.git?path=Packages/com.fullmetalbagel.unity-stable-reference
Option 3: Manual Installation
- Clone or download this repository
- Copy the
Packages/com.fullmetalbagel.unity-stable-referencefolder into your project'sPackagesdirectory
Features
- GUID-based Stability: Uses .NET's
[Guid]attribute to maintain references across refactoring - Source Generation: Zero runtime reflection, all wrapper code is generated at compile-time
- Type Safety: Full IntelliSense support and compile-time type checking
- Inspector Support: Custom property drawers for seamless Unity Editor integration
- Minimal Overhead: Thin wrapper pattern with negligible performance impact
- Interface Support: Works with interfaces, abstract classes, and concrete types
- No Dependencies: Self-contained package with no external dependencies
- Roslyn Analyzer: Provides helpful diagnostics and code fixes for missing GUIDs
How It Works
Unity Stable Reference uses Roslyn source generators to create stable wrapper classes for your types. When you mark a type with the [StableWrapperCodeGen] attribute and a [Guid] attribute, the system generates specialized wrapper classes that can be reliably serialized by Unity.
Usage
Quick Start
Import the Generated Stable Wrappers sample:
- Open the Package Manager (Window → Package Manager)
- Find "Unity Stable Reference" in your project packages
- In the package details, expand "Samples"
- Click "Import" next to "Generated Stable Wrappers"
Configure your assembly:
- Navigate to
Assets/Samples/Unity Stable Reference/{version}/__StableWrapper__/ - Select the
__StableWrapper__.asmdeffile - In the Inspector, add your project's assembly as a reference
- Click "Apply"
- Navigate to
Mark your types with attributes:
using System;
using System.Runtime.InteropServices;
using UnityStableReference;
// The Guid ensures your reference survives refactoring
[Guid("8F9CC34B-A30B-48AB-967C-5B3F0CE0793A"), StableWrapperCodeGen]
public class PlayerData : IGameData
{
public string Name { get; set; }
public int Level { get; set; }
}
- Use StableReference in your MonoBehaviours:
using System;
using UnityEngine;
using UnityStableReference;
public class GameManager : MonoBehaviour
{
[SerializeField]
private StableReference<IGameData> _playerData;
void Start()
{
// Create new instance
_playerData = new PlayerData { Name = "Hero", Level = 1 };
// Access the value
if (_playerData.Value != null)
{
Debug.Log($"Player: {_playerData.Value}");
}
}
}
Alternative Setup Methods
Method 1: Assembly-level attribute
Add to any C# file in your assembly (outside of any namespace):
[assembly: UnityStableReference.StableWrapperCodeGen]
Then ensure "Auto Referenced" is enabled on your assembly definition.
Method 2: Manual assembly setup
- Create your own wrapper assembly
- Reference both your game assembly and StableReference.Runtime
- Add the assembly-level attribute as shown above
Advanced Examples
Working with Interfaces
// Define your interface
public interface IWeapon
{
float Damage { get; }
void Attack();
}
// Implement concrete types with GUIDs
[Guid("A1B2C3D4-E5F6-7890-ABCD-EF1234567890"), StableWrapperCodeGen]
public class Sword : IWeapon
{
public float Damage => 10f;
public void Attack() => Debug.Log("Swing!");
}
[Guid("B2C3D4E5-F6A7-8901-BCDE-F12345678901"), StableWrapperCodeGen]
public class Bow : IWeapon
{
public float Damage => 8f;
public void Attack() => Debug.Log("Shoot!");
}
// Use in MonoBehaviour
public class Player : MonoBehaviour
{
[SerializeField] private StableReference<IWeapon> _primaryWeapon;
[SerializeField] private StableReference<IWeapon> _secondaryWeapon;
void Start()
{
_primaryWeapon?.Value?.Attack();
_secondaryWeapon?.Value?.Attack();
}
}
Collections and Arrays
public class Inventory : MonoBehaviour
{
// Arrays of stable references
[SerializeField] private StableReference<IItem>[] _items;
// Lists work too
[SerializeField] private List<StableReference<IItem>> _equipment;
// Nested in other serializable classes
[Serializable]
public class ItemSlot
{
public StableReference<IItem> item;
public int quantity;
}
[SerializeField] private ItemSlot[] _slots;
}
Inheritance Hierarchies
// Base class
[Guid("C3D4E5F6-A7B8-9012-CDEF-234567890123"), StableWrapperCodeGen]
public abstract class Enemy : IEntity
{
public abstract void Attack();
}
// Derived classes with their own GUIDs
[Guid("D4E5F6A7-B890-1234-DEFA-345678901234"), StableWrapperCodeGen]
public class Goblin : Enemy
{
public override void Attack() => Debug.Log("Goblin attacks!");
}
[Guid("E5F6A7B8-9012-3456-EFAB-456789012345"), StableWrapperCodeGen]
public class Dragon : Enemy
{
public override void Attack() => Debug.Log("Dragon breathes fire!");
}
Comparison with Unity's MoveFromAttribute
Unity's built-in [MoveFrom] attribute provides an alternative approach to handle serialization issues. Understanding its advantages and disadvantages can help determine when to use this package instead.
MoveFromAttribute Advantages
- Simple to implement with minimal setup (just add the attribute with a path string)
- Built into Unity with no additional dependencies
- No runtime overhead after deserialization is complete
- Works well for straightforward field renames and moves between parent/child classes
- Handles one-time migrations where the serialized path is known
- Native integration with Unity's serialization system
MoveFromAttribute Disadvantages
- Limited to handling field renames and simple relocations
- Cannot maintain references when changing between interface implementations
- Doesn't work well with polymorphic types or significant class restructuring
- Path strings can be error-prone and hard to maintain
- Not designed for ongoing type evolution scenarios
UnityStableReference is ideal when you need more robust reference stability, especially when significant refactoring is anticipated.
Troubleshooting
Common Issues
"Missing Guid attribute" warning
Problem: You see warnings about missing Guid attributes on types marked with [StableWrapperCodeGen].
Solution: The package includes a Roslyn analyzer that detects this issue. In Visual Studio or Rider:
- Click on the warning
- Use the quick fix action (lightbulb icon)
- Select "Add Guid attribute" to automatically generate a unique GUID
Alternatively, manually add a Guid:
[Guid("YOUR-UNIQUE-GUID-HERE"), StableWrapperCodeGen]
public class YourClass { }
References become null after code changes
Problem: Your serialized references are lost after refactoring.
Possible causes:
- Missing or changed GUID: Ensure the Guid attribute is present and hasn't been modified
- Assembly not referenced: Check that
__StableWrapper__.asmdefreferences your assembly - Generated code not updated: Try reimporting the package or recompiling
Type dropdown shows "None" or is empty
Problem: The type selection dropdown in the Inspector doesn't show your types.
Solutions:
- Ensure your types have both
[Guid]and[StableWrapperCodeGen]attributes - Check that the assembly containing your types is referenced in
__StableWrapper__.asmdef - Verify that Unity has compiled successfully (check Console for errors)
- Try right-clicking the
__StableWrapper__folder and selecting "Reimport"
"Could not find wrapper type" errors
Problem: Runtime errors about missing wrapper types.
Solution:
- Ensure the
__StableWrapper__assembly is included in your build - Check that "Auto Referenced" is enabled on the assembly definition
- Verify the assembly is not being stripped in build settings
Performance Considerations
- First-time generation: Initial code generation may take a few seconds for large codebases
- Runtime overhead: Minimal - only a thin wrapper indirection
- Memory usage: Each wrapped instance has a small memory overhead (typically 16-24 bytes)
Best Practices
- Generate GUIDs once: Never change a GUID after serialized data exists
- Version control: Always commit GUID attributes with your types
- Assembly organization: Keep wrapped types in dedicated assemblies for faster compilation
- Testing: Test serialization after major refactoring to ensure references are maintained
API Reference
Attributes
[StableWrapperCodeGen]
Marks a type for stable wrapper generation. Must be used with [Guid].
[Guid("..."), StableWrapperCodeGen]
public class MyClass { }
[assembly: StableWrapperCodeGen]
Enables wrapper generation for all marked types in an assembly.
[assembly: UnityStableReference.StableWrapperCodeGen]
Types
StableReference<T>
The main wrapper type for creating stable references.
Properties:
Value: Gets or sets the wrapped objectIsValid: Returns true if the reference contains a non-null value
Methods:
- Implicit conversion to/from
T - Supports
==and!=operators
Example:
StableReference<IMyInterface> stableRef = new MyImplementation();
IMyInterface value = stableRef; // Implicit conversion
Contributing
We welcome contributions! Please follow these guidelines:
- Fork the repository and create a feature branch
- Write tests for any new functionality
- Follow the existing code style (use the .editorconfig file)
- Update documentation as needed
- Submit a pull request with a clear description
Development Setup
- Clone the repository
- Open the Unity project for testing Unity integration
- Use the
dotnet/UnityStableReference.slnsolution for analyzer development
Running Tests
cd dotnet
dotnet test
Building the Analyzer
cd dotnet
dotnet build UnityStableReference/UnityStableReference.csproj
dotnet publish UnityStableReference/UnityStableReference.csproj -c Release
The published DLL will be automatically copied to the Unity package.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Installs Over Time
Operating Systems
No data yet
Top Countries
No data yet
Git Versions
No data yet
Embed Install Badge
Add an install count badge to your README
[](https://www.pkglnk.dev/pkg/unity-stable-reference)<a href="https://www.pkglnk.dev/pkg/unity-stable-reference"><img src="https://www.pkglnk.dev/badge/unity-stable-reference.svg?style=pkglnk" alt="pkglnk installs"></a>
No comments yet. Be the first!