Working gardener saveable behavior tree
This commit is contained in:
293
docs/SaveableStateMachine_AutoGeneration.md
Normal file
293
docs/SaveableStateMachine_AutoGeneration.md
Normal file
@@ -0,0 +1,293 @@
|
||||
# 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
|
||||
|
||||
```csharp
|
||||
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_LawnMower`
|
||||
- `MainScene/StateMachine_Gardener/StateMachine`
|
||||
- `QuarryScene/StateMachine_Enemies/Boss/StateMachine`
|
||||
|
||||
**Example Custom IDs:**
|
||||
- User sets `customSaveId = "GardenerAI"` → `MainScene/GardenerAI`
|
||||
- User sets `customSaveId = "PlayerController"` → `MainScene/PlayerController`
|
||||
|
||||
---
|
||||
|
||||
## 📋 Field Changes
|
||||
|
||||
### Old Implementation
|
||||
```csharp
|
||||
[SerializeField]
|
||||
[Tooltip("Unique identifier for the save system")]
|
||||
private string saveId = "";
|
||||
```
|
||||
|
||||
### New Implementation
|
||||
```csharp
|
||||
[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:**
|
||||
|
||||
```csharp
|
||||
// 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 `verbose` mode 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
|
||||
```csharp
|
||||
private void Start()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(saveId)) // ❌ Silent failure!
|
||||
{
|
||||
RegisterWithSaveSystem();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetSaveId()
|
||||
{
|
||||
return saveId; // ❌ Could be empty!
|
||||
}
|
||||
```
|
||||
|
||||
### After Fix
|
||||
```csharp
|
||||
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:**
|
||||
- [x] GetSaveId() never returns empty string
|
||||
- [x] Scene name included for cross-scene uniqueness
|
||||
- [x] Hierarchy path ensures uniqueness within scene
|
||||
- [x] No manual configuration required
|
||||
- [x] Works with nested GameObjects
|
||||
|
||||
**For Custom IDs:**
|
||||
- [x] customSaveId field is optional
|
||||
- [x] Scene name still prepended to custom ID
|
||||
- [x] Migration tool sets custom ID to GameObject name
|
||||
- [x] Users can modify custom ID in inspector
|
||||
|
||||
**For Save/Load System:**
|
||||
- [x] Always registers with SaveLoadManager
|
||||
- [x] No silent failures
|
||||
- [x] SerializeState() works correctly
|
||||
- [x] RestoreState() works correctly
|
||||
- [x] 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-generation
|
||||
- `StateMachineMigrationTool.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)
|
||||
|
||||
Reference in New Issue
Block a user