Working gardener saveable behavior tree

This commit is contained in:
Michal Pikulski
2025-11-03 01:34:34 +01:00
parent 14416e141e
commit 3b7bc76757
23 changed files with 2373 additions and 61 deletions

View File

@@ -6,7 +6,8 @@
"GUID:69448af7b92c7f342b298e06a37122aa",
"GUID:9e24947de15b9834991c9d8411ea37cf",
"GUID:70ef9a24f4cfc4aec911c1414e3f90ad",
"GUID:d1e08c06f8f9473888c892637c83c913"
"GUID:d1e08c06f8f9473888c892637c83c913",
"GUID:db4a9769b2b9c5a4788bcd189eea1f0b"
],
"includePlatforms": [
"Editor"

View File

@@ -0,0 +1,575 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using Core.SaveLoad;
using System.Collections.Generic;
using System.Linq;
namespace Editor
{
/// <summary>
/// Editor utility to migrate StateMachine components to SaveableStateMachine.
/// </summary>
public class StateMachineMigrationTool : EditorWindow
{
private Vector2 scrollPosition;
private List<StateMachineInfo> foundStateMachines = new List<StateMachineInfo>();
private bool showPrefabs = true;
private bool showScenes = true;
[MenuItem("Tools/AppleHills/Migrate StateMachines to Saveable")]
public static void ShowWindow()
{
var window = GetWindow<StateMachineMigrationTool>("StateMachine Migration");
window.minSize = new Vector2(600, 400);
window.Show();
}
private void OnGUI()
{
EditorGUILayout.LabelField("StateMachine → SaveableStateMachine Migration", EditorStyles.boldLabel);
EditorGUILayout.Space();
EditorGUILayout.HelpBox(
"This tool will replace all StateMachine components with SaveableStateMachine.\n" +
"All properties and references will be preserved.",
MessageType.Info
);
EditorGUILayout.Space();
// Scan options
EditorGUILayout.LabelField("Scan Options:", EditorStyles.boldLabel);
showPrefabs = EditorGUILayout.Toggle("Include Prefabs", showPrefabs);
showScenes = EditorGUILayout.Toggle("Include Scenes", showScenes);
EditorGUILayout.Space();
if (GUILayout.Button("Scan Project", GUILayout.Height(30)))
{
ScanProject();
}
EditorGUILayout.Space();
// Display results
if (foundStateMachines.Count > 0)
{
EditorGUILayout.LabelField($"Found {foundStateMachines.Count} StateMachine(s):", EditorStyles.boldLabel);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
foreach (var info in foundStateMachines)
{
// Set background color for migrated items
Color originalBgColor = GUI.backgroundColor;
if (info.isAlreadySaveable)
{
GUI.backgroundColor = new Color(0.5f, 1f, 0.5f); // Light green
}
EditorGUILayout.BeginHorizontal("box");
GUI.backgroundColor = originalBgColor; // Reset for content
EditorGUILayout.LabelField(info.name, GUILayout.Width(200));
EditorGUILayout.LabelField(info.path, GUILayout.ExpandWidth(true));
// Ping button (for scene objects or prefabs)
if (GUILayout.Button("Ping", GUILayout.Width(50)))
{
PingObject(info);
}
if (info.isAlreadySaveable)
{
// Green checkmark with bold style
GUIStyle greenStyle = new GUIStyle(GUI.skin.label);
greenStyle.normal.textColor = new Color(0f, 0.6f, 0f);
greenStyle.fontStyle = FontStyle.Bold;
EditorGUILayout.LabelField("✓ Migrated", greenStyle, GUILayout.Width(120));
}
else
{
if (GUILayout.Button("Migrate", GUILayout.Width(80)))
{
if (MigrateSingle(info))
{
// Refresh the list to show updated status
ScanProject();
return;
}
}
}
EditorGUILayout.EndHorizontal();
GUI.backgroundColor = originalBgColor; // Final reset
}
EditorGUILayout.EndScrollView();
EditorGUILayout.Space();
int migrateCount = foundStateMachines.Count(sm => !sm.isAlreadySaveable);
if (migrateCount > 0)
{
if (GUILayout.Button($"Migrate All ({migrateCount})", GUILayout.Height(40)))
{
if (EditorUtility.DisplayDialog(
"Confirm Migration",
$"This will migrate {migrateCount} StateMachine(s) to SaveableStateMachine.\n\nContinue?",
"Yes, Migrate All",
"Cancel"))
{
MigrateAll();
}
}
}
}
else
{
EditorGUILayout.HelpBox("Click 'Scan Project' to find StateMachine components.", MessageType.Info);
}
}
private void ScanProject()
{
foundStateMachines.Clear();
// Find all prefabs
if (showPrefabs)
{
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
foreach (string guid in prefabGuids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
if (prefab != null)
{
// Use GetComponents to find Pixelplacement.StateMachine
var components = prefab.GetComponentsInChildren<Component>(true);
foreach (var component in components)
{
if (component == null) continue;
var componentType = component.GetType();
if (componentType.Name == "StateMachine" && componentType.Namespace == "Pixelplacement")
{
bool isAlreadySaveable = component is SaveableStateMachine;
foundStateMachines.Add(new StateMachineInfo
{
name = component.gameObject.name,
path = path,
isPrefab = true,
isAlreadySaveable = isAlreadySaveable,
assetPath = path
});
}
}
}
}
}
// Find all in scenes
if (showScenes)
{
string[] sceneGuids = AssetDatabase.FindAssets("t:Scene");
foreach (string guid in sceneGuids)
{
string scenePath = AssetDatabase.GUIDToAssetPath(guid);
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
var allComponents = GameObject.FindObjectsOfType<Component>(true);
foreach (var component in allComponents)
{
if (component == null || component.gameObject.scene != scene) continue;
var componentType = component.GetType();
if (componentType.Name == "StateMachine" && componentType.Namespace == "Pixelplacement")
{
bool isAlreadySaveable = component is SaveableStateMachine;
foundStateMachines.Add(new StateMachineInfo
{
name = component.gameObject.name,
path = $"{scenePath} → {component.transform.GetHierarchyPath()}",
isPrefab = false,
isAlreadySaveable = isAlreadySaveable,
assetPath = scenePath,
gameObject = component.gameObject
});
}
}
EditorSceneManager.CloseScene(scene, true);
}
}
Debug.Log($"[StateMachine Migration] Found {foundStateMachines.Count} StateMachine(s)");
}
private void PingObject(StateMachineInfo info)
{
if (info.isPrefab)
{
// Load and ping the prefab asset
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(info.assetPath);
if (prefab != null)
{
EditorGUIUtility.PingObject(prefab);
Selection.activeObject = prefab;
}
}
else
{
// For scene objects, we need to open the scene if it's not already open
var currentScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
if (currentScene.path != info.assetPath)
{
// Scene is not currently open
if (EditorUtility.DisplayDialog(
"Open Scene?",
$"The object is in scene:\n{info.assetPath}\n\nDo you want to open this scene?",
"Yes, Open Scene",
"Cancel"))
{
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
EditorSceneManager.OpenScene(info.assetPath, OpenSceneMode.Single);
// Find the object in the newly opened scene
if (info.gameObject != null)
{
// The gameObject reference might be stale, find by path
GameObject obj = GameObject.Find(info.gameObject.name);
if (obj != null)
{
Selection.activeGameObject = obj;
EditorGUIUtility.PingObject(obj);
}
}
}
}
}
else
{
// Scene is already open
if (info.gameObject != null)
{
Selection.activeGameObject = info.gameObject;
EditorGUIUtility.PingObject(info.gameObject);
// Also scroll to it in hierarchy
EditorApplication.ExecuteMenuItem("Window/General/Hierarchy");
}
}
}
}
private void MigrateAll()
{
int migratedCount = 0;
int errorCount = 0;
foreach (var info in foundStateMachines)
{
if (!info.isAlreadySaveable)
{
if (MigrateSingle(info))
{
migratedCount++;
}
else
{
errorCount++;
}
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.DisplayDialog(
"Migration Complete",
$"Migrated: {migratedCount}\nErrors: {errorCount}",
"OK"
);
// Refresh list to show green highlighting for newly migrated items
ScanProject();
}
private bool MigrateSingle(StateMachineInfo info)
{
try
{
if (info.isPrefab)
{
return MigratePrefab(info.assetPath);
}
else
{
return MigrateSceneObject(info.assetPath, info.gameObject);
}
}
catch (System.Exception ex)
{
Debug.LogError($"[StateMachine Migration] Error migrating '{info.name}': {ex.Message}");
return false;
}
}
private bool MigratePrefab(string prefabPath)
{
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
try
{
var components = prefabRoot.GetComponentsInChildren<Component>(true);
int migratedInPrefab = 0;
foreach (var component in components)
{
if (component == null) continue;
var componentType = component.GetType();
if (componentType.Name == "StateMachine" &&
componentType.Namespace == "Pixelplacement" &&
!(component is SaveableStateMachine))
{
if (MigrateComponent(component.gameObject, component))
{
migratedInPrefab++;
}
}
}
PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
Debug.Log($"[StateMachine Migration] Migrated {migratedInPrefab} component(s) in prefab: {prefabPath}");
return true;
}
finally
{
PrefabUtility.UnloadPrefabContents(prefabRoot);
}
}
private bool MigrateSceneObject(string scenePath, GameObject gameObject)
{
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
try
{
var components = gameObject.GetComponents<Component>();
foreach (var component in components)
{
if (component == null) continue;
var componentType = component.GetType();
if (componentType.Name == "StateMachine" &&
componentType.Namespace == "Pixelplacement" &&
!(component is SaveableStateMachine))
{
bool success = MigrateComponent(gameObject, component);
if (success)
{
EditorSceneManager.MarkSceneDirty(scene);
EditorSceneManager.SaveScene(scene);
Debug.Log($"[StateMachine Migration] Migrated component in scene: {scenePath}");
}
return success;
}
}
return false;
}
finally
{
EditorSceneManager.CloseScene(scene, false);
}
}
private bool MigrateComponent(GameObject gameObject, Component oldComponent)
{
// Capture old component data using SerializedObject
SerializedObject oldSO = new SerializedObject(oldComponent);
var defaultState = oldSO.FindProperty("defaultState");
var verbose = oldSO.FindProperty("verbose");
var allowReentry = oldSO.FindProperty("allowReentry");
var returnToDefaultOnDisable = oldSO.FindProperty("returnToDefaultOnDisable");
var onStateExited = oldSO.FindProperty("OnStateExited");
var onStateEntered = oldSO.FindProperty("OnStateEntered");
var onFirstStateEntered = oldSO.FindProperty("OnFirstStateEntered");
var onFirstStateExited = oldSO.FindProperty("OnFirstStateExited");
var onLastStateEntered = oldSO.FindProperty("OnLastStateEntered");
var onLastStateExited = oldSO.FindProperty("OnLastStateExited");
// Remove old component
Object.DestroyImmediate(oldComponent);
// Add new component
var newSM = gameObject.AddComponent<SaveableStateMachine>();
// Restore data using SerializedObject
SerializedObject newSO = new SerializedObject(newSM);
CopySerializedProperty(defaultState, newSO.FindProperty("defaultState"));
CopySerializedProperty(verbose, newSO.FindProperty("verbose"));
CopySerializedProperty(allowReentry, newSO.FindProperty("allowReentry"));
CopySerializedProperty(returnToDefaultOnDisable, newSO.FindProperty("returnToDefaultOnDisable"));
CopySerializedProperty(onStateExited, newSO.FindProperty("OnStateExited"));
CopySerializedProperty(onStateEntered, newSO.FindProperty("OnStateEntered"));
CopySerializedProperty(onFirstStateEntered, newSO.FindProperty("OnFirstStateEntered"));
CopySerializedProperty(onFirstStateExited, newSO.FindProperty("OnFirstStateExited"));
CopySerializedProperty(onLastStateEntered, newSO.FindProperty("OnLastStateEntered"));
CopySerializedProperty(onLastStateExited, newSO.FindProperty("OnLastStateExited"));
// Set a custom Save ID based on GameObject name (simple and readable)
// Users can customize this in the inspector if needed
var customSaveIdProperty = newSO.FindProperty("customSaveId");
if (customSaveIdProperty != null)
{
// Use GameObject name as the custom ID (scene name will be added automatically by GetSaveId)
customSaveIdProperty.stringValue = gameObject.name;
Debug.Log($"[Migration] Set custom Save ID: '{customSaveIdProperty.stringValue}' for '{gameObject.name}'");
}
newSO.ApplyModifiedProperties();
EditorUtility.SetDirty(gameObject);
return true;
}
private void CopySerializedProperty(SerializedProperty source, SerializedProperty dest)
{
if (source == null || dest == null) return;
switch (source.propertyType)
{
case SerializedPropertyType.Integer:
dest.intValue = source.intValue;
break;
case SerializedPropertyType.Boolean:
dest.boolValue = source.boolValue;
break;
case SerializedPropertyType.Float:
dest.floatValue = source.floatValue;
break;
case SerializedPropertyType.String:
dest.stringValue = source.stringValue;
break;
case SerializedPropertyType.Color:
dest.colorValue = source.colorValue;
break;
case SerializedPropertyType.ObjectReference:
dest.objectReferenceValue = source.objectReferenceValue;
break;
case SerializedPropertyType.LayerMask:
dest.intValue = source.intValue;
break;
case SerializedPropertyType.Enum:
dest.enumValueIndex = source.enumValueIndex;
break;
case SerializedPropertyType.Vector2:
dest.vector2Value = source.vector2Value;
break;
case SerializedPropertyType.Vector3:
dest.vector3Value = source.vector3Value;
break;
case SerializedPropertyType.Vector4:
dest.vector4Value = source.vector4Value;
break;
case SerializedPropertyType.Rect:
dest.rectValue = source.rectValue;
break;
case SerializedPropertyType.ArraySize:
dest.arraySize = source.arraySize;
break;
case SerializedPropertyType.Character:
dest.intValue = source.intValue;
break;
case SerializedPropertyType.AnimationCurve:
dest.animationCurveValue = source.animationCurveValue;
break;
case SerializedPropertyType.Bounds:
dest.boundsValue = source.boundsValue;
break;
case SerializedPropertyType.Quaternion:
dest.quaternionValue = source.quaternionValue;
break;
case SerializedPropertyType.Generic:
// Handle UnityEvent and other generic types by copying children
CopyGenericProperty(source, dest);
break;
}
}
private void CopyGenericProperty(SerializedProperty source, SerializedProperty dest)
{
// For arrays and lists
if (source.isArray)
{
dest.arraySize = source.arraySize;
for (int i = 0; i < source.arraySize; i++)
{
CopySerializedProperty(source.GetArrayElementAtIndex(i), dest.GetArrayElementAtIndex(i));
}
}
else
{
// For complex objects, copy all children
SerializedProperty sourceIterator = source.Copy();
SerializedProperty destIterator = dest.Copy();
bool enterChildren = true;
while (sourceIterator.Next(enterChildren))
{
// Only process immediate children
if (!sourceIterator.propertyPath.StartsWith(source.propertyPath + "."))
break;
enterChildren = false;
// Find corresponding property in destination
string relativePath = sourceIterator.propertyPath.Substring(source.propertyPath.Length + 1);
SerializedProperty destChild = dest.FindPropertyRelative(relativePath);
if (destChild != null)
{
CopySerializedProperty(sourceIterator, destChild);
}
}
}
}
private class StateMachineInfo
{
public string name;
public string path;
public bool isPrefab;
public bool isAlreadySaveable;
public string assetPath;
public GameObject gameObject;
}
}
public static class TransformExtensions
{
public static string GetHierarchyPath(this Transform transform)
{
string path = transform.name;
while (transform.parent != null)
{
transform = transform.parent;
path = transform.name + "/" + path;
}
return path;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 13897a2afcff4d21a3eb20fe5092bf7a
timeCreated: 1762121174

View File

@@ -19,7 +19,7 @@ using UnityEngine.Events;
namespace Pixelplacement
{
[RequireComponent (typeof (Initialization))]
public class StateMachine : MonoBehaviour
public class StateMachine : MonoBehaviour
{
//Public Variables:
public GameObject defaultState;

View File

@@ -52,13 +52,14 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: abefccb95d18f534f81d5158b8fc721f, type: 3}
m_Name:
m_EditorClassIdentifier:
ChaseSpline: {fileID: 8996538767324381723}
GardenerObject: {fileID: 8361739881193827101}
chaseSpline: {fileID: 0}
runningGardenerTransform: {fileID: 0}
chaseDuration: 2
chaseDelay: 0
animator: {fileID: 3886598470756828970}
lawnMowerRef: {fileID: 0}
audioController: {fileID: 6510906053583315767}
lawnmowerAnchor: {fileID: 0}
--- !u!114 &8996538767324381723
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -328,6 +329,31 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 4ad080e6ca3114e4e96ccc33655d3dff, type: 3}
m_Name:
m_EditorClassIdentifier:
defaultState: {fileID: 0}
currentState: {fileID: 0}
_unityEventsFolded: 0
verbose: 0
allowReentry: 0
returnToDefaultOnDisable: 1
OnStateExited:
m_PersistentCalls:
m_Calls: []
OnStateEntered:
m_PersistentCalls:
m_Calls: []
OnFirstStateEntered:
m_PersistentCalls:
m_Calls: []
OnFirstStateExited:
m_PersistentCalls:
m_Calls: []
OnLastStateEntered:
m_PersistentCalls:
m_Calls: []
OnLastStateExited:
m_PersistentCalls:
m_Calls: []
customSaveId:
--- !u!1 &1388078248852387932
GameObject:
m_ObjectHideFlags: 0

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 416226e4d22a03e48ba954e140a9ce8c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -93,7 +93,7 @@ LightmapSettings:
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
m_LightingDataAsset: {fileID: 112000000, guid: 70edf6dc95775d84fb06ae92d32fbc4c, type: 2}
m_LightingSettings: {fileID: 0}
--- !u!196 &4
NavMeshSettings:
@@ -430628,6 +430628,11 @@ Transform:
m_CorrespondingSourceObject: {fileID: 5145306031820616614, guid: fbbe1f4baf226904b96f839fe0c00181, type: 3}
m_PrefabInstance: {fileID: 227102088}
m_PrefabAsset: {fileID: 0}
--- !u!4 &230875883 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 8361739881193827101, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
m_PrefabInstance: {fileID: 1101333109}
m_PrefabAsset: {fileID: 0}
--- !u!1 &232490955
GameObject:
m_ObjectHideFlags: 0
@@ -434811,6 +434816,37 @@ Transform:
m_CorrespondingSourceObject: {fileID: 5145306031820616614, guid: fbbe1f4baf226904b96f839fe0c00181, type: 3}
m_PrefabInstance: {fileID: 374459781}
m_PrefabAsset: {fileID: 0}
--- !u!1 &378802579
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 378802580}
m_Layer: 0
m_Name: MowerGardernerAnchor
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &378802580
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 378802579}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 17.07, y: 13.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1481757352}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &381929909
GameObject:
m_ObjectHideFlags: 0
@@ -442508,6 +442544,17 @@ MonoBehaviour:
audioSource: {fileID: 0}
clipPriority: 0
sourcePriority: 0
--- !u!114 &729515061 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 8996538767324381723, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
m_PrefabInstance: {fileID: 1101333109}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f1ec11ed173ba4d8d99e75c4bf174d82, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!4 &733706664 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 6078012632802010276, guid: 3346526f3046f424196615241a307104, type: 3}
@@ -445774,6 +445821,50 @@ SpriteRenderer:
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &917099765 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 780600094299918916, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
m_PrefabInstance: {fileID: 1101333109}
m_PrefabAsset: {fileID: 0}
--- !u!114 &917099774
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 917099765}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 833a4ccef651449e973e623d9107bef5, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::Interactions.OneClickInteraction
isOneTime: 0
cooldown: -1
characterToInteract: 0
interactionStarted:
m_PersistentCalls:
m_Calls: []
interactionInterrupted:
m_PersistentCalls:
m_Calls: []
characterArrived:
m_PersistentCalls:
m_Calls: []
interactionComplete:
m_PersistentCalls:
m_Calls: []
--- !u!114 &917099775
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 917099765}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 95e46aacea5b42888ee7881894193c11, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::Core.SaveLoad.SaveableState
--- !u!1001 &923779306
PrefabInstance:
m_ObjectHideFlags: 0
@@ -449856,6 +449947,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3660460661744729199, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
propertyPath: defaultState
value:
objectReference: {fileID: 917099765}
- target: {fileID: 5181221738455605092, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
propertyPath: m_Camera
value:
@@ -449896,14 +449991,35 @@ PrefabInstance:
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9074453772172382270, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
propertyPath: chaseSpline
value:
objectReference: {fileID: 729515061}
- target: {fileID: 9074453772172382270, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
propertyPath: lawnMowerRef
value:
objectReference: {fileID: 1481757349}
m_RemovedComponents: []
- target: {fileID: 9074453772172382270, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
propertyPath: lawnmowerAnchor
value:
objectReference: {fileID: 378802579}
- target: {fileID: 9074453772172382270, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
propertyPath: runningGardenerTransform
value:
objectReference: {fileID: 230875883}
m_RemovedComponents:
- {fileID: 5836635533533271833, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
- {fileID: 4324904235553461363, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
- {fileID: 8255968868352608137, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 780600094299918916, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
insertIndex: -1
addedObject: {fileID: 917099774}
- targetCorrespondingSourceObject: {fileID: 780600094299918916, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
insertIndex: -1
addedObject: {fileID: 917099775}
m_SourcePrefab: {fileID: 100100000, guid: 4b7426bc1f8736749b68973653f4dbfb, type: 3}
--- !u!114 &1101333110 stripped
MonoBehaviour:
@@ -452889,6 +453005,23 @@ PrefabInstance:
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: fbbe1f4baf226904b96f839fe0c00181, type: 3}
--- !u!1 &1263511373 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 3045303213881461051, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
m_PrefabInstance: {fileID: 4912039252317080710}
m_PrefabAsset: {fileID: 0}
--- !u!114 &1263511378
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1263511373}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 95e46aacea5b42888ee7881894193c11, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::Core.SaveLoad.SaveableState
--- !u!1 &1265143476
GameObject:
m_ObjectHideFlags: 0
@@ -457038,6 +457171,11 @@ GameObject:
m_CorrespondingSourceObject: {fileID: 1417937103223012543, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
m_PrefabInstance: {fileID: 4912039252317080710}
m_PrefabAsset: {fileID: 0}
--- !u!4 &1481757352 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 6004009293778554413, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
m_PrefabInstance: {fileID: 4912039252317080710}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &1482575303
PrefabInstance:
m_ObjectHideFlags: 0
@@ -470844,6 +470982,10 @@ PrefabInstance:
propertyPath: birdGameStats
value:
objectReference: {fileID: 708284665}
- target: {fileID: 2519051890178917637, guid: fc42c3bdda1c86d49b0bf80c28e5d372, type: 3}
propertyPath: m_LocalPosition.x
value: 6.11
objectReference: {fileID: 0}
- target: {fileID: 5418919921209364345, guid: fc42c3bdda1c86d49b0bf80c28e5d372, type: 3}
propertyPath: m_Name
value: AnneLiseBushA
@@ -471479,6 +471621,10 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 2114204102434534, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1126777572448403549, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
propertyPath: characterToInteract
value: 1
@@ -471543,6 +471689,10 @@ PrefabInstance:
propertyPath: characterArrived.m_PersistentCalls.m_Calls.Array.data[1].m_Arguments.m_ObjectArgumentAssemblyTypeName
value: UnityEngine.Object, UnityEngine
objectReference: {fileID: 0}
- target: {fileID: 3045303213881461051, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3485064730924644412, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
propertyPath: m_Name
value: LawnmowerStateMachine
@@ -471551,6 +471701,14 @@ PrefabInstance:
propertyPath: gardenerAudioController
value:
objectReference: {fileID: 430675504}
- target: {fileID: 3878369439964005511, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
propertyPath: defaultState
value:
objectReference: {fileID: 1263511373}
- target: {fileID: 6004009293778554413, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
propertyPath: m_LocalPosition.x
value: 72
objectReference: {fileID: 0}
- target: {fileID: 7402687028936857164, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
propertyPath: m_LocalPosition.x
value: 42.73
@@ -471591,10 +471749,19 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedComponents:
- {fileID: 2801535793207353683, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
- {fileID: 8303949474549176097, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
- {fileID: 7566253248842427861, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_AddedGameObjects:
- targetCorrespondingSourceObject: {fileID: 6004009293778554413, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
insertIndex: -1
addedObject: {fileID: 378802580}
m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 3045303213881461051, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
insertIndex: -1
addedObject: {fileID: 1263511378}
m_SourcePrefab: {fileID: 100100000, guid: 1fda7fccaa5fbd04695f4c98d29bcbe0, type: 3}
--- !u!114 &4912039252317080711 stripped
MonoBehaviour:
@@ -471794,6 +471961,14 @@ PrefabInstance:
propertyPath: m_Name
value: AnneLiseBushD
objectReference: {fileID: 0}
- target: {fileID: 5415533004257413878, guid: 3fa494ec083cbe54a86c3a1b107a90c0, type: 3}
propertyPath: m_LocalPosition.x
value: 6.5
objectReference: {fileID: 0}
- target: {fileID: 6239236866170444314, guid: 3fa494ec083cbe54a86c3a1b107a90c0, type: 3}
propertyPath: dialogueGraph
value:
objectReference: {fileID: 3965311268370046156, guid: cccf504e8af617a4295c5d0110ee29bb, type: 3}
- target: {fileID: 6920441724407775332, guid: 3fa494ec083cbe54a86c3a1b107a90c0, type: 3}
propertyPath: birdGameStats
value:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 70edf6dc95775d84fb06ae92d32fbc4c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 112000000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,36 +1,55 @@
using System;
using System.Collections;
using UnityEngine;
using Pixelplacement;
using Pixelplacement.TweenSystem;
using Core.SaveLoad;
using Pixelplacement;
using UnityEngine.Serialization;
public class GardenerChaseBehavior : MonoBehaviour
public class GardenerChaseBehavior : SaveableState
{
public Spline ChaseSpline;
public Transform GardenerObject;
private static readonly int Property = Animator.StringToHash("IsIdle?");
public Spline chaseSpline;
public Transform runningGardenerTransform;
public float chaseDuration;
public float chaseDelay;
[SerializeField] private Animator animator;
[SerializeField] public GameObject lawnMowerRef;
private TweenBase tweenRef;
public GardenerAudioController audioController;
public GameObject lawnmowerAnchor;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
public override void OnEnterState()
{
tweenRef = Tween.Spline (ChaseSpline, GardenerObject, 0, 1, false, chaseDuration, chaseDelay, Tween.EaseLinear, Tween.LoopType.None, HandleTweenStarted, HandleTweenFinished);
tweenRef = Tween.Spline(chaseSpline, runningGardenerTransform, 0, 1, false, chaseDuration, chaseDelay, Tween.EaseLinear,
Tween.LoopType.None, HandleTweenStarted, HandleTweenFinished);
}
public override void OnRestoreState(string data)
{
animator.SetBool("IsIdle?", false);
var gardenerSpriteRef = runningGardenerTransform.gameObject;
gardenerSpriteRef.transform.SetPositionAndRotation(lawnmowerAnchor.transform.position, gardenerSpriteRef.transform.rotation);
HandleTweenFinished();
}
void HandleTweenFinished ()
{
//Debug.Log ("Tween finished!");
tweenRef.Stop();
Destroy(ChaseSpline);
var gardenerSpriteRef = gameObject.transform.Find("GardenerRunningSprite");
Debug.Log ("Tween finished!");
tweenRef?.Stop();
Destroy(chaseSpline);
var gardenerSpriteRef = runningGardenerTransform.gameObject;
gardenerSpriteRef.transform.SetParent(lawnMowerRef.transform, true);
animator.SetBool(Property, false);
StartCoroutine(UpdateAnimatorBoolAfterDelay(0.5f));
}
private IEnumerator UpdateAnimatorBoolAfterDelay(float delay)
{
yield return new WaitForSeconds(delay);
animator.SetBool(Property, false);
}
void HandleTweenStarted ()
{
@@ -38,3 +57,4 @@ public class GardenerChaseBehavior : MonoBehaviour
animator.SetBool("IsIdle?", false);
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Bootstrap;
using UnityEngine;
@@ -26,6 +27,9 @@ namespace Core.SaveLoad
// Participant registry
private readonly Dictionary<string, ISaveParticipant> participants = new Dictionary<string, ISaveParticipant>();
// Pending participants (registered during restoration)
private readonly List<ISaveParticipant> pendingParticipants = new List<ISaveParticipant>();
// State
public bool IsSaving { get; private set; }
@@ -48,7 +52,10 @@ namespace Core.SaveLoad
private void Start()
{
#if UNITY_EDITOR
OnSceneLoadCompleted("RestoreInEditor");
#endif
Load();
}
private void OnApplicationQuit()
@@ -67,12 +74,6 @@ namespace Core.SaveLoad
SceneManagerService.Instance.SceneUnloadStarted += OnSceneUnloadStarted;
Logging.Debug("[SaveLoadManager] Subscribed to SceneManagerService events");
}
#if UNITY_EDITOR
OnSceneLoadCompleted("RestoreInEditor");
#endif
Load();
}
void OnDestroy()
@@ -119,11 +120,20 @@ namespace Core.SaveLoad
participants[saveId] = participant;
Logging.Debug($"[SaveLoadManager] Registered participant: {saveId}");
// If we have save data loaded and we're not currently restoring, restore this participant's state immediately
// BUT only if the participant hasn't already been restored (prevents double-restoration when inactive objects become active)
if (IsSaveDataLoaded && !IsRestoringState && currentSaveData != null && !participant.HasBeenRestored)
// If we have save data loaded and the participant hasn't been restored yet
if (IsSaveDataLoaded && currentSaveData != null && !participant.HasBeenRestored)
{
RestoreParticipantState(participant);
if (IsRestoringState)
{
// We're currently restoring - queue this participant for later restoration
pendingParticipants.Add(participant);
Logging.Debug($"[SaveLoadManager] Queued participant for pending restoration: {saveId}");
}
else
{
// Not currently restoring - restore this participant's state immediately
RestoreParticipantState(participant);
}
}
}
@@ -229,6 +239,7 @@ namespace Core.SaveLoad
/// <summary>
/// Restores state for all currently registered participants.
/// Called after loading save data.
/// Uses pending queue to handle participants that register during restoration.
/// </summary>
private void RestoreAllParticipantStates()
{
@@ -238,7 +249,12 @@ namespace Core.SaveLoad
IsRestoringState = true;
int restoredCount = 0;
foreach (var kvp in participants)
// Clear pending queue at the start
pendingParticipants.Clear();
// Create a snapshot to avoid collection modification during iteration
// (RestoreState can trigger GameObject activation which can register new participants)
foreach (var kvp in participants.ToList())
{
string saveId = kvp.Key;
ISaveParticipant participant = kvp.Value;
@@ -260,8 +276,48 @@ namespace Core.SaveLoad
}
}
// Process pending participants that registered during the main restoration loop
const int maxPendingPasses = 10;
int pendingPass = 0;
int totalPendingRestored = 0;
while (pendingParticipants.Count > 0 && pendingPass < maxPendingPasses)
{
pendingPass++;
// Take snapshot of current pending list and clear the main list
// (restoring pending participants might add more pending participants)
var currentPending = new List<ISaveParticipant>(pendingParticipants);
pendingParticipants.Clear();
int passRestored = 0;
foreach (var participant in currentPending)
{
try
{
RestoreParticipantState(participant);
passRestored++;
totalPendingRestored++;
}
catch (Exception ex)
{
Logging.Warning($"[SaveLoadManager] Exception while restoring pending participant: {ex}");
}
}
Logging.Debug($"[SaveLoadManager] Pending pass {pendingPass}: Restored {passRestored} participants");
}
if (pendingParticipants.Count > 0)
{
Logging.Warning($"[SaveLoadManager] Reached maximum pending passes ({maxPendingPasses}). {pendingParticipants.Count} participants remain unrestored.");
}
// Final cleanup
pendingParticipants.Clear();
IsRestoringState = false;
Logging.Debug($"[SaveLoadManager] Restored state for {restoredCount} participants");
Logging.Debug($"[SaveLoadManager] Restored state for {restoredCount} participants + {totalPendingRestored} pending participants");
OnParticipantStatesRestored?.Invoke();
}
@@ -334,8 +390,9 @@ namespace Core.SaveLoad
}
// Capture state from all registered participants directly into the list
// Create a snapshot to avoid collection modification during iteration
int savedCount = 0;
foreach (var kvp in participants)
foreach (var kvp in participants.ToList())
{
string saveId = kvp.Key;
ISaveParticipant participant = kvp.Value;

View File

@@ -0,0 +1,47 @@
using Pixelplacement;
namespace Core.SaveLoad
{
/// <summary>
/// Base class for states that need save/load functionality.
/// Inherit from this instead of Pixelplacement.State for states in SaveableStateMachines.
/// </summary>
public class SaveableState : State
{
/// <summary>
/// Called when this state is entered during normal gameplay.
/// Override this method to implement state initialization logic
/// (animations, player movement, event subscriptions, etc.).
/// This is NOT called when restoring from a save file.
/// </summary>
public virtual void OnEnterState()
{
// Default: Do nothing
// States override this to implement their entry logic
}
/// <summary>
/// Called when this state is being restored from a save file.
/// Override this method to restore state from saved data without
/// playing animations or triggering side effects.
/// </summary>
/// <param name="data">Serialized state data from SerializeState()</param>
public virtual void OnRestoreState(string data)
{
// Default: Do nothing
// States override this to implement their restoration logic
}
/// <summary>
/// Called when the state machine is being saved.
/// Override this method to serialize this state's internal data.
/// </summary>
/// <returns>Serialized state data as a string (JSON recommended)</returns>
public virtual string SerializeState()
{
// Default: No state data to save
return "";
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 95e46aacea5b42888ee7881894193c11
timeCreated: 1762121675

View File

@@ -0,0 +1,257 @@
using UnityEngine;
using Pixelplacement;
using Bootstrap;
namespace Core.SaveLoad
{
// SaveableStateMachine - Inherits from StateMachine, uses SaveableState for states
// Auto-generates Save ID from scene name and hierarchy path (like SaveableInteractable)
/// <summary>
/// Extended StateMachine that integrates with the AppleHills save/load system.
/// Inherits from Pixelplacement.StateMachine and adds save/load functionality.
/// Use SaveableState (not State) for child states to get save/load hooks.
/// </summary>
public class SaveableStateMachine : StateMachine, ISaveParticipant
{
[SerializeField]
[Tooltip("Optional custom save ID. If empty, will auto-generate from scene name and hierarchy path.")]
private string customSaveId = "";
/// <summary>
/// Is this state machine currently being restored from a save file?
/// </summary>
public bool IsRestoring { get; private set; }
/// <summary>
/// Has this state machine been restored from save data?
/// </summary>
public bool HasBeenRestored { get; private set; }
// Override ChangeState to call OnEnterState on SaveableState components
public new GameObject ChangeState(GameObject state)
{
var result = base.ChangeState(state);
// If not restoring and change was successful, call OnEnterState
if (!IsRestoring && result != null && currentState != null)
{
var saveableState = currentState.GetComponent<SaveableState>();
if (saveableState != null)
{
saveableState.OnEnterState();
}
}
return result;
}
public new GameObject ChangeState(string state)
{
var result = base.ChangeState(state);
// If not restoring and change was successful, call OnEnterState
if (!IsRestoring && result != null && currentState != null)
{
var saveableState = currentState.GetComponent<SaveableState>();
if (saveableState != null)
{
saveableState.OnEnterState();
}
}
return result;
}
public new GameObject ChangeState(int childIndex)
{
var result = base.ChangeState(childIndex);
// If not restoring and change was successful, call OnEnterState
if (!IsRestoring && result != null && currentState != null)
{
var saveableState = currentState.GetComponent<SaveableState>();
if (saveableState != null)
{
saveableState.OnEnterState();
}
}
return result;
}
private void Start()
{
// Register with save system (no validation needed - we auto-generate ID)
BootCompletionService.RegisterInitAction(() =>
{
if (SaveLoadManager.Instance != null)
{
SaveLoadManager.Instance.RegisterParticipant(this);
}
else
{
Debug.LogWarning($"[SaveableStateMachine] SaveLoadManager.Instance is null, cannot register '{name}'", this);
}
});
}
#if UNITY_EDITOR
private void OnValidate()
{
// Optional: Log the auto-generated ID in verbose mode
if (verbose && string.IsNullOrEmpty(customSaveId))
{
Debug.Log($"[SaveableStateMachine] '{name}' will use auto-generated Save ID: {GetSaveId()}", this);
}
}
#endif
private void OnDestroy()
{
// Unregister from save system
if (SaveLoadManager.Instance != null)
{
SaveLoadManager.Instance.UnregisterParticipant(GetSaveId());
}
}
#region ISaveParticipant Implementation
public string GetSaveId()
{
string sceneName = GetSceneName();
if (!string.IsNullOrEmpty(customSaveId))
{
return $"{sceneName}/{customSaveId}";
}
// Auto-generate from hierarchy path
string hierarchyPath = GetHierarchyPath();
return $"{sceneName}/StateMachine_{hierarchyPath}";
}
private string GetSceneName()
{
return gameObject.scene.name;
}
private string GetHierarchyPath()
{
string path = gameObject.name;
Transform parent = transform.parent;
while (parent != null)
{
path = parent.name + "/" + path;
parent = parent.parent;
}
return path;
}
public string SerializeState()
{
if (currentState == null)
{
return JsonUtility.ToJson(new StateMachineSaveData { stateName = "", stateData = "" });
}
SaveableState saveableState = currentState.GetComponent<SaveableState>();
string stateData = saveableState?.SerializeState() ?? "";
var saveData = new StateMachineSaveData
{
stateName = currentState.name,
stateData = stateData
};
return JsonUtility.ToJson(saveData);
}
public void RestoreState(string data)
{
if (string.IsNullOrEmpty(data))
{
if (verbose)
{
Debug.LogWarning($"[SaveableStateMachine] No data to restore for '{name}'", this);
}
return;
}
try
{
StateMachineSaveData saveData = JsonUtility.FromJson<StateMachineSaveData>(data);
if (string.IsNullOrEmpty(saveData.stateName))
{
if (verbose)
{
Debug.LogWarning($"[SaveableStateMachine] No state name in save data for '{name}'", this);
}
return;
}
// Set IsRestoring flag so we won't call OnEnterState
IsRestoring = true;
// Change to the saved state
ChangeState(saveData.stateName);
// Now explicitly call OnRestoreState with the saved data
if (currentState != null)
{
SaveableState saveableState = currentState.GetComponent<SaveableState>();
if (saveableState != null)
{
saveableState.OnRestoreState(saveData.stateData);
}
}
HasBeenRestored = true;
IsRestoring = false;
if (verbose)
{
Debug.Log($"[SaveableStateMachine] Restored '{name}' to state: {saveData.stateName}", this);
}
}
catch (System.Exception ex)
{
Debug.LogError($"[SaveableStateMachine] Exception restoring '{name}': {ex.Message}", this);
IsRestoring = false;
}
}
#endregion
#region Editor Utilities
#if UNITY_EDITOR
[ContextMenu("Log Save ID")]
private void LogSaveId()
{
Debug.Log($"Save ID: {GetSaveId()}", this);
}
[ContextMenu("Test Serialize")]
private void TestSerialize()
{
string serialized = SerializeState();
Debug.Log($"Serialized state: {serialized}", this);
}
#endif
#endregion
[System.Serializable]
private class StateMachineSaveData
{
public string stateName;
public string stateData;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6f56763d30b94bf6873d395a6c116eb5
timeCreated: 1762116611

View File

@@ -1,21 +1,15 @@
using System.Security.Cryptography.X509Certificates;
using Core;
using Core.SaveLoad;
using Pixelplacement;
using UnityEngine;
public class GardenerBehaviour : MonoBehaviour
public class GardenerBehaviour : SaveableStateMachine
{
private StateMachine stateMachineRef;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
stateMachineRef = GetComponent<StateMachine>();
}
public void stateSwitch (string StateName)
{
Logging.Debug("State Switch to: " + StateName);
stateMachineRef.ChangeState(StateName);
ChangeState(StateName);
}
}

View File

@@ -1,17 +1,9 @@
using Core;
using UnityEngine;
using Core.SaveLoad;
using Pixelplacement;
public class LawnMowerBehaviour : MonoBehaviour
public class LawnMowerBehaviour : SaveableStateMachine
{
private StateMachine stateMachineRef;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
stateMachineRef = GetComponent<StateMachine>();
}
public void mowerTouched()
{
Logging.Debug("Mower Touched");
@@ -20,6 +12,6 @@ public class LawnMowerBehaviour : MonoBehaviour
public void stateSwitch(string StateName)
{
Logging.Debug("State Switch to: " + StateName);
stateMachineRef.ChangeState(StateName);
ChangeState(StateName);
}
}

View File

@@ -1,7 +1,8 @@
using Core.SaveLoad;
using UnityEngine;
using Pixelplacement;
public class LawnMowerChaseBehaviour : MonoBehaviour
public class LawnMowerChaseBehaviour : SaveableState
{
public Spline ChaseSpline;
public Transform LawnMowerObject;
@@ -23,7 +24,7 @@ public class LawnMowerChaseBehaviour : MonoBehaviour
public bool gardenerChasing = true;
public GardenerAudioController gardenerAudioController;
void Start()
public override void OnEnterState()
{
LawnMowerObject.position = ChaseSpline.GetPosition(startPercentage);
@@ -66,6 +67,11 @@ public class LawnMowerChaseBehaviour : MonoBehaviour
_initialTweenActive = true;
}
public override void OnRestoreState(string data)
{
OnEnterState();
}
void Update()
{
float percentage = ChaseSpline.ClosestPoint(LawnMowerObject.position);