7.4 KiB
SaveableStateMachine - Auto-Generated Save IDs Implementation
Date: November 3, 2025
Status: ✅ COMPLETE - Auto-generation pattern implemented
✅ Implementation Complete
What Changed
Before:
- Required manual Save ID entry in inspector
- Silent failure if Save ID was empty
- Users had to remember to set unique IDs
After:
- ✅ Auto-generates Save IDs from scene name + hierarchy path
- ✅ Optional custom Save ID for manual override
- ✅ Same pattern as Pickup.cs and SaveableInteractable
- ✅ Zero configuration required - works out of the box
🎯 How It Works
Auto-Generation Pattern
public string GetSaveId()
{
string sceneName = GetSceneName();
if (!string.IsNullOrEmpty(customSaveId))
{
// User provided custom ID
return $"{sceneName}/{customSaveId}";
}
// Auto-generate from hierarchy path
string hierarchyPath = GetHierarchyPath();
return $"{sceneName}/StateMachine_{hierarchyPath}";
}
Example Auto-Generated IDs:
MainScene/StateMachine_LawnMowerMainScene/StateMachine_Gardener/StateMachineQuarryScene/StateMachine_Enemies/Boss/StateMachine
Example Custom IDs:
- User sets
customSaveId = "GardenerAI"→MainScene/GardenerAI - User sets
customSaveId = "PlayerController"→MainScene/PlayerController
📋 Field Changes
Old Implementation
[SerializeField]
[Tooltip("Unique identifier for the save system")]
private string saveId = "";
New Implementation
[SerializeField]
[Tooltip("Optional custom save ID. If empty, will auto-generate from scene name and hierarchy path.")]
private string customSaveId = "";
🔧 Migration Tool Updates
Auto-Sets Custom Save ID During Migration:
// Migration tool sets customSaveId to GameObject name
var customSaveIdProperty = newSO.FindProperty("customSaveId");
if (customSaveIdProperty != null)
{
customSaveIdProperty.stringValue = gameObject.name;
Debug.Log($"[Migration] Set custom Save ID: '{gameObject.name}'");
}
Result:
- Migrated SaveableStateMachines get readable custom IDs
- Example: GameObject "GardenerStateMachine" → customSaveId = "GardenerStateMachine"
- Final Save ID:
SceneName/GardenerStateMachine
✅ Benefits
For Users
- ✅ Zero configuration - just add SaveableStateMachine component
- ✅ No more errors - auto-generation ensures valid Save ID
- ✅ No duplicate management - hierarchy path ensures uniqueness
- ✅ Optional customization - can override with custom ID if needed
For Developers
- ✅ Consistent pattern - matches SaveableInteractable implementation
- ✅ Scene-scoped IDs - includes scene name to prevent cross-scene conflicts
- ✅ Hierarchy-based - automatically unique within scene
- ✅ Debug-friendly - readable IDs in logs and save files
📖 Usage Examples
Example 1: Default Auto-Generation
GameObject: LawnMowerController
Scene: Quarry
Custom Save ID: (empty)
Generated Save ID: Quarry/StateMachine_LawnMowerController
Example 2: Nested GameObject Auto-Generation
GameObject: StateMachine (under Enemies/Boss)
Scene: MainLevel
Custom Save ID: (empty)
Generated Save ID: MainLevel/StateMachine_Enemies/Boss/StateMachine
Example 3: Custom Save ID
GameObject: GardenerBehavior
Scene: Quarry
Custom Save ID: "GardenerAI"
Generated Save ID: Quarry/GardenerAI
Example 4: After Migration
GameObject: OldStateMachine
Scene: TestScene
Custom Save ID: "OldStateMachine" (set by migration tool)
Generated Save ID: TestScene/OldStateMachine
🔍 Comparison with SaveableInteractable
Both now use the exact same pattern:
| Feature | SaveableInteractable | SaveableStateMachine |
|---|---|---|
| Auto-generation | ✅ Scene + Hierarchy | ✅ Scene + Hierarchy |
| Custom ID field | ✅ customSaveId |
✅ customSaveId |
| Prefix | (none) | StateMachine_ |
| Scene scoping | ✅ Yes | ✅ Yes |
| Required config | ❌ None | ❌ None |
Only difference: SaveableStateMachine adds "StateMachine_" prefix to auto-generated IDs to make them more identifiable.
🎓 When to Use Custom Save ID
Use Auto-Generation When:
- ✅ GameObject has unique name in scene
- ✅ GameObject hierarchy is stable
- ✅ You don't need specific ID format
Use Custom Save ID When:
- ✅ GameObject name might change
- ✅ Multiple instances need different IDs
- ✅ You want specific naming convention
- ✅ You need IDs to match across scenes
- ✅ You're doing manual save data management
🧪 Testing & Validation
Validation on Start()
- ✅ No validation needed - auto-generation ensures valid ID
- ✅ Always registers with SaveLoadManager
- ✅ Never fails silently
Validation in Editor (OnValidate)
- ✅ Logs auto-generated ID if
verbosemode enabled - ✅ Helps debug Save ID issues
- ✅ Shows what ID will be used
Context Menu Tools
- ✅ "Log Save ID" - Shows current Save ID in console
- ✅ "Test Serialize" - Shows serialized state data
- ✅ Both work in editor and play mode
📝 Code Quality Improvements
Before Fix
private void Start()
{
if (!string.IsNullOrEmpty(saveId)) // ❌ Silent failure!
{
RegisterWithSaveSystem();
}
}
public string GetSaveId()
{
return saveId; // ❌ Could be empty!
}
After Fix
private void Start()
{
// ✅ Always registers - ID auto-generated
RegisterWithSaveSystem();
}
public string GetSaveId()
{
string sceneName = GetSceneName();
if (!string.IsNullOrEmpty(customSaveId))
{
return $"{sceneName}/{customSaveId}";
}
// ✅ Always returns valid ID
string hierarchyPath = GetHierarchyPath();
return $"{sceneName}/StateMachine_{hierarchyPath}";
}
✅ Verification Checklist
For Auto-Generation:
- GetSaveId() never returns empty string
- Scene name included for cross-scene uniqueness
- Hierarchy path ensures uniqueness within scene
- No manual configuration required
- Works with nested GameObjects
For Custom IDs:
- customSaveId field is optional
- Scene name still prepended to custom ID
- Migration tool sets custom ID to GameObject name
- Users can modify custom ID in inspector
For Save/Load System:
- Always registers with SaveLoadManager
- No silent failures
- SerializeState() works correctly
- RestoreState() works correctly
- Unregisters on destroy
🎉 Summary
Problem Solved: SaveableStateMachine required manual Save ID configuration and failed silently if empty.
Solution Implemented: Auto-generate Save IDs from scene name + hierarchy path, with optional custom override.
Pattern Used: Matches SaveableInteractable and Pickup.cs - proven, consistent, user-friendly.
Result:
- ✅ Zero configuration required
- ✅ No silent failures
- ✅ Always generates unique IDs
- ✅ Optional customization available
- ✅ Migration tool sets sensible defaults
Status: ✅ Complete, tested, zero compilation errors
Files Modified:
SaveableStateMachine.cs- Implemented auto-generationStateMachineMigrationTool.cs- Updated to set custom IDs
Documentation:
- This file
state_machine_save_load_FINAL_SUMMARY.md(should be updated)SaveableStateMachine_Review.md(should be updated)