From 67bea695e4e3eb499a7370ecf688dcc64f211144 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Wed, 4 Dec 2024 15:44:45 -0500 Subject: [PATCH 01/31] Work last succesful action retry exploration --- .../Runtime/Prefabs/RGOverlayCanvas.prefab | 3200 ++++++----------- .../Runtime/Prefabs/RGOverlayCanvasV2.prefab | 89 +- .../BotSegments/ActionExplorationDriver.cs | 71 + .../ActionExplorationDriver.cs.meta | 3 + .../BotSegmentsPlaybackController.cs | 40 +- .../BotActions/KeyMomentMouseActionData.cs | 11 +- .../KeyMoments/IKeyMomentExploration.cs | 6 + .../KeyMoments/IKeyMomentExploration.cs.meta | 3 + 8 files changed, 1219 insertions(+), 2204 deletions(-) create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs.meta create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs.meta diff --git a/src/gg.regression.unity.bots/Runtime/Prefabs/RGOverlayCanvas.prefab b/src/gg.regression.unity.bots/Runtime/Prefabs/RGOverlayCanvas.prefab index 1120f3af..c606dd1d 100644 --- a/src/gg.regression.unity.bots/Runtime/Prefabs/RGOverlayCanvas.prefab +++ b/src/gg.regression.unity.bots/Runtime/Prefabs/RGOverlayCanvas.prefab @@ -414,8 +414,7 @@ MonoBehaviour: m_text: Segments m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} - m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, - type: 2} + m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} m_fontSharedMaterials: [] m_fontMaterial: {fileID: 0} m_fontMaterials: [] @@ -1299,8 +1298,7 @@ MonoBehaviour: m_text: Gameplay Session Uploading ... m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} - m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, - type: 2} + m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} m_fontSharedMaterials: [] m_fontMaterial: {fileID: 0} m_fontMaterials: [] @@ -1821,8 +1819,7 @@ MonoBehaviour: m_text: Sequences m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} - m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, - type: 2} + m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} m_fontSharedMaterials: [] m_fontMaterial: {fileID: 0} m_fontMaterials: [] @@ -1957,8 +1954,7 @@ MonoBehaviour: m_text: Replay Data Loading ... m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} - m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, - type: 2} + m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} m_fontSharedMaterials: [] m_fontMaterial: {fileID: 0} m_fontMaterials: [] @@ -2230,8 +2226,7 @@ MonoBehaviour: This Box Grows Updwards' m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} - m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, - type: 2} + m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} m_fontSharedMaterials: [] m_fontMaterial: {fileID: 0} m_fontMaterials: [] @@ -2649,8 +2644,7 @@ MonoBehaviour: m_text: 21 m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} - m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, - type: 2} + m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} m_fontSharedMaterials: [] m_fontMaterial: {fileID: 0} m_fontMaterials: [] @@ -3457,8 +3451,7 @@ MonoBehaviour: gameObjectsDropdown: {fileID: 0} behaviorsDropdown: {fileID: 0} activeBotRoot: {fileID: 0} - rgBotEntry: {fileID: 7128609913998066405, guid: bf084db059b1b48858cdb9ab14e8e53d, - type: 3} + rgBotEntry: {fileID: 7128609913998066405, guid: bf084db059b1b48858cdb9ab14e8e53d, type: 3} --- !u!114 &6602689997961244080 MonoBehaviour: m_ObjectHideFlags: 0 @@ -3473,13 +3466,10 @@ MonoBehaviour: m_EditorClassIdentifier: sequencesPanel: {fileID: 3545653956877431460} segmentsPanel: {fileID: 1972991633119368260} - sequenceCardPrefab: {fileID: 4199513441948227604, guid: 1286929087b3c4db0aa7ebfeba68731c, - type: 3} - segmentCardPrefab: {fileID: 4199513441948227604, guid: 33bc06c2f7ebd144d8e50252f54768c9, - type: 3} + sequenceCardPrefab: {fileID: 4199513441948227604, guid: 1286929087b3c4db0aa7ebfeba68731c, type: 3} + segmentCardPrefab: {fileID: 4199513441948227604, guid: 33bc06c2f7ebd144d8e50252f54768c9, type: 3} sequenceEditor: {fileID: 1768688999565580427} deleteSequenceDialog: {fileID: 5563740331335366267} - recordingToolbar: {fileID: 9073394237553129601} reloadButton: {fileID: 6051290809586765412} --- !u!114 &3883112371070943306 MonoBehaviour: @@ -4030,8 +4020,7 @@ MonoBehaviour: m_text: This overlay will close when starting a Sequence or Segment m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} - m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, - type: 2} + m_sharedMaterial: {fileID: 8592267718606122159, guid: 7fc1e1edc788747d3b207177e40103d6, type: 2} m_fontSharedMaterials: [] m_fontMaterial: {fileID: 0} m_fontMaterials: [] @@ -4256,6 +4245,7 @@ GameObject: - component: {fileID: 1784025199367272708} - component: {fileID: 5893801167117800152} - component: {fileID: 5122435472915985299} + - component: {fileID: 9033581721538341271} m_Layer: 5 m_Name: Recording Toolbar m_TagString: Untagged @@ -4349,6 +4339,18 @@ MonoBehaviour: mouseTransform: {fileID: 6927235806806861544} cursor: {fileID: 6509774744981593005} clickHighlight: {fileID: 4177226886652366683} +--- !u!114 &9033581721538341271 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9073394237553129601} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ad058943c1ca443a9fe70ad904bdc46b, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1 &9150084813955965295 GameObject: m_ObjectHideFlags: 0 @@ -4405,4713 +4407,3771 @@ PrefabInstance: propertyPath: m_AnchoredPosition.y value: -65 objectReference: {fileID: 0} - - target: {fileID: 30115021444014790, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 30115021444014790, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 30115021444014790, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 30115021444014790, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 30115021444014790, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 30115021444014790, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 30115021444014790, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 30115021444014790, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -337 objectReference: {fileID: 0} - - target: {fileID: 77335370782478927, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 77335370782478927, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 77335370782478927, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 77335370782478927, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 77335370782478927, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 77335370782478927, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 77335370782478927, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 77335370782478927, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 84115045780104405, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 84115045780104405, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 84115045780104405, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 84115045780104405, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 84115045780104405, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 84115045780104405, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 84115045780104405, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 84115045780104405, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 87384171957796238, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 87384171957796238, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 87384171957796238, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 87384171957796238, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 87384171957796238, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 87384171957796238, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 87384171957796238, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 87384171957796238, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 98339627337143294, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 98339627337143294, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 98339627337143294, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 98339627337143294, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 98339627337143294, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 98339627337143294, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 98339627337143294, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 98339627337143294, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 225 objectReference: {fileID: 0} - - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 558 objectReference: {fileID: 0} - - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 120.5 objectReference: {fileID: 0} - - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 129190860956398542, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -287 objectReference: {fileID: 0} - - target: {fileID: 203463490328798921, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 203463490328798921, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 203463490328798921, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 203463490328798921, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 203463490328798921, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 203463490328798921, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -192 objectReference: {fileID: 0} - - target: {fileID: 204130718417855833, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 204130718417855833, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 55 objectReference: {fileID: 0} - - target: {fileID: 247955018416004435, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 247955018416004435, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 247955018416004435, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 247955018416004435, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 247955018416004435, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 247955018416004435, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 257590620066606733, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 257590620066606733, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 257590620066606733, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 257590620066606733, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 257590620066606733, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 257590620066606733, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 257590620066606733, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 257590620066606733, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 260321299304885537, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 260321299304885537, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 260321299304885537, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 260321299304885537, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 260321299304885537, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 260321299304885537, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 260321299304885537, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 260321299304885537, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -81 objectReference: {fileID: 0} - - target: {fileID: 267687603784806310, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 267687603784806310, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 267687603784806310, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 267687603784806310, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 267687603784806310, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 267687603784806310, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -134 objectReference: {fileID: 0} - - target: {fileID: 331245472229601596, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 331245472229601596, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 331245472229601596, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 331245472229601596, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 331245472229601596, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 331245472229601596, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -290 objectReference: {fileID: 0} - - target: {fileID: 352213818025109303, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 352213818025109303, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 352213818025109303, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 352213818025109303, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 352213818025109303, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 352213818025109303, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 352213818025109303, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 352213818025109303, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 444379137753862658, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 444379137753862658, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 444379137753862658, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 444379137753862658, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 444379137753862658, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 444379137753862658, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 444379137753862658, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 444379137753862658, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 485213735607278569, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 485213735607278569, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 485213735607278569, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 485213735607278569, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 485213735607278569, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 485213735607278569, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 485213735607278569, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 485213735607278569, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 512626182302087506, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 512626182302087506, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 512626182302087506, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 512626182302087506, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 512626182302087506, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 512626182302087506, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -437 objectReference: {fileID: 0} - - target: {fileID: 521480107203449191, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 521480107203449191, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 521480107203449191, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 521480107203449191, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 521480107203449191, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 521480107203449191, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 521480107203449191, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 521480107203449191, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 569917710567418167, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 569917710567418167, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 569917710567418167, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 569917710567418167, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 569917710567418167, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 569917710567418167, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 181 objectReference: {fileID: 0} - - target: {fileID: 569917710567418167, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 569917710567418167, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 583609011113767133, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 583609011113767133, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 583609011113767133, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 583609011113767133, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 583609011113767133, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 583609011113767133, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 583609011113767133, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 583609011113767133, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 661130840400282561, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 661130840400282561, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 661130840400282561, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 661130840400282561, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 661130840400282561, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 661130840400282561, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 683127955022078699, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 683127955022078699, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 683127955022078699, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 683127955022078699, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 683127955022078699, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 683127955022078699, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 704607385059654815, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 704607385059654815, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 704607385059654815, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 704607385059654815, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 704607385059654815, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 704607385059654815, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 704607385059654815, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 704607385059654815, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 763208329488630369, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 763208329488630369, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 763208329488630369, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 763208329488630369, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 763208329488630369, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 763208329488630369, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -200 objectReference: {fileID: 0} - - target: {fileID: 777774228845721264, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 777774228845721264, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 777774228845721264, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 777774228845721264, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 777774228845721264, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 777774228845721264, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 780856496766251266, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 780856496766251266, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 780856496766251266, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 780856496766251266, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 780856496766251266, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 780856496766251266, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 780856496766251266, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 780856496766251266, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -383 objectReference: {fileID: 0} - - target: {fileID: 794040545167376962, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 794040545167376962, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 794040545167376962, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 794040545167376962, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 794040545167376962, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 794040545167376962, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 794040545167376962, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 794040545167376962, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 844610140676645786, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 844610140676645786, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 560 objectReference: {fileID: 0} - - target: {fileID: 906312436672542037, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 906312436672542037, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 906312436672542037, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 906312436672542037, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 906312436672542037, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 906312436672542037, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 12 objectReference: {fileID: 0} - - target: {fileID: 906312436672542037, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 906312436672542037, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 933406499206390999, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 933406499206390999, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 933406499206390999, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 933406499206390999, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 933406499206390999, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 933406499206390999, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -388 objectReference: {fileID: 0} - - target: {fileID: 951996313412098073, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 951996313412098073, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 951996313412098073, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 951996313412098073, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 951996313412098073, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 951996313412098073, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 2.5 objectReference: {fileID: 0} - - target: {fileID: 951996313412098073, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 951996313412098073, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 1005393149062427372, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1005393149062427372, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1005393149062427372, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1005393149062427372, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1005393149062427372, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1005393149062427372, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 1015058812686985689, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1015058812686985689, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1015058812686985689, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1015058812686985689, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1015058812686985689, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1015058812686985689, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -410 objectReference: {fileID: 0} - - target: {fileID: 1022650866000776515, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1022650866000776515, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1022650866000776515, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1022650866000776515, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1022650866000776515, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1022650866000776515, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 1022650866000776515, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1022650866000776515, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 1136352430772565462, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1136352430772565462, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1136352430772565462, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1136352430772565462, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1136352430772565462, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1136352430772565462, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -388 objectReference: {fileID: 0} - - target: {fileID: 1220159243891585983, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1220159243891585983, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1220159243891585983, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1220159243891585983, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1220159243891585983, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1220159243891585983, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 1220159243891585983, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1220159243891585983, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 1241122122446674274, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1241122122446674274, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1241122122446674274, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1241122122446674274, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1241122122446674274, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1241122122446674274, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 1241122122446674274, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1241122122446674274, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -107 objectReference: {fileID: 0} - - target: {fileID: 1306213549485166284, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1306213549485166284, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1306213549485166284, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1306213549485166284, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1306213549485166284, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1306213549485166284, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 1306213549485166284, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1306213549485166284, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 1318347851240235196, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1318347851240235196, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1318347851240235196, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1318347851240235196, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1318347851240235196, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1318347851240235196, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 1318347851240235196, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1318347851240235196, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 1328285981023653757, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1328285981023653757, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1328285981023653757, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1328285981023653757, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1328285981023653757, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1328285981023653757, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 1328285981023653757, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1328285981023653757, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 1385629104356653617, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1385629104356653617, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1385629104356653617, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1385629104356653617, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1385629104356653617, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1385629104356653617, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -94 objectReference: {fileID: 0} - - target: {fileID: 1386804125913104074, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1386804125913104074, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1386804125913104074, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1386804125913104074, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1386804125913104074, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1386804125913104074, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 1483096259220272133, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1483096259220272133, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1483096259220272133, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1483096259220272133, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1483096259220272133, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1483096259220272133, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 12 objectReference: {fileID: 0} - - target: {fileID: 1483096259220272133, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1483096259220272133, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 1537703756642258736, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1537703756642258736, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1537703756642258736, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1537703756642258736, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1537703756642258736, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1537703756642258736, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 1537703756642258736, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1537703756642258736, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 1558739268129681263, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1558739268129681263, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1558739268129681263, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1558739268129681263, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1558739268129681263, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1558739268129681263, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 112.5 objectReference: {fileID: 0} - - target: {fileID: 1558739268129681263, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1558739268129681263, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -246 objectReference: {fileID: 0} - - target: {fileID: 1568965991232751323, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1568965991232751323, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1568965991232751323, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1568965991232751323, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1568965991232751323, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1568965991232751323, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 1568965991232751323, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1568965991232751323, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 1585385324866452366, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1585385324866452366, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1585385324866452366, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1585385324866452366, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1585385324866452366, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1585385324866452366, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 1585385324866452366, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1585385324866452366, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 1585818349289657569, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1585818349289657569, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1585818349289657569, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1585818349289657569, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1585818349289657569, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1585818349289657569, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -42.5 objectReference: {fileID: 0} - - target: {fileID: 1701962130057935365, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1701962130057935365, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1701962130057935365, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1701962130057935365, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1701962130057935365, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1701962130057935365, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 1701962130057935365, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1701962130057935365, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 1712481366825139454, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1712481366825139454, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1712481366825139454, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1712481366825139454, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1712481366825139454, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1712481366825139454, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 1732535618533063130, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1732535618533063130, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1732535618533063130, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1732535618533063130, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1732535618533063130, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1732535618533063130, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 112.5 objectReference: {fileID: 0} - - target: {fileID: 1732535618533063130, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1732535618533063130, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -400.5 objectReference: {fileID: 0} - - target: {fileID: 1763505295149916016, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1763505295149916016, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1763505295149916016, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1763505295149916016, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1763505295149916016, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1763505295149916016, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 1763505295149916016, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1763505295149916016, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 1765439803952212468, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1765439803952212468, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1765439803952212468, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1765439803952212468, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1765439803952212468, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1765439803952212468, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 1853996611892664125, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1853996611892664125, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1853996611892664125, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1853996611892664125, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1853996611892664125, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1853996611892664125, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 1973878864165531119, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1973878864165531119, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1973878864165531119, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1973878864165531119, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 1973878864165531119, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1973878864165531119, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 1973878864165531119, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 1973878864165531119, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 2117598518535369365, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2117598518535369365, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2117598518535369365, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2117598518535369365, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2117598518535369365, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2117598518535369365, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -143 objectReference: {fileID: 0} - - target: {fileID: 2143089256640224570, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2143089256640224570, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2143089256640224570, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2143089256640224570, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2143089256640224570, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2143089256640224570, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -30 objectReference: {fileID: 0} - - target: {fileID: 2170721818604894310, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2170721818604894310, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2170721818604894310, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2170721818604894310, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2170721818604894310, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2170721818604894310, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 2228079383664856222, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2228079383664856222, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2228079383664856222, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2228079383664856222, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2228079383664856222, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2228079383664856222, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -437 objectReference: {fileID: 0} - - target: {fileID: 2233152366543563662, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2233152366543563662, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: overlayContainer value: objectReference: {fileID: 7029756735420670719} - - target: {fileID: 2251814040980116169, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2251814040980116169, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2251814040980116169, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2251814040980116169, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2251814040980116169, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2251814040980116169, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 2258853675713098347, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2258853675713098347, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2258853675713098347, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2258853675713098347, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2258853675713098347, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2258853675713098347, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 2258853675713098347, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2258853675713098347, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -38 objectReference: {fileID: 0} - - target: {fileID: 2267489858870464546, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2267489858870464546, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2267489858870464546, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2267489858870464546, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2267489858870464546, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2267489858870464546, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 2267489858870464546, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2267489858870464546, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -429 objectReference: {fileID: 0} - - target: {fileID: 2300806456631978087, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2300806456631978087, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2300806456631978087, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2300806456631978087, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2300806456631978087, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2300806456631978087, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 181 objectReference: {fileID: 0} - - target: {fileID: 2300806456631978087, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2300806456631978087, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 2303754332799187804, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2303754332799187804, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2303754332799187804, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2303754332799187804, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2303754332799187804, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2303754332799187804, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 2326912770822280407, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2326912770822280407, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2326912770822280407, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2326912770822280407, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2326912770822280407, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2326912770822280407, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -45 objectReference: {fileID: 0} - - target: {fileID: 2367708116977737020, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2367708116977737020, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2367708116977737020, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2367708116977737020, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2367708116977737020, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2367708116977737020, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -486 objectReference: {fileID: 0} - - target: {fileID: 2385158097636075017, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2385158097636075017, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2385158097636075017, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2385158097636075017, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2385158097636075017, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2385158097636075017, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -203 objectReference: {fileID: 0} - - target: {fileID: 2444398308664522348, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2444398308664522348, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2444398308664522348, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2444398308664522348, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2444398308664522348, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2444398308664522348, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 2444398308664522348, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2444398308664522348, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 2455122730164643849, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2455122730164643849, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2455122730164643849, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2455122730164643849, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2455122730164643849, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2455122730164643849, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -404 objectReference: {fileID: 0} - - target: {fileID: 2456675895653890512, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2456675895653890512, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2456675895653890512, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2456675895653890512, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2456675895653890512, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2456675895653890512, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 2479754433963900418, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2479754433963900418, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2479754433963900418, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2479754433963900418, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2479754433963900418, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2479754433963900418, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 2479754433963900418, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2479754433963900418, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 2582212326657905103, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2582212326657905103, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2582212326657905103, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2582212326657905103, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2582212326657905103, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2582212326657905103, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 2604155301919894007, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2604155301919894007, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2604155301919894007, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2604155301919894007, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2604155301919894007, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2604155301919894007, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 2604155301919894007, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2604155301919894007, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 2634833424808815480, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2634833424808815480, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2634833424808815480, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2634833424808815480, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2634833424808815480, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2634833424808815480, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 20.5 objectReference: {fileID: 0} - - target: {fileID: 2634833424808815480, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2634833424808815480, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 2685849558146343064, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2685849558146343064, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2685849558146343064, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2685849558146343064, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2685849558146343064, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2685849558146343064, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 181 objectReference: {fileID: 0} - - target: {fileID: 2685849558146343064, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2685849558146343064, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 2789543203456985149, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2789543203456985149, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2789543203456985149, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2789543203456985149, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2789543203456985149, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2789543203456985149, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 2789543203456985149, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2789543203456985149, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 2809958688114271528, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2809958688114271528, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2809958688114271528, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2809958688114271528, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2809958688114271528, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2809958688114271528, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 181 objectReference: {fileID: 0} - - target: {fileID: 2809958688114271528, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2809958688114271528, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 2821720489114375163, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2821720489114375163, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2821720489114375163, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2821720489114375163, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2821720489114375163, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2821720489114375163, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 2858755361231068352, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2858755361231068352, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2858755361231068352, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2858755361231068352, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2858755361231068352, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2858755361231068352, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -166 objectReference: {fileID: 0} - - target: {fileID: 2866394882537468592, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2866394882537468592, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2866394882537468592, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2866394882537468592, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2866394882537468592, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2866394882537468592, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -234 objectReference: {fileID: 0} - - target: {fileID: 2896101278252355382, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2896101278252355382, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2896101278252355382, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2896101278252355382, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2896101278252355382, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2896101278252355382, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 2896101278252355382, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2896101278252355382, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 2947393856728721127, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2947393856728721127, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2947393856728721127, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2947393856728721127, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2947393856728721127, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2947393856728721127, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 181 objectReference: {fileID: 0} - - target: {fileID: 2947393856728721127, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2947393856728721127, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 2949844792206942361, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2949844792206942361, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2949844792206942361, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2949844792206942361, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2949844792206942361, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2949844792206942361, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 2949844792206942361, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2949844792206942361, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 2969045366243343080, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2969045366243343080, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2969045366243343080, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2969045366243343080, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2969045366243343080, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2969045366243343080, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 2969045366243343080, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 2969045366243343080, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 3018154975732926576, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3018154975732926576, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3018154975732926576, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3018154975732926576, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3018154975732926576, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3018154975732926576, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 3018154975732926576, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3018154975732926576, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 3027872231084616681, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3027872231084616681, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3027872231084616681, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3027872231084616681, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3027872231084616681, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3027872231084616681, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 3027872231084616681, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3027872231084616681, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 3029193201226372439, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3029193201226372439, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3029193201226372439, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3029193201226372439, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3029193201226372439, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3029193201226372439, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -42.5 objectReference: {fileID: 0} - - target: {fileID: 3122723130810400760, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3122723130810400760, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3122723130810400760, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3122723130810400760, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3122723130810400760, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3122723130810400760, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 3127599251048193682, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3127599251048193682, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3127599251048193682, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3127599251048193682, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3127599251048193682, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3127599251048193682, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 3127599251048193682, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3127599251048193682, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 3165674883933869677, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3165674883933869677, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3165674883933869677, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3165674883933869677, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3165674883933869677, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3165674883933869677, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 3199381384026623327, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3199381384026623327, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3199381384026623327, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3199381384026623327, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3199381384026623327, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3199381384026623327, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 3199381384026623327, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3199381384026623327, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 3212811395144484037, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3212811395144484037, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3212811395144484037, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3212811395144484037, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3212811395144484037, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3212811395144484037, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 3218860630197044214, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3218860630197044214, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3218860630197044214, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3218860630197044214, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3218860630197044214, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3218860630197044214, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 3218860630197044214, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3218860630197044214, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -61 objectReference: {fileID: 0} - - target: {fileID: 3291164635857811980, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3291164635857811980, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3291164635857811980, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3291164635857811980, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3291164635857811980, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3291164635857811980, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 3311823783812664700, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3311823783812664700, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3311823783812664700, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3311823783812664700, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3311823783812664700, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3311823783812664700, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -370 objectReference: {fileID: 0} - - target: {fileID: 3380071134174822371, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3380071134174822371, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3380071134174822371, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3380071134174822371, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3380071134174822371, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3380071134174822371, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -192 objectReference: {fileID: 0} - - target: {fileID: 3427790084538253474, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3427790084538253474, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3427790084538253474, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3427790084538253474, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3427790084538253474, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3427790084538253474, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 3427790084538253474, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3427790084538253474, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 3511611198738013431, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3511611198738013431, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3511611198738013431, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3511611198738013431, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3511611198738013431, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3511611198738013431, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 3511611198738013431, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3511611198738013431, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -291 objectReference: {fileID: 0} - - target: {fileID: 3536485242179543856, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3536485242179543856, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3536485242179543856, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3536485242179543856, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3536485242179543856, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3536485242179543856, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 3536485242179543856, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3536485242179543856, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 3558662070718837907, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3558662070718837907, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3558662070718837907, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3558662070718837907, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3558662070718837907, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3558662070718837907, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 3558662070718837907, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3558662070718837907, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 3617771236235289097, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3617771236235289097, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3617771236235289097, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3617771236235289097, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3617771236235289097, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3617771236235289097, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 3617771236235289097, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3617771236235289097, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 3643968847881988958, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3643968847881988958, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3643968847881988958, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3643968847881988958, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3643968847881988958, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3643968847881988958, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 3643968847881988958, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3643968847881988958, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 3678061572166905370, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3678061572166905370, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3678061572166905370, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3678061572166905370, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3678061572166905370, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3678061572166905370, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 3678061572166905370, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3678061572166905370, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -199 objectReference: {fileID: 0} - - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 1017 objectReference: {fileID: 0} - - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 577 objectReference: {fileID: 0} - - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 524.5 objectReference: {fileID: 0} - - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3687363723740831427, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -304.5 objectReference: {fileID: 0} - - target: {fileID: 3791481730052276291, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3791481730052276291, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3791481730052276291, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3791481730052276291, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3791481730052276291, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3791481730052276291, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -339 objectReference: {fileID: 0} - - target: {fileID: 3819287859360718370, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3819287859360718370, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3819287859360718370, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3819287859360718370, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3819287859360718370, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3819287859360718370, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 350 objectReference: {fileID: 0} - - target: {fileID: 3819287859360718370, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3819287859360718370, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -38 objectReference: {fileID: 0} - - target: {fileID: 3855995637649192986, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3855995637649192986, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3855995637649192986, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3855995637649192986, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3855995637649192986, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3855995637649192986, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 12 objectReference: {fileID: 0} - - target: {fileID: 3855995637649192986, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3855995637649192986, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 3876394009550353679, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3876394009550353679, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3876394009550353679, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3876394009550353679, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3876394009550353679, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3876394009550353679, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 3876394009550353679, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3876394009550353679, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 3900092926629035137, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3900092926629035137, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3900092926629035137, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3900092926629035137, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3900092926629035137, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3900092926629035137, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -339 objectReference: {fileID: 0} - - target: {fileID: 3907937977386941866, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3907937977386941866, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3907937977386941866, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3907937977386941866, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3907937977386941866, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3907937977386941866, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 12 objectReference: {fileID: 0} - - target: {fileID: 3907937977386941866, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3907937977386941866, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 3912712235540535504, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3912712235540535504, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 276.14 objectReference: {fileID: 0} - - target: {fileID: 3912712235540535504, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3912712235540535504, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 43.21 objectReference: {fileID: 0} - - target: {fileID: 3989648726170367173, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3989648726170367173, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3989648726170367173, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3989648726170367173, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3989648726170367173, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3989648726170367173, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 3989648726170367173, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3989648726170367173, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 3992090404843696226, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3992090404843696226, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3992090404843696226, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3992090404843696226, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 3992090404843696226, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 3992090404843696226, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4042465043493813398, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4042465043493813398, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_Name value: Sequence Editor objectReference: {fileID: 0} - - target: {fileID: 4042465043493813398, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4042465043493813398, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_IsActive value: 0 objectReference: {fileID: 0} - - target: {fileID: 4042741396440515488, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4042741396440515488, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4042741396440515488, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4042741396440515488, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4042741396440515488, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4042741396440515488, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 4042741396440515488, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4042741396440515488, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4077022293691431972, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4077022293691431972, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.x value: 1 objectReference: {fileID: 0} - - target: {fileID: 4077022293691431972, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4077022293691431972, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4077022293691431972, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4077022293691431972, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 0.11532384 objectReference: {fileID: 0} - - target: {fileID: 4116029253153515353, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4116029253153515353, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4116029253153515353, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4116029253153515353, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4116029253153515353, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4116029253153515353, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -341 objectReference: {fileID: 0} - - target: {fileID: 4141320251370026715, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4141320251370026715, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4141320251370026715, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4141320251370026715, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4141320251370026715, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4141320251370026715, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 4141320251370026715, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4141320251370026715, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_Pivot.x value: 0.5 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_Pivot.y value: 0.5 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_RootOrder value: 5 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.x value: 1 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalPosition.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalRotation.w value: 1 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalRotation.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalRotation.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalRotation.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalEulerAnglesHint.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalEulerAnglesHint.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 4172012668602294237, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4172012668602294237, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4172012668602294237, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4172012668602294237, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4172012668602294237, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4172012668602294237, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 4200149898977339354, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4200149898977339354, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4200149898977339354, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4200149898977339354, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4200149898977339354, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4200149898977339354, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 4200149898977339354, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4200149898977339354, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4226592279210574713, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4226592279210574713, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4226592279210574713, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4226592279210574713, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4226592279210574713, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4226592279210574713, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4226592279210574713, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4226592279210574713, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4268085035395573039, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4268085035395573039, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4268085035395573039, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4268085035395573039, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4268085035395573039, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4268085035395573039, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 4268085035395573039, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4268085035395573039, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 4273003277522735620, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4273003277522735620, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4273003277522735620, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4273003277522735620, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4273003277522735620, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4273003277522735620, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 4273003277522735620, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4273003277522735620, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 4274026643118787423, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4274026643118787423, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4274026643118787423, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4274026643118787423, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4274026643118787423, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4274026643118787423, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 4274026643118787423, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4274026643118787423, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 4292547435812371883, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4292547435812371883, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4292547435812371883, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4292547435812371883, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4292547435812371883, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4292547435812371883, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 4292547435812371883, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4292547435812371883, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4295020119809080277, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4295020119809080277, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4295020119809080277, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4295020119809080277, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4295020119809080277, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4295020119809080277, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 12 objectReference: {fileID: 0} - - target: {fileID: 4295020119809080277, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4295020119809080277, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 4390486264951443393, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4390486264951443393, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4390486264951443393, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4390486264951443393, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4390486264951443393, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4390486264951443393, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 4390486264951443393, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4390486264951443393, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 4412091861332618992, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4412091861332618992, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4412091861332618992, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4412091861332618992, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4412091861332618992, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4412091861332618992, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 4426156168036536282, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4426156168036536282, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4426156168036536282, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4426156168036536282, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4426156168036536282, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4426156168036536282, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4505085611890266598, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4505085611890266598, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4505085611890266598, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4505085611890266598, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4505085611890266598, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4505085611890266598, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 4505085611890266598, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4505085611890266598, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 4529809092500216705, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4529809092500216705, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4529809092500216705, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4529809092500216705, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4529809092500216705, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4529809092500216705, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4545258759768844252, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4545258759768844252, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4545258759768844252, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4545258759768844252, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4545258759768844252, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4545258759768844252, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 64.5 objectReference: {fileID: 0} - - target: {fileID: 4545258759768844252, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4545258759768844252, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 4547432178857562221, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4547432178857562221, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4547432178857562221, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4547432178857562221, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4547432178857562221, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4547432178857562221, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 4547432178857562221, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4547432178857562221, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 4690513678931962887, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4690513678931962887, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4690513678931962887, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4690513678931962887, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4690513678931962887, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4690513678931962887, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4728737844615621475, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4728737844615621475, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4728737844615621475, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4728737844615621475, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4728737844615621475, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4728737844615621475, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -42.5 objectReference: {fileID: 0} - - target: {fileID: 4766828743738240723, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4766828743738240723, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4766828743738240723, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4766828743738240723, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4766828743738240723, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4766828743738240723, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -42.5 objectReference: {fileID: 0} - - target: {fileID: 4775895431174469971, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4775895431174469971, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4775895431174469971, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4775895431174469971, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4775895431174469971, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4775895431174469971, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 176 objectReference: {fileID: 0} - - target: {fileID: 4775895431174469971, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4775895431174469971, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -321 objectReference: {fileID: 0} - - target: {fileID: 4819063596728966182, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4819063596728966182, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4819063596728966182, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4819063596728966182, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4819063596728966182, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4819063596728966182, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -45 objectReference: {fileID: 0} - - target: {fileID: 4846702226123712444, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4846702226123712444, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4846702226123712444, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4846702226123712444, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4846702226123712444, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4846702226123712444, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 4864112806864870766, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4864112806864870766, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4864112806864870766, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4864112806864870766, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4864112806864870766, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4864112806864870766, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 4864112806864870766, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4864112806864870766, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 4871179589147381957, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4871179589147381957, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4871179589147381957, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4871179589147381957, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4871179589147381957, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4871179589147381957, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4919363145526898586, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4919363145526898586, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4919363145526898586, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4919363145526898586, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4919363145526898586, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4919363145526898586, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 4919363145526898586, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4919363145526898586, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 4950859946163183492, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4950859946163183492, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4950859946163183492, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4950859946163183492, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4950859946163183492, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4950859946163183492, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 4950859946163183492, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4950859946163183492, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 4974501804509130825, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4974501804509130825, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4974501804509130825, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4974501804509130825, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4974501804509130825, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 4974501804509130825, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 5040543715760412092, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5040543715760412092, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5040543715760412092, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5040543715760412092, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5040543715760412092, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5040543715760412092, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 5040543715760412092, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5040543715760412092, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 5083754474524375079, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5083754474524375079, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5083754474524375079, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5083754474524375079, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5083754474524375079, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5083754474524375079, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 352 objectReference: {fileID: 0} - - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 561 objectReference: {fileID: 0} - - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 825 objectReference: {fileID: 0} - - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5166697826147890227, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -288.5 objectReference: {fileID: 0} - - target: {fileID: 5176041274309925380, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5176041274309925380, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5176041274309925380, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5176041274309925380, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5176041274309925380, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5176041274309925380, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 48 objectReference: {fileID: 0} - - target: {fileID: 5176041274309925380, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5176041274309925380, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 5254254121217106846, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5254254121217106846, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5254254121217106846, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5254254121217106846, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5254254121217106846, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5254254121217106846, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -535 objectReference: {fileID: 0} - - target: {fileID: 5270904493479099785, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5270904493479099785, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5270904493479099785, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5270904493479099785, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5270904493479099785, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5270904493479099785, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 5270904493479099785, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5270904493479099785, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 5339952742944465712, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5339952742944465712, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5339952742944465712, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5339952742944465712, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5339952742944465712, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5339952742944465712, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -64 objectReference: {fileID: 0} - - target: {fileID: 5386016517325636194, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5386016517325636194, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5386016517325636194, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5386016517325636194, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5386016517325636194, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5386016517325636194, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 5386016517325636194, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5386016517325636194, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 5390158124389872711, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5390158124389872711, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5390158124389872711, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5390158124389872711, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5390158124389872711, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5390158124389872711, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 5390158124389872711, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5390158124389872711, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 5429965128184412101, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5429965128184412101, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5429965128184412101, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5429965128184412101, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5429965128184412101, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5429965128184412101, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -535 objectReference: {fileID: 0} - - target: {fileID: 5491863396796674483, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5491863396796674483, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5491863396796674483, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5491863396796674483, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5491863396796674483, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5491863396796674483, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 5491863396796674483, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5491863396796674483, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 5519463783089244957, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5519463783089244957, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5519463783089244957, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5519463783089244957, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5519463783089244957, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5519463783089244957, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 5568861853753603006, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5568861853753603006, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5568861853753603006, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5568861853753603006, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5568861853753603006, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5568861853753603006, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 5568861853753603006, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5568861853753603006, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 5576592467085567394, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5576592467085567394, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5576592467085567394, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5576592467085567394, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5576592467085567394, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5576592467085567394, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 5577984834215615260, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5577984834215615260, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5577984834215615260, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5577984834215615260, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5577984834215615260, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5577984834215615260, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 181 objectReference: {fileID: 0} - - target: {fileID: 5577984834215615260, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5577984834215615260, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 5618542852586048676, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5618542852586048676, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5618542852586048676, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5618542852586048676, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5618542852586048676, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5618542852586048676, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 5618542852586048676, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5618542852586048676, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 5644430396891624611, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5644430396891624611, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5644430396891624611, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5644430396891624611, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5644430396891624611, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5644430396891624611, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 5686548939280700271, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5686548939280700271, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5686548939280700271, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5686548939280700271, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5686548939280700271, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5686548939280700271, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 5686548939280700271, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5686548939280700271, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -245 objectReference: {fileID: 0} - - target: {fileID: 5730302301586565292, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5730302301586565292, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5730302301586565292, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5730302301586565292, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5730302301586565292, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5730302301586565292, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -42.5 objectReference: {fileID: 0} - - target: {fileID: 5850751717701770817, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5850751717701770817, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5850751717701770817, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5850751717701770817, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5850751717701770817, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5850751717701770817, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 5850751717701770817, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5850751717701770817, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 5861759957892968497, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5861759957892968497, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5861759957892968497, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5861759957892968497, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5861759957892968497, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5861759957892968497, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 5861759957892968497, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5861759957892968497, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 5956739885965720388, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5956739885965720388, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5956739885965720388, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5956739885965720388, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5956739885965720388, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5956739885965720388, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 5957912699959869375, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5957912699959869375, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5957912699959869375, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5957912699959869375, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 5957912699959869375, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 5957912699959869375, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -143 objectReference: {fileID: 0} - - target: {fileID: 6097813947988134477, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6097813947988134477, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6097813947988134477, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6097813947988134477, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6097813947988134477, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6097813947988134477, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 6117902980420797688, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6117902980420797688, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6117902980420797688, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6117902980420797688, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6117902980420797688, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6117902980420797688, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 6117902980420797688, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6117902980420797688, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 6120488409171767962, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6120488409171767962, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: overlayContainer value: objectReference: {fileID: 7029756735420670719} - - target: {fileID: 6151292713048977784, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6151292713048977784, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6151292713048977784, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6151292713048977784, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6151292713048977784, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6151292713048977784, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 6182557230287648915, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6182557230287648915, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6182557230287648915, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6182557230287648915, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6182557230287648915, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6182557230287648915, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 6236444078902467295, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6236444078902467295, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6236444078902467295, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6236444078902467295, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6236444078902467295, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6236444078902467295, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 6236444078902467295, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6236444078902467295, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 6285005982554436264, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6285005982554436264, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6285005982554436264, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6285005982554436264, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6285005982554436264, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6285005982554436264, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 6285005982554436264, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6285005982554436264, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 6310066907093996948, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6310066907093996948, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6310066907093996948, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6310066907093996948, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6310066907093996948, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6310066907093996948, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -479 objectReference: {fileID: 0} - - target: {fileID: 6325650855192150710, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6325650855192150710, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6325650855192150710, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6325650855192150710, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6325650855192150710, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6325650855192150710, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 6325650855192150710, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6325650855192150710, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 6396495503706644968, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6396495503706644968, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.x value: 1 objectReference: {fileID: 0} - - target: {fileID: 6396495503706644968, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6396495503706644968, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6396495503706644968, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6396495503706644968, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 0.10280377 objectReference: {fileID: 0} - - target: {fileID: 6417685734736914561, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6417685734736914561, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6417685734736914561, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6417685734736914561, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6417685734736914561, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6417685734736914561, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -633 objectReference: {fileID: 0} - - target: {fileID: 6441420351778335827, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6441420351778335827, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6441420351778335827, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6441420351778335827, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6441420351778335827, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6441420351778335827, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -132 objectReference: {fileID: 0} - - target: {fileID: 6462794463485520245, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6462794463485520245, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6462794463485520245, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6462794463485520245, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6462794463485520245, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6462794463485520245, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 6462794463485520245, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6462794463485520245, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 6468079940336033616, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6468079940336033616, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6468079940336033616, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6468079940336033616, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6468079940336033616, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6468079940336033616, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 6468079940336033616, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6468079940336033616, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 352 objectReference: {fileID: 0} - - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 462 objectReference: {fileID: 0} - - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 441 objectReference: {fileID: 0} - - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6474244363453279179, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -239 objectReference: {fileID: 0} - - target: {fileID: 6511016102994059451, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6511016102994059451, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6511016102994059451, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6511016102994059451, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6511016102994059451, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6511016102994059451, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 6511016102994059451, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6511016102994059451, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 6557389948899713421, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6557389948899713421, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6557389948899713421, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6557389948899713421, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6557389948899713421, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6557389948899713421, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 6557389948899713421, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6557389948899713421, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 6588323240625918887, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6588323240625918887, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6588323240625918887, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6588323240625918887, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6588323240625918887, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6588323240625918887, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 6697716499446789088, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6697716499446789088, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6697716499446789088, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6697716499446789088, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6697716499446789088, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6697716499446789088, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 6779393761168049710, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6779393761168049710, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6779393761168049710, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6779393761168049710, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6779393761168049710, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6779393761168049710, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 12 objectReference: {fileID: 0} - - target: {fileID: 6779393761168049710, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6779393761168049710, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 6788427989059055244, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6788427989059055244, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6788427989059055244, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6788427989059055244, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6788427989059055244, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6788427989059055244, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 6788427989059055244, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6788427989059055244, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 6802895412626651689, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6802895412626651689, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6802895412626651689, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6802895412626651689, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6802895412626651689, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6802895412626651689, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -290 objectReference: {fileID: 0} - - target: {fileID: 6810805923639480726, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6810805923639480726, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6810805923639480726, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6810805923639480726, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6810805923639480726, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6810805923639480726, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 6810805923639480726, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6810805923639480726, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 6852177013983798401, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6852177013983798401, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6852177013983798401, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6852177013983798401, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6852177013983798401, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6852177013983798401, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -98 objectReference: {fileID: 0} - - target: {fileID: 6866513587730574465, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6866513587730574465, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6866513587730574465, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6866513587730574465, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6866513587730574465, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6866513587730574465, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 6866513587730574465, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6866513587730574465, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 6880254950007033323, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6880254950007033323, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6880254950007033323, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6880254950007033323, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6880254950007033323, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6880254950007033323, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 6956339239428777852, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6956339239428777852, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6956339239428777852, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6956339239428777852, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6956339239428777852, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 6956339239428777852, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -42.5 objectReference: {fileID: 0} - - target: {fileID: 7010944589376363593, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7010944589376363593, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7010944589376363593, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7010944589376363593, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7010944589376363593, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7010944589376363593, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 7024732067703882376, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7024732067703882376, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7024732067703882376, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7024732067703882376, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7024732067703882376, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7024732067703882376, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 247.5 objectReference: {fileID: 0} - - target: {fileID: 7024732067703882376, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7024732067703882376, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6.749999 objectReference: {fileID: 0} - - target: {fileID: 7051216308554388898, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7051216308554388898, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7051216308554388898, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7051216308554388898, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7051216308554388898, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7051216308554388898, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 7069825668023797656, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7069825668023797656, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7069825668023797656, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7069825668023797656, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7069825668023797656, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7069825668023797656, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 12 objectReference: {fileID: 0} - - target: {fileID: 7069825668023797656, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7069825668023797656, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 7080720688030013249, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7080720688030013249, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_RaycastTarget value: 1 objectReference: {fileID: 0} - - target: {fileID: 7080720688030013249, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7080720688030013249, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_UseSpriteMesh value: 0 objectReference: {fileID: 0} - - target: {fileID: 7080720688030013249, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7080720688030013249, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_PreserveAspect value: 0 objectReference: {fileID: 0} - - target: {fileID: 7081335814271635642, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7081335814271635642, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7081335814271635642, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7081335814271635642, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7081335814271635642, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7081335814271635642, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -241 objectReference: {fileID: 0} - - target: {fileID: 7164347239034604592, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7164347239034604592, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 1049 objectReference: {fileID: 0} - - target: {fileID: 7164347239034604592, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7164347239034604592, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 609 objectReference: {fileID: 0} - - target: {fileID: 7180193048215726465, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7180193048215726465, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7180193048215726465, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7180193048215726465, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7180193048215726465, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7180193048215726465, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 7180193048215726465, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7180193048215726465, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 7210765804473045784, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7210765804473045784, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7210765804473045784, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7210765804473045784, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7210765804473045784, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7210765804473045784, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 7210765804473045784, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7210765804473045784, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 7229192940109731933, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7229192940109731933, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7229192940109731933, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7229192940109731933, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7229192940109731933, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7229192940109731933, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 7229192940109731933, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7229192940109731933, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 7403002483852777077, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7403002483852777077, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7403002483852777077, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7403002483852777077, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7403002483852777077, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7403002483852777077, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 7431518413187657139, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7431518413187657139, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7431518413187657139, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7431518413187657139, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7431518413187657139, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7431518413187657139, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 7431518413187657139, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7431518413187657139, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 7558510768859012817, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7558510768859012817, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7558510768859012817, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7558510768859012817, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7558510768859012817, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7558510768859012817, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 7652598395964180341, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7652598395964180341, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7652598395964180341, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7652598395964180341, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7652598395964180341, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7652598395964180341, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 7652598395964180341, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7652598395964180341, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -153 objectReference: {fileID: 0} - - target: {fileID: 7681766743012783757, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7681766743012783757, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7681766743012783757, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7681766743012783757, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7681766743012783757, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7681766743012783757, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 7681766743012783757, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7681766743012783757, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 7693851504597581453, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7693851504597581453, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7693851504597581453, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7693851504597581453, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7693851504597581453, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7693851504597581453, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -241 objectReference: {fileID: 0} - - target: {fileID: 7730632265507893707, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7730632265507893707, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7730632265507893707, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7730632265507893707, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7730632265507893707, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7730632265507893707, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 7730632265507893707, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7730632265507893707, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 7741049735351951578, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7741049735351951578, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7741049735351951578, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7741049735351951578, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7741049735351951578, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7741049735351951578, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 7753494466888832034, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7753494466888832034, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7753494466888832034, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7753494466888832034, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7753494466888832034, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7753494466888832034, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -272 objectReference: {fileID: 0} - - target: {fileID: 7774463935147190309, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7774463935147190309, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7774463935147190309, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7774463935147190309, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7774463935147190309, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7774463935147190309, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 7774463935147190309, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7774463935147190309, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 7799312910598814585, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7799312910598814585, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7799312910598814585, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7799312910598814585, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7799312910598814585, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7799312910598814585, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -584 objectReference: {fileID: 0} - - target: {fileID: 7824541253030743126, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7824541253030743126, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7824541253030743126, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7824541253030743126, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7824541253030743126, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7824541253030743126, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 176 objectReference: {fileID: 0} - - target: {fileID: 7824541253030743126, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7824541253030743126, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 7826411434696489613, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7826411434696489613, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7826411434696489613, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7826411434696489613, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7826411434696489613, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7826411434696489613, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 7826411434696489613, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7826411434696489613, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 7847602860803915359, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7847602860803915359, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7847602860803915359, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7847602860803915359, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7847602860803915359, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7847602860803915359, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 7847602860803915359, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7847602860803915359, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 7848185623830490126, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7848185623830490126, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7848185623830490126, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7848185623830490126, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7848185623830490126, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7848185623830490126, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 7848185623830490126, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7848185623830490126, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 7880952190095000697, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7880952190095000697, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7880952190095000697, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7880952190095000697, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7880952190095000697, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7880952190095000697, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 117.5 objectReference: {fileID: 0} - - target: {fileID: 7880952190095000697, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7880952190095000697, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -6 objectReference: {fileID: 0} - - target: {fileID: 7902151618787621115, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7902151618787621115, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7902151618787621115, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7902151618787621115, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7902151618787621115, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7902151618787621115, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 7902151618787621115, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7902151618787621115, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 7934170242872945824, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7934170242872945824, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7934170242872945824, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7934170242872945824, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7934170242872945824, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7934170242872945824, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 7934170242872945824, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7934170242872945824, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 260.14 objectReference: {fileID: 0} - - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 27.21 objectReference: {fileID: 0} - - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 138.07 objectReference: {fileID: 0} - - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 7980773051223131924, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21.605 objectReference: {fileID: 0} - - target: {fileID: 8004919701782828230, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8004919701782828230, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 8004919701782828230, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8004919701782828230, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 8004919701782828230, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8004919701782828230, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 8004919701782828230, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8004919701782828230, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: 81 objectReference: {fileID: 0} - - target: {fileID: 8032246627525164694, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8032246627525164694, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8032246627525164694, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8032246627525164694, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8032246627525164694, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8032246627525164694, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 8151185325810277752, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8151185325810277752, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8151185325810277752, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8151185325810277752, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8151185325810277752, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8151185325810277752, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 8169764946748676778, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8169764946748676778, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8169764946748676778, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8169764946748676778, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8169764946748676778, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8169764946748676778, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 181 objectReference: {fileID: 0} - - target: {fileID: 8169764946748676778, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8169764946748676778, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 8199324428626115990, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8199324428626115990, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8199324428626115990, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8199324428626115990, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8199324428626115990, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8199324428626115990, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -268 objectReference: {fileID: 0} - - target: {fileID: 8258254911629808918, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8258254911629808918, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8258254911629808918, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8258254911629808918, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8258254911629808918, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8258254911629808918, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 8385644836376080513, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8385644836376080513, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8385644836376080513, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8385644836376080513, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8385644836376080513, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8385644836376080513, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 8385644836376080513, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8385644836376080513, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 8424853825852650310, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8424853825852650310, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 73 objectReference: {fileID: 0} - - target: {fileID: 8434720392888418614, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8434720392888418614, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8434720392888418614, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8434720392888418614, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8434720392888418614, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8434720392888418614, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 8469567553629357650, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8469567553629357650, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8469567553629357650, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8469567553629357650, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8469567553629357650, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8469567553629357650, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 8516594591135269858, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8516594591135269858, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8516594591135269858, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8516594591135269858, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8516594591135269858, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8516594591135269858, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 8598736363425155725, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8598736363425155725, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8598736363425155725, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8598736363425155725, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8598736363425155725, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8598736363425155725, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 8605266563269758506, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8605266563269758506, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8605266563269758506, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8605266563269758506, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8605266563269758506, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8605266563269758506, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 8605266563269758506, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8605266563269758506, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 8615316628600618484, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8615316628600618484, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8615316628600618484, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8615316628600618484, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8615316628600618484, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8615316628600618484, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -32.5 objectReference: {fileID: 0} - - target: {fileID: 8680088375614889640, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8680088375614889640, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8680088375614889640, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8680088375614889640, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8680088375614889640, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8680088375614889640, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -486 objectReference: {fileID: 0} - - target: {fileID: 8687227926982764076, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8687227926982764076, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8687227926982764076, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8687227926982764076, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8687227926982764076, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8687227926982764076, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -42.5 objectReference: {fileID: 0} - - target: {fileID: 8690175769329235223, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8690175769329235223, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8690175769329235223, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8690175769329235223, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8690175769329235223, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8690175769329235223, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 8690175769329235223, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8690175769329235223, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 8702184620453700018, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8702184620453700018, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8702184620453700018, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8702184620453700018, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8702184620453700018, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8702184620453700018, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -302 objectReference: {fileID: 0} - - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 224 objectReference: {fileID: 0} - - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.y value: 38 objectReference: {fileID: 0} - - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 112.5 objectReference: {fileID: 0} - - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8737356546176888317, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -539 objectReference: {fileID: 0} - - target: {fileID: 8751093968873392275, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8751093968873392275, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8751093968873392275, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8751093968873392275, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8751093968873392275, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8751093968873392275, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 8783949143639092159, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8783949143639092159, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8783949143639092159, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8783949143639092159, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8783949143639092159, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8783949143639092159, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 8783949143639092159, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8783949143639092159, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 8809924185605691794, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8809924185605691794, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8809924185605691794, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8809924185605691794, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8809924185605691794, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8809924185605691794, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 8844462517833474130, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8844462517833474130, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_CullTransparentMesh value: 1 objectReference: {fileID: 0} - - target: {fileID: 8903677218956871069, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8903677218956871069, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8903677218956871069, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8903677218956871069, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8903677218956871069, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8903677218956871069, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 8958337316916194088, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8958337316916194088, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8958337316916194088, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8958337316916194088, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8958337316916194088, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8958337316916194088, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 64.5 objectReference: {fileID: 0} - - target: {fileID: 8958337316916194088, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8958337316916194088, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 8962731343690624656, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8962731343690624656, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8962731343690624656, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8962731343690624656, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8962731343690624656, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8962731343690624656, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 123.5 objectReference: {fileID: 0} - - target: {fileID: 8962731343690624656, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8962731343690624656, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -21 objectReference: {fileID: 0} - - target: {fileID: 8983220153540063109, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8983220153540063109, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8983220153540063109, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8983220153540063109, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8983220153540063109, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 8983220153540063109, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -94 objectReference: {fileID: 0} - - target: {fileID: 9035928253452349842, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9035928253452349842, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9035928253452349842, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9035928253452349842, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9035928253452349842, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9035928253452349842, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 9035928253452349842, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9035928253452349842, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 9041086693106963600, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9041086693106963600, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9041086693106963600, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9041086693106963600, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9041086693106963600, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9041086693106963600, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 20.5 objectReference: {fileID: 0} - - target: {fileID: 9041086693106963600, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9041086693106963600, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_SizeDelta.x value: 250 objectReference: {fileID: 0} - - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 125 objectReference: {fileID: 0} - - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9066302257242451626, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 9122132885086162329, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9122132885086162329, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_Value value: 1.0000008 objectReference: {fileID: 0} - - target: {fileID: 9124126768738734240, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9124126768738734240, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_Size value: 0.89719623 objectReference: {fileID: 0} - - target: {fileID: 9124126768738734240, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9124126768738734240, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_Value value: 1 objectReference: {fileID: 0} - - target: {fileID: 9140002754767807945, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9140002754767807945, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9140002754767807945, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9140002754767807945, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9140002754767807945, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9140002754767807945, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 9140002754767807945, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9140002754767807945, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} - - target: {fileID: 9165447478334623854, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9165447478334623854, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9165447478334623854, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9165447478334623854, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9165447478334623854, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9165447478334623854, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -336 objectReference: {fileID: 0} - - target: {fileID: 9193408330584138605, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9193408330584138605, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9193408330584138605, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9193408330584138605, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9193408330584138605, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9193408330584138605, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 126 objectReference: {fileID: 0} - - target: {fileID: 9193408330584138605, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9193408330584138605, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 9193991082770542908, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9193991082770542908, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9193991082770542908, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9193991082770542908, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9193991082770542908, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9193991082770542908, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 8 objectReference: {fileID: 0} - - target: {fileID: 9193991082770542908, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9193991082770542908, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 9214710806144903103, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9214710806144903103, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9214710806144903103, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9214710806144903103, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 9214710806144903103, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9214710806144903103, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.x value: 177 objectReference: {fileID: 0} - - target: {fileID: 9214710806144903103, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + - target: {fileID: 9214710806144903103, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} propertyPath: m_AnchoredPosition.y value: -10 objectReference: {fileID: 0} @@ -9122,14 +8182,12 @@ PrefabInstance: m_SourcePrefab: {fileID: 100100000, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} --- !u!1 &1768688999565580427 stripped GameObject: - m_CorrespondingSourceObject: {fileID: 4042465043493813398, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + m_CorrespondingSourceObject: {fileID: 4042465043493813398, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} m_PrefabInstance: {fileID: 2346971118752932893} m_PrefabAsset: {fileID: 0} --- !u!224 &1824198797412453037 stripped RectTransform: - m_CorrespondingSourceObject: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, - type: 3} + m_CorrespondingSourceObject: {fileID: 4162107705649557168, guid: 04ac259d13a0945ac87ddc5cac56785e, type: 3} m_PrefabInstance: {fileID: 2346971118752932893} m_PrefabAsset: {fileID: 0} --- !u!1001 &2808644653837158682 @@ -9140,373 +8198,299 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 5224021696837641672} m_Modifications: - - target: {fileID: 210366935220034040, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 210366935220034040, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 210366935220034040, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 210366935220034040, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 210366935220034040, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 210366935220034040, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.x value: 457.37 objectReference: {fileID: 0} - - target: {fileID: 513760574875140303, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 513760574875140303, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 513760574875140303, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 513760574875140303, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 513760574875140303, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 513760574875140303, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.x value: 323.13 objectReference: {fileID: 0} - - target: {fileID: 513760574875140303, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 513760574875140303, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 473.37 objectReference: {fileID: 0} - - target: {fileID: 1362750833803285249, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 1362750833803285249, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.x value: 133.37 objectReference: {fileID: 0} - - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.x value: 796.5 objectReference: {fileID: 0} - - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.y value: 30 objectReference: {fileID: 0} - - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 398.25 objectReference: {fileID: 0} - - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 2653587487447981162, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.y value: -15 objectReference: {fileID: 0} - - target: {fileID: 2967997487976718605, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 2967997487976718605, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: overlayContainer value: objectReference: {fileID: 7029756735420670719} - - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.x value: 93.37 objectReference: {fileID: 0} - - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 78.685 objectReference: {fileID: 0} - - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 4942844073237141754, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 5020629288057838675, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 5020629288057838675, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.x value: 828.5 objectReference: {fileID: 0} - - target: {fileID: 5020629288057838675, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 5020629288057838675, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.y value: 154.11 objectReference: {fileID: 0} - - target: {fileID: 6448674239935938532, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6448674239935938532, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6448674239935938532, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6448674239935938532, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6448674239935938532, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6448674239935938532, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 20.5 objectReference: {fileID: 0} - - target: {fileID: 6448674239935938532, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6448674239935938532, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_Pivot.x value: 0.5 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_Pivot.y value: 0.5 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_RootOrder value: 2 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.x value: 1 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalPosition.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalRotation.w value: 1 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalRotation.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalRotation.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalRotation.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalEulerAnglesHint.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalEulerAnglesHint.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.x value: 796.5 objectReference: {fileID: 0} - - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.y value: 82.11 objectReference: {fileID: 0} - - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 414.25 objectReference: {fileID: 0} - - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 6974553987836872862, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.y value: -57.055 objectReference: {fileID: 0} - - target: {fileID: 7572337814382127804, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7572337814382127804, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7572337814382127804, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7572337814382127804, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7572337814382127804, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7572337814382127804, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 64.5 objectReference: {fileID: 0} - - target: {fileID: 7572337814382127804, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7572337814382127804, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.x value: 484.25 objectReference: {fileID: 0} - - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_SizeDelta.y value: 20.11 objectReference: {fileID: 0} - - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 156.125 objectReference: {fileID: 0} - - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7750639364715422709, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.y value: -46 objectReference: {fileID: 0} - - target: {fileID: 7767635361911652193, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7767635361911652193, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_Name value: DeleteSequenceDialog objectReference: {fileID: 0} - - target: {fileID: 7767635361911652193, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 7767635361911652193, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_IsActive value: 0 objectReference: {fileID: 0} - - target: {fileID: 8355597630735644262, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 8355597630735644262, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8355597630735644262, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 8355597630735644262, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8355597630735644262, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 8355597630735644262, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 346 objectReference: {fileID: 0} - - target: {fileID: 8355597630735644262, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 8355597630735644262, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.y value: -118.11 objectReference: {fileID: 0} - - target: {fileID: 8559602234941710125, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 8559602234941710125, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8559602234941710125, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 8559602234941710125, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 8559602234941710125, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 8559602234941710125, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.x value: 18 objectReference: {fileID: 0} - - target: {fileID: 8559602234941710125, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 8559602234941710125, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: m_AnchoredPosition.y value: -20 objectReference: {fileID: 0} - - target: {fileID: 9089944729806318628, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + - target: {fileID: 9089944729806318628, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} propertyPath: overlayContainer value: objectReference: {fileID: 7029756735420670719} @@ -9517,14 +8501,12 @@ PrefabInstance: m_SourcePrefab: {fileID: 100100000, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} --- !u!1 &5563740331335366267 stripped GameObject: - m_CorrespondingSourceObject: {fileID: 7767635361911652193, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + m_CorrespondingSourceObject: {fileID: 7767635361911652193, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} m_PrefabInstance: {fileID: 2808644653837158682} m_PrefabAsset: {fileID: 0} --- !u!224 &8701351736407767869 stripped RectTransform: - m_CorrespondingSourceObject: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, - type: 3} + m_CorrespondingSourceObject: {fileID: 6790062799219569191, guid: b173f524aed5741e7bf7718478ebfae9, type: 3} m_PrefabInstance: {fileID: 2808644653837158682} m_PrefabAsset: {fileID: 0} --- !u!1001 &8127591394754714046 @@ -9535,148 +8517,119 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 8867301982941913259} m_Modifications: - - target: {fileID: 4199513441948227604, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4199513441948227604, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_Name value: CreateSequenceCard objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_Pivot.x value: 0.5 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_Pivot.y value: 0.5 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_RootOrder value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_AnchorMax.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_AnchorMax.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_AnchorMin.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_AnchorMin.y value: 1 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_SizeDelta.x value: 200 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_SizeDelta.y value: 100 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalPosition.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalPosition.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalPosition.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalRotation.w value: 1 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalRotation.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalRotation.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalRotation.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_AnchoredPosition.x value: 100 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_AnchoredPosition.y value: -50 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalEulerAnglesHint.x value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalEulerAnglesHint.y value: 0 objectReference: {fileID: 0} - - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.size value: 1 objectReference: {fileID: 0} - - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.data[0].m_Mode value: 1 objectReference: {fileID: 0} - - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.data[0].m_Target value: objectReference: {fileID: 1644905056975458796} - - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.data[0].m_CallState value: 2 objectReference: {fileID: 0} - - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.data[0].m_MethodName value: OnClick objectReference: {fileID: 0} - - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.data[0].m_TargetAssemblyTypeName value: RGCreateNewSequenceButton, RegressionGames objectReference: {fileID: 0} - - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - target: {fileID: 9035366199221417254, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} propertyPath: m_OnClick.m_PersistentCalls.m_Calls.Array.data[0].m_Arguments.m_ObjectArgumentAssemblyTypeName value: UnityEngine.Object, UnityEngine objectReference: {fileID: 0} @@ -9684,15 +8637,13 @@ PrefabInstance: m_RemovedGameObjects: [] m_AddedGameObjects: [] m_AddedComponents: - - targetCorrespondingSourceObject: {fileID: 4854958758869477337, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + - targetCorrespondingSourceObject: {fileID: 4854958758869477337, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} insertIndex: -1 addedObject: {fileID: 1644905056975458796} m_SourcePrefab: {fileID: 100100000, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} --- !u!1 &3723146531997908583 stripped GameObject: - m_CorrespondingSourceObject: {fileID: 4854958758869477337, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + m_CorrespondingSourceObject: {fileID: 4854958758869477337, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} m_PrefabInstance: {fileID: 8127591394754714046} m_PrefabAsset: {fileID: 0} --- !u!114 &1644905056975458796 @@ -9710,7 +8661,6 @@ MonoBehaviour: overlayContainer: {fileID: 7029756735420670719} --- !u!224 &5483322438096022206 stripped RectTransform: - m_CorrespondingSourceObject: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, - type: 3} + m_CorrespondingSourceObject: {fileID: 4383038157323776768, guid: 2a80ebd7f472840da84a654a4fa3d09e, type: 3} m_PrefabInstance: {fileID: 8127591394754714046} m_PrefabAsset: {fileID: 0} diff --git a/src/gg.regression.unity.bots/Runtime/Prefabs/RGOverlayCanvasV2.prefab b/src/gg.regression.unity.bots/Runtime/Prefabs/RGOverlayCanvasV2.prefab index 557b2cec..3509dd80 100644 --- a/src/gg.regression.unity.bots/Runtime/Prefabs/RGOverlayCanvasV2.prefab +++ b/src/gg.regression.unity.bots/Runtime/Prefabs/RGOverlayCanvasV2.prefab @@ -31,7 +31,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6927235806806861544} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -107,7 +106,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 130924262344969576} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -185,7 +183,6 @@ RectTransform: m_Children: - {fileID: 3141922717576822039} m_Father: {fileID: 6746351550061287240} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -310,7 +307,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1094668923711103164} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 1} m_AnchorMax: {x: 0.5, y: 1} @@ -447,7 +443,6 @@ RectTransform: m_Children: - {fileID: 7961718474796200068} m_Father: {fileID: 6746351550061287240} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -536,7 +531,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 47554025908246306} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.5} m_AnchorMax: {x: 0, y: 0.5} @@ -612,7 +606,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1452189883864539947} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -747,7 +740,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 47554025908246306} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -888,7 +880,6 @@ RectTransform: - {fileID: 2517803416891369262} - {fileID: 6648679281401812096} m_Father: {fileID: 5224021696837641672} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -963,7 +954,6 @@ RectTransform: m_Children: - {fileID: 1508444460829567942} m_Father: {fileID: 3393945995968011374} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1001,7 +991,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1775505273637563011} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -1081,7 +1070,6 @@ RectTransform: m_Children: - {fileID: 6986221192224276067} m_Father: {fileID: 7825146823198246843} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -1215,7 +1203,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -1370,7 +1357,6 @@ RectTransform: - {fileID: 4362780102445503521} - {fileID: 4911149214502031902} m_Father: {fileID: 1452189883864539947} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 0} @@ -1462,7 +1448,9 @@ Canvas: m_OverrideSorting: 1 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 25 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 30000 m_TargetDisplay: 0 @@ -1497,7 +1485,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1094668923711103164} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 0} m_AnchorMax: {x: 1, y: 0} @@ -1575,7 +1562,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -1728,7 +1714,6 @@ RectTransform: m_Children: - {fileID: 266832984541047860} m_Father: {fileID: 1090041802443136416} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -1862,7 +1847,6 @@ RectTransform: m_Children: - {fileID: 130924262344969576} m_Father: {fileID: 8326409972815191664} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1988,7 +1972,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 10 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -2143,7 +2126,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 8 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -2295,7 +2277,6 @@ RectTransform: m_Children: - {fileID: 1593904811034481552} m_Father: {fileID: 5849487789424347138} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0, y: 1} @@ -2371,7 +2352,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2163531068832325664} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -2447,7 +2427,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5849487789424347138} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -2529,7 +2508,6 @@ RectTransform: - {fileID: 4515592992782609972} - {fileID: 536196743177323916} m_Father: {fileID: 2530510026482189899} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.5} m_AnchorMax: {x: 1, y: 0.5} @@ -2617,7 +2595,6 @@ RectTransform: m_Children: - {fileID: 2530510026482189899} m_Father: {fileID: 8326409972815191664} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -2706,7 +2683,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2163531068832325664} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.5} m_AnchorMax: {x: 0, y: 0.5} @@ -2786,7 +2762,6 @@ RectTransform: - {fileID: 4334941368482101610} - {fileID: 3393945995968011374} m_Father: {fileID: 8587920458476045062} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 0} @@ -2878,7 +2853,9 @@ Canvas: m_OverrideSorting: 1 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 25 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 30000 m_TargetDisplay: 0 @@ -2913,7 +2890,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1094668923711103164} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.75} m_AnchorMax: {x: 0, y: 0.75} @@ -3049,7 +3025,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 11 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -3204,7 +3179,6 @@ RectTransform: m_Children: - {fileID: 1852812869902392873} m_Father: {fileID: 6648679281401812096} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -3293,7 +3267,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 3141922717576822039} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -3369,7 +3342,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -3446,7 +3418,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5630640976557725279} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -3627,7 +3598,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -3780,7 +3750,6 @@ RectTransform: m_Children: - {fileID: 3152240568156906031} m_Father: {fileID: 1094668923711103164} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -3912,7 +3881,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -4046,7 +4014,6 @@ RectTransform: m_Children: - {fileID: 2163531068832325664} m_Father: {fileID: 4362780102445503521} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -4085,7 +4052,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -4218,7 +4184,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6927235806806861544} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -4294,7 +4259,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6927235806806861544} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -4372,7 +4336,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090041802443136416} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -4523,7 +4486,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 47554025908246306} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -4599,7 +4561,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1452189883864539947} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 1} m_AnchorMax: {x: 0.5, y: 1} @@ -4734,7 +4695,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8587920458476045062} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 0.5} m_AnchorMax: {x: 1, y: 0.5} @@ -4815,7 +4775,6 @@ RectTransform: - {fileID: 6746351550061287240} - {fileID: 2953817864760835568} m_Father: {fileID: 4901750364568730873} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0, y: 1} @@ -4948,7 +4907,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2943620597921803164} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -5045,7 +5003,6 @@ RectTransform: - {fileID: 5248371518487353189} - {fileID: 5849487789424347138} m_Father: {fileID: 7825146823198246843} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0.5, y: 1} @@ -5181,7 +5138,6 @@ RectTransform: - {fileID: 8587920458476045062} - {fileID: 7825146823198246843} m_Father: {fileID: 1094668923711103164} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.8} m_AnchorMax: {x: 1, y: 0.8} @@ -5283,7 +5239,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8587920458476045062} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 1} m_AnchorMax: {x: 0.5, y: 1} @@ -5418,7 +5373,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8587920458476045062} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -5565,7 +5519,6 @@ RectTransform: - {fileID: 1090041802443136416} - {fileID: 1094668923711103164} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -5589,7 +5542,9 @@ Canvas: m_OverrideSorting: 0 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 25 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 29999 m_TargetDisplay: 0 @@ -5665,7 +5620,8 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: recordingMinFPS: 0 - stateRecordingsDirectory: + minimizeRecordingCriteria: 1 + minimizeRecordingMouseMovements: 0 --- !u!114 &4232611005985544413 MonoBehaviour: m_ObjectHideFlags: 0 @@ -5783,7 +5739,6 @@ RectTransform: m_Children: - {fileID: 985415874430226196} m_Father: {fileID: 1090041802443136416} - m_RootOrder: 9 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -5860,7 +5815,6 @@ RectTransform: - {fileID: 5061430410649258493} - {fileID: 5716806768315515097} m_Father: {fileID: 1090041802443136416} - m_RootOrder: 12 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -5898,7 +5852,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6128659421540762060} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -6035,7 +5988,6 @@ RectTransform: - {fileID: 1276465453068283002} - {fileID: 1092592833771759674} m_Father: {fileID: 7961718474796200068} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.5} m_AnchorMax: {x: 1, y: 0.5} @@ -6121,7 +6073,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2065522685927686854} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -6261,7 +6212,6 @@ RectTransform: - {fileID: 8326409972815191664} - {fileID: 2387824098431642500} m_Father: {fileID: 4901750364568730873} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0, y: 1} @@ -6393,7 +6343,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2163531068832325664} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -6529,7 +6478,6 @@ RectTransform: m_Children: - {fileID: 1775505273637563011} m_Father: {fileID: 1094668923711103164} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 0.7} @@ -6605,7 +6553,6 @@ RectTransform: - {fileID: 6128659421540762060} - {fileID: 2065522685927686854} m_Father: {fileID: 4901750364568730873} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0, y: 1} @@ -6642,7 +6589,6 @@ RectTransform: m_Children: - {fileID: 7980167310545832456} m_Father: {fileID: 4911149214502031902} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -6680,7 +6626,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1452189883864539947} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 0.5} m_AnchorMax: {x: 1, y: 0.5} @@ -6755,7 +6700,6 @@ RectTransform: m_Children: - {fileID: 47554025908246306} m_Father: {fileID: 4334941368482101610} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -6794,7 +6738,6 @@ RectTransform: - {fileID: 6280591337693800581} - {fileID: 5080053995929809686} m_Father: {fileID: 6128659421540762060} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -6892,7 +6835,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6280591337693800581} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -6968,7 +6910,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6033063948165999297} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -7084,6 +7025,7 @@ GameObject: - component: {fileID: 1784025199367272708} - component: {fileID: 5893801167117800152} - component: {fileID: 5122435472915985299} + - component: {fileID: 5923157588599890469} m_Layer: 5 m_Name: Recording Toolbar m_TagString: Untagged @@ -7117,7 +7059,6 @@ RectTransform: - {fileID: 5789129086972528656} - {fileID: 6927235806806861544} m_Father: {fileID: 5224021696837641672} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 0} m_AnchorMax: {x: 1, y: 0} @@ -7178,3 +7119,15 @@ MonoBehaviour: mouseTransform: {fileID: 6927235806806861544} cursor: {fileID: 6509774744981593005} clickHighlight: {fileID: 4177226886652366683} +--- !u!114 &5923157588599890469 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9073394237553129601} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ad058943c1ca443a9fe70ad904bdc46b, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs new file mode 100644 index 00000000..42c8e103 --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using RegressionGames.StateRecorder.BotSegments.Models; +using RegressionGames.StateRecorder.Models; +using UnityEngine; + +namespace StateRecorder.BotSegments +{ + public class ActionExplorationDriver : MonoBehaviour + { + + // normally the first thing we do is retry the previous action + private IBotActionData _previouslyCompletedAction = null; + + public bool IsExploring { get; private set;} + + private IBotActionData _activeAction = null; + + public void StartExploring(IBotActionData previousAction) + { + _previouslyCompletedAction = previousAction; + IsExploring = true; + } + + public void PerformExploratoryAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) + { + error = null; + if (!IsExploring) + { + return; + } + + if (_previouslyCompletedAction != null) + { + _previouslyCompletedAction.ReplayReset(); + + _activeAction = _previouslyCompletedAction; + _previouslyCompletedAction = null; + } + + if (_activeAction == null) + { + // TODO: Implement hooks to exploration algorithms here + } + + if (_activeAction != null) + { + if (!_activeAction.IsCompleted()) + { + _activeAction.StartAction(segmentNumber, currentTransforms, currentEntities); + _activeAction.ProcessAction(segmentNumber, currentTransforms, currentEntities, out error); + } + else + { + _activeAction = null; + } + } + + } + + public void StopExploring(int segmentNumber) + { + IsExploring = false; + + if (_activeAction != null) + { + _activeAction.AbortAction(segmentNumber); + _activeAction = null; + } + } + } +} diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs.meta new file mode 100644 index 00000000..a8ff4ca2 --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ad058943c1ca443a9fe70ad904bdc46b +timeCreated: 1733343221 \ No newline at end of file diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs index 3c9bbcb1..8e8b7e68 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs @@ -3,7 +3,9 @@ using System.IO; using Newtonsoft.Json; using RegressionGames.StateRecorder.BotSegments.Models; +using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments; using RegressionGames.StateRecorder.Models; +using StateRecorder.BotSegments; using StateRecorder.BotSegments.Models; #if UNITY_EDITOR using UnityEditor; @@ -56,8 +58,11 @@ public class BotSegmentsPlaybackController : MonoBehaviour private ScreenRecorder _screenRecorder; + private ActionExplorationDriver _explorationDriver; + private void Start() { + _explorationDriver = GetComponent(); KeyboardEventSender.Initialize(); SceneManager.sceneLoaded += OnSceneLoad; SceneManager.sceneUnloaded += OnSceneUnload; @@ -394,7 +399,7 @@ public void Update() private float _lastTimeLoggedKeyFrameConditions = 0; - private const int LOG_ERROR_INTERVAL = 3; + private const int ACTION_WARNING_INTERVAL = 3; // seconds private void LogPlaybackWarning(string loggedMessage) { @@ -409,6 +414,8 @@ private void LogPlaybackWarning(string loggedMessage) } } + private IBotActionData _lastSuccessfulAction = null; + private void EvaluateBotSegments() { var now = Time.unscaledTime; @@ -455,19 +462,34 @@ private void EvaluateBotSegments() BotSegment firstActionSegment = _nextBotSegments[0]; try { + // allow the main action to retry between every exploratory action var didAction = firstActionSegment.ProcessAction(transformStatuses, entityStatuses, out var error); // only log this if we're really stuck on it if (error == null && didAction) { + _explorationDriver.StopExploring(firstActionSegment.Replay_SegmentNumber); // for every non error action, reset the timer _lastTimeLoggedKeyFrameConditions = now; FindObjectOfType()?.SetKeyFrameWarningText(null); } - if (error != null && _lastTimeLoggedKeyFrameConditions < now - LOG_ERROR_INTERVAL) + if (error != null) { - var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\r\n" + error; - LogPlaybackWarning(loggedMessage); + if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) + { + if (firstActionSegment.botAction?.data is IKeyMomentExploration) + { + var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\r\n" + error + "\r\nStarting exploratory actions..."; + LogPlaybackWarning(loggedMessage); + _explorationDriver.StartExploring(_lastSuccessfulAction); + } + else + { + var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\r\n" + error; + LogPlaybackWarning(loggedMessage); + } + } + _explorationDriver.PerformExploratoryAction(firstActionSegment.Replay_SegmentNumber, transformStatuses, entityStatuses, out var explorationError); } } catch (Exception ex) @@ -511,8 +533,8 @@ private void EvaluateBotSegments() matchedThisUpdate = true; } - // wait 10 seconds between logging this as some actions take quite a while - if (nextBotSegment.Replay_ActionStarted && !nextBotSegment.Replay_ActionCompleted && _lastTimeLoggedKeyFrameConditions < now - LOG_ERROR_INTERVAL) + // wait ACTION_WARNING_INTERVAL seconds between logging this as some actions take quite a while + if (nextBotSegment.Replay_ActionStarted && !nextBotSegment.Replay_ActionCompleted && _lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) { _lastTimeLoggedKeyFrameConditions = now; var loggedMessage = $"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - Waiting for actions to complete"; @@ -523,6 +545,8 @@ private void EvaluateBotSegments() if (nextBotSegment.Replay_Matched && nextBotSegment.Replay_ActionStarted && nextBotSegment.Replay_ActionCompleted) { + _lastSuccessfulAction = nextBotSegment.botAction?.data; + _lastTimeLoggedKeyFrameConditions = now; RGDebug.LogInfo($"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - DONE - Criteria Matched && Action Completed - {nextBotSegment.name ?? nextBotSegment.resourcePath} - {nextBotSegment.description}"); //Process the inputs from that bot segment if necessary @@ -536,8 +560,8 @@ private void EvaluateBotSegments() } else { - // only log this every 10 seconds for the first key frame being evaluated after its actions complete - if (nextBotSegmentIndex == 0 && nextBotSegment.Replay_ActionCompleted && _lastTimeLoggedKeyFrameConditions < now - LOG_ERROR_INTERVAL) + // only log this every ACTION_WARNING_INTERVAL seconds for the first key frame being evaluated after its actions complete + if (nextBotSegmentIndex == 0 && nextBotSegment.Replay_ActionCompleted && _lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) { var warningText = KeyFrameEvaluator.Evaluator.GetUnmatchedCriteria(); if (warningText != null) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 8f2a4690..7efbeebb 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -54,7 +54,7 @@ public static List TokenizeObjectPath(string path) * Data for clicking on a key moment object in the frame */ [Serializable] - public class KeyMomentMouseActionData : IBotActionData + public class KeyMomentMouseActionData : IBotActionData, IKeyMomentExploration { // api version for this object, update if object format changes public int apiVersion = SdkApiVersion.VERSION_28; @@ -84,6 +84,11 @@ public void ReplayReset() _isDoneWaitOneFrame = false; } + public void ActivateExploration() + { + // start the exploration process + } + public void StartAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities) { if (!_isStopped) @@ -389,14 +394,14 @@ public bool ProcessAction(int segmentNumber, Dictionary curr else { // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse action object found for paths:\n{string.Join("\n", preConditionNormalizedPaths.Select(a => a.path))}\n Exploring to find a match ..."; + error = $"No valid mouse action object found for paths:\n{string.Join("\n", preConditionNormalizedPaths.Select(a => a.path))}"; return false; } } else { // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse action object found for path:\n{preConditionNormalizedPaths[0].path}\n Exploring to find a match ..."; + error = $"No valid mouse action object found for path:\n{preConditionNormalizedPaths[0].path}"; return false; } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs new file mode 100644 index 00000000..a4b2d994 --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs @@ -0,0 +1,6 @@ +namespace RegressionGames.StateRecorder.BotSegments.Models.KeyMoments +{ + public interface IKeyMomentExploration + { + } +} diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs.meta new file mode 100644 index 00000000..6a762cb7 --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5be61c5ff7074167a32f9367fd59252e +timeCreated: 1733342460 \ No newline at end of file From b41c4d0aef530cc90b6261e531cdbb755097dfe7 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Thu, 5 Dec 2024 08:57:15 -0500 Subject: [PATCH 02/31] Copy keymoments as sequence to project for easier replay --- .../Scripts/RGOverlay/RGSequenceEditor.cs | 14 +++--- .../Scripts/RGOverlay/RGSequenceManager.cs | 17 +++---- .../BotSegments/BotSegmentDirectoryParser.cs | 9 ++-- .../Scripts/StateRecorder/ScreenRecorder.cs | 48 +++++++++++++------ 4 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/RGOverlay/RGSequenceEditor.cs b/src/gg.regression.unity.bots/Runtime/Scripts/RGOverlay/RGSequenceEditor.cs index e3684c4e..bcd0900b 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/RGOverlay/RGSequenceEditor.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/RGOverlay/RGSequenceEditor.cs @@ -43,13 +43,13 @@ public class RGSequenceEditor : MonoBehaviour // shown when creating a new Sequence public GameObject createInstructionText; - + // shown when updating an existing Sequence public GameObject updateInstructionText; - + // shown when updating an existing Sequence in a build public GameObject updateOverrideInstructionText; - + public RGDropZone _dropZone; private IList _segmentEntries; @@ -79,9 +79,9 @@ public void Initialize(bool makingACopy, string existingResourcePath, string exi DescriptionInput.onValueChanged.AddListener(OnDescriptionInputChange); _dropZone = DropZonePrefab.GetComponent(); - // set indicator that this sequence is being overriden by a local file, within a build + // set indicator that this sequence is being overriden by a local file, within a build overrideIndicator.gameObject.SetActive(isOverride); - + _makingACopy = makingACopy; // if the Available Segment List cannot be found, we will try to find it somewhere in the scene @@ -127,7 +127,7 @@ public void Initialize(bool makingACopy, string existingResourcePath, string exi CurrentSequence = new BotSequence(); titleComponent.text = "Create Sequence"; } - + // set the instruction text if (isOverride || (!Application.isEditor && isBeingEdited)) { @@ -177,7 +177,7 @@ public static bool IsRecordingSequencePath(string sequencePath) { return false; } - return sequencePath.EndsWith("/"+ScreenRecorder.RecordingPathName); + return sequencePath.Contains("/"+ScreenRecorder.LatestRecordingPathName); } /** diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/RGOverlay/RGSequenceManager.cs b/src/gg.regression.unity.bots/Runtime/Scripts/RGOverlay/RGSequenceManager.cs index 857725dc..981de381 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/RGOverlay/RGSequenceManager.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/RGOverlay/RGSequenceManager.cs @@ -34,9 +34,9 @@ public class RGSequenceManager : MonoBehaviour public GameObject sequenceEditor; public GameObject deleteSequenceDialog; - + public Button reloadButton; - + private static RGSequenceManager _this; private ReplayToolbarManager _replayToolbarManager; @@ -102,7 +102,7 @@ public void Start() { UpscaleUI(); } - + _replayToolbarManager = FindObjectOfType(); // load our assets and show the Sequences tab content @@ -159,7 +159,7 @@ public void SetMobileView() { UpscaleUI(); reloadButton.gameObject.SetActive(false); - + // the create sequence button is always the first child in the sequences panel var createSequenceButton = sequencesPanel.transform.GetChild(0); createSequenceButton.gameObject.SetActive(false); @@ -307,10 +307,11 @@ public void InstantiateSequences(IDictionary seque // ensure we aren't appending new sequences on to the previous ones ClearExistingSequences(); - var latestRecording = sequences.FirstOrDefault(a => a.Key!= null && a.Key.EndsWith("/" + ScreenRecorder.RecordingPathName)); - // make the latest recording the first child - if (latestRecording.Key != null) + var latestRecordings = sequences.Where(a => a.Key!= null && a.Key.Contains("/" + ScreenRecorder.LatestRecordingPathName)).ToList(); + // sort so that latest recording is before latest key moment recording + latestRecordings = latestRecordings.OrderBy(a => a.Key.Length).ToList(); + foreach (var latestRecording in latestRecordings) { var resourcePath = latestRecording.Key; var sequenceInfo = latestRecording.Value; @@ -321,7 +322,7 @@ public void InstantiateSequences(IDictionary seque foreach (var sequenceKVPair in sequences) { var resourcePath = sequenceKVPair.Key; - if (!resourcePath.EndsWith("/" + ScreenRecorder.RecordingPathName)) + if (!resourcePath.Contains("/" + ScreenRecorder.LatestRecordingPathName)) { var sequenceInfo = sequenceKVPair.Value; InstantiateSequence(resourcePath, sequenceInfo); diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentDirectoryParser.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentDirectoryParser.cs index 6bd2209b..87afcea9 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentDirectoryParser.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentDirectoryParser.cs @@ -68,17 +68,16 @@ public static List ParseBotSegmentResourcePath(string resourcePath, /** * Sort json files FROM THE SAME DIRECTORY numerically if possible, otherwise Lexicographically */ - public static List OrderJsonFiles(IEnumerable jsonFiles) + public static List OrderJsonFiles(string[] jsonFiles) { - RGDebug.LogInfo($"OrderJsonFiles - Input File List - {string.Join("\n", jsonFiles)}"); + RGDebug.LogInfo($"OrderJsonFiles - Input File List [{jsonFiles.Length}] - {string.Join(", ", jsonFiles)}"); Exception lambdaException = null; // sort by numeric value of entries (not string comparison of filenames) .. normalize to front slashes - jsonFiles = jsonFiles.Where(e=>e.EndsWith(".json")).Select(e =>e = e.Replace('\\', '/')); - List entries; + var entries = jsonFiles.Where(e=>e.EndsWith(".json")).Select(e =>e = e.Replace('\\', '/')).ToList(); try { // try to order the files as numbered file names - entries = jsonFiles.OrderBy(e => + entries = entries.OrderBy(e => { if (lambdaException != null) { diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs index 6b4a694f..df292317 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs @@ -57,7 +57,8 @@ public TickDataToWriteToDisk(string directoryPrefix, long tickNumber, byte[] bot public class ScreenRecorder : MonoBehaviour { - public static readonly string RecordingPathName = "Latest_Recording"; + public static readonly string LatestRecordingPathName = "Latest_Recording"; + public static readonly string LatestRecordingKeyMomentsPathName = LatestRecordingPathName + "_KeyMoments"; [Tooltip("Minimum FPS at which to capture frames if you desire more granularity in recordings. Key frames may still be recorded more frequently than this. <= 0 will only record key frames")] public int recordingMinFPS; @@ -178,6 +179,7 @@ private async Task HandleEndRecording(long tickCount, long loggedErrors, string dataDirectoryPrefix, string botSegmentsDirectoryPrefix, + string keyMomentsDirectoryPrefix, string screenshotsDirectoryPrefix, string codeCoverageMetadataPath, string actionCoverageMetadataPath, @@ -260,6 +262,14 @@ private async Task HandleEndRecording(long tickCount, RGDebug.LogInfo($"Finished zipping replay to file: {logsDirectoryPrefix}.zip"); }); + var zipTask5 = Task.Run(() => + { + // Then save the key moments separately + RGDebug.LogInfo($"Zipping key_moments recording replay to file: {keyMomentsDirectoryPrefix}.zip"); + ZipFile.CreateFromDirectory(keyMomentsDirectoryPrefix, keyMomentsDirectoryPrefix + ".zip"); + RGDebug.LogInfo($"Finished zipping replay to file: {keyMomentsDirectoryPrefix}.zip"); + }); + // Finally, we also save a thumbnail, by choosing the middle file in the screenshots var screenshotFiles = Directory.GetFiles(screenshotsDirectoryPrefix); if (screenshotFiles.Length > 0) @@ -272,13 +282,14 @@ private async Task HandleEndRecording(long tickCount, RGActionRuntimeCoverageAnalysis.Reset(); // wait for the zip tasks to finish - Task.WaitAll(zipTask1, zipTask2, zipTask3, zipTask4); + Task.WaitAll(zipTask1, zipTask2, zipTask3, zipTask4, zipTask5); if (!wasReplay) { - // Copy the most recent recording into the user's project if running in the editor , or their persistent data path if running in production runtime + // Copy the most recent recording into the user's project if running in the editor, or their persistent data path if running in production runtime // do NOT copy if this was a replay - await MoveSegmentsToProject(botSegmentsDirectoryPrefix); + await MoveSegmentsToProject(botSegmentsDirectoryPrefix, LatestRecordingPathName); + await MoveSegmentsToProject(keyMomentsDirectoryPrefix, LatestRecordingKeyMomentsPathName); } #if UNITY_EDITOR @@ -309,6 +320,10 @@ private async Task HandleEndRecording(long tickCount, { Directory.Delete(botSegmentsDirectoryPrefix, true); } + if (Directory.Exists(keyMomentsDirectoryPrefix)) + { + Directory.Delete(keyMomentsDirectoryPrefix, true); + } if (Directory.Exists(screenshotsDirectoryPrefix)) { Directory.Delete(screenshotsDirectoryPrefix, true); @@ -325,28 +340,30 @@ private async Task HandleEndRecording(long tickCount, _tokenSource = null; } - private async Task MoveSegmentsToProject(string botSegmentsDirectoryPrefix) + private async Task MoveSegmentsToProject(string sourceDirectoryPrefix, string recordingPathName) { // Get all the file paths normalized to / // Note: Ensure that these files aren't loaded lazily (don't use Directory.EnumerateFiles) // as the folder gets moved later down this function and lazy loading will not find the files. - var segmentFiles = Directory.GetFiles(botSegmentsDirectoryPrefix) + var segmentFiles = Directory.GetFiles(sourceDirectoryPrefix) .Where(a=>a.EndsWith(".json")) .Select(a=>a.Replace('\\','/')) .Select(a=>a.Substring(a.LastIndexOf('/')+1)); // Order numerically instead of alphanumerically to ensure 2.json is before 10.json. - segmentFiles = BotSegmentDirectoryParser.OrderJsonFiles(segmentFiles); + segmentFiles = BotSegmentDirectoryParser.OrderJsonFiles(segmentFiles.ToArray()); string segmentResourceDirectory = null; string sequenceJsonPath = null; + + #if UNITY_EDITOR - segmentResourceDirectory = "Assets/RegressionGames/Resources/BotSegments/" + RecordingPathName; - sequenceJsonPath = "Assets/RegressionGames/Resources/BotSequences/" + RecordingPathName + ".json"; + segmentResourceDirectory = "Assets/RegressionGames/Resources/BotSegments/" + recordingPathName; + sequenceJsonPath = "Assets/RegressionGames/Resources/BotSequences/" + recordingPathName + ".json"; #else // Production runtime should write to persistent data path - segmentResourceDirectory = Application.persistentDataPath + "/RegressionGames/Resources/BotSegments/" + RecordingPathName; - sequenceJsonPath = Application.persistentDataPath + "/RegressionGames/Resources/BotSequences/" + RecordingPathName + ".json"; + segmentResourceDirectory = Application.persistentDataPath + "/RegressionGames/Resources/BotSegments/" + recordingPathName; + sequenceJsonPath = Application.persistentDataPath + "/RegressionGames/Resources/BotSequences/" + recordingPathName + ".json"; #endif try { @@ -371,17 +388,17 @@ private async Task MoveSegmentsToProject(string botSegmentsDirectoryPrefix) // ReSharper disable once PossibleMultipleEnumeration foreach (var segmentFile in segmentFiles) { - var sourcePath = botSegmentsDirectoryPrefix + "/" + segmentFile; + var sourcePath = sourceDirectoryPrefix + "/" + segmentFile; var destinationPath = segmentResourceDirectory + "/" + segmentFile; File.Copy(sourcePath, destinationPath); } // Then delete the original directory - Directory.Delete(botSegmentsDirectoryPrefix, true); + Directory.Delete(sourceDirectoryPrefix, true); } else { // move the directory (this also deletes the source directory) - Directory.Move(botSegmentsDirectoryPrefix, segmentResourceDirectory); + Directory.Move(sourceDirectoryPrefix, segmentResourceDirectory); } } @@ -406,7 +423,7 @@ private async Task MoveSegmentsToProject(string botSegmentsDirectoryPrefix) var botSequence = new BotSequence() { - name = "Latest Recording", + name = recordingPathName.Replace('_', ' ').Replace('-', ' '), description = "Note: This sequence was generated when recording a gameplay session and should not be modified. Creating a new recording will overwrite this sequence.", segments = sequenceEntries }; @@ -818,6 +835,7 @@ private void StopRecordingCleanupHelper(bool wasRecording, bool wasReplay) loggedErrors, _currentGameplaySessionDataDirectoryPrefix, _currentGameplaySessionBotSegmentsDirectoryPrefix, + _currentGameplaySessionKeyMomentsDirectoryPrefix, _currentGameplaySessionScreenshotsDirectoryPrefix, _currentGameplaySessionCodeCoverageMetadataPath, _currentGameplaySessionActionCoverageMetadataPath, From 2e57856f092b9285e7582658c4ea94de8432fac6 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Thu, 5 Dec 2024 09:12:59 -0500 Subject: [PATCH 03/31] Minimize the mouse data captured for key moments --- .../BotSegments/Models/BotAction.cs | 21 ++++++- .../BotSegments/Models/BotSegment.cs | 44 +++++++++++++- .../BotActions/KeyMomentMouseActionData.cs | 20 ++++++- .../IKeyMomentStringBuilderWriteable.cs | 22 +++++++ .../IKeyMomentStringBuilderWriteable.cs.meta | 3 + .../MouseInputActionDataJsonConverter.cs | 21 +++++++ .../MouseInputActionDataJsonConverter.cs.meta | 3 + .../Models/MouseInputActionData.cs | 59 +++++++++++++------ .../Scripts/StateRecorder/ScreenRecorder.cs | 2 +- 9 files changed, 171 insertions(+), 24 deletions(-) create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/IKeyMomentStringBuilderWriteable.cs create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/IKeyMomentStringBuilderWriteable.cs.meta create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/JsonConverters/MouseInputActionDataJsonConverter.cs create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/JsonConverters/MouseInputActionDataJsonConverter.cs.meta diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotAction.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotAction.cs index 79d581c2..1587dc8d 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotAction.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotAction.cs @@ -7,7 +7,7 @@ namespace RegressionGames.StateRecorder.BotSegments.Models { [Serializable] - public class BotAction + public class BotAction : IStringBuilderWriteable, IKeyMomentStringBuilderWriteable { // api version for this top level schema, update if we add/remove/change fields here public int apiVersion = SdkApiVersion.VERSION_1; @@ -82,5 +82,24 @@ public void WriteToStringBuilder(StringBuilder stringBuilder) data.WriteToStringBuilder(stringBuilder); stringBuilder.Append("}"); } + + public void WriteKeyMomentToStringBuilder(StringBuilder stringBuilder) + { + stringBuilder.Append("{\"type\":"); + StringJsonConverter.WriteToStringBuilder(stringBuilder, type.ToString()); + stringBuilder.Append(",\"apiVersion\":"); + IntJsonConverter.WriteToStringBuilder(stringBuilder, apiVersion); + stringBuilder.Append(",\"data\":"); + if (data is IKeyMomentStringBuilderWriteable keyMomentStringBuilderWriteable) + { + keyMomentStringBuilderWriteable.WriteKeyMomentToStringBuilder(stringBuilder); + } + else + { + data.WriteToStringBuilder(stringBuilder); + } + stringBuilder.Append("}"); + } + } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegment.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegment.cs index 43bfa4e4..19cfef5f 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegment.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegment.cs @@ -17,7 +17,7 @@ namespace RegressionGames.StateRecorder.BotSegments.Models { [Serializable] - public class BotSegment + public class BotSegment : IStringBuilderWriteable, IKeyMomentStringBuilderWriteable { // re-usable and large enough to fit all sizes @@ -40,7 +40,7 @@ public class BotSegment // NOT WRITTEN TO JSON - Populated at load time public bool isOverride; - + /** * Description for this bot segment. Used for naming on the UI. */ @@ -232,6 +232,46 @@ private bool HasTransientCriteriaHelper(List criteriaList) return false; } + public string ToKeyMomentJsonString() + { + _stringBuilder.Value.Clear(); + WriteKeyMomentToStringBuilder(_stringBuilder.Value); + return _stringBuilder.Value.ToString(); + } + + public void WriteKeyMomentToStringBuilder(StringBuilder stringBuilder) + { + stringBuilder.Append("{\n\"name\":"); + StringJsonConverter.WriteToStringBuilder(stringBuilder, name ); + stringBuilder.Append(",\n\"description\":"); + StringJsonConverter.WriteToStringBuilder(stringBuilder, description ); + stringBuilder.Append(",\n\"sessionId\":"); + StringJsonConverter.WriteToStringBuilder(stringBuilder, sessionId); + stringBuilder.Append(",\n\"apiVersion\":"); + IntJsonConverter.WriteToStringBuilder(stringBuilder, apiVersion); + stringBuilder.Append(",\n\"endCriteria\":[\n"); + var endCriteriaLength = endCriteria.Count; + for (var i = 0; i < endCriteriaLength; i++) + { + var criteria = endCriteria[i]; + criteria.WriteToStringBuilder(stringBuilder); + if (i + 1 < endCriteriaLength) + { + stringBuilder.Append(",\n"); + } + } + stringBuilder.Append("\n],\n\"botAction\":"); + if (botAction != null) + { + botAction.WriteKeyMomentToStringBuilder(stringBuilder); + } + else + { + stringBuilder.Append("null"); + } + stringBuilder.Append("}"); + } + public string ToJsonString() { _stringBuilder.Value.Clear(); diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 7efbeebb..1d48882d 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -54,7 +54,7 @@ public static List TokenizeObjectPath(string path) * Data for clicking on a key moment object in the frame */ [Serializable] - public class KeyMomentMouseActionData : IBotActionData, IKeyMomentExploration + public class KeyMomentMouseActionData : IBotActionData, IKeyMomentExploration, IStringBuilderWriteable, IKeyMomentStringBuilderWriteable { // api version for this object, update if object format changes public int apiVersion = SdkApiVersion.VERSION_28; @@ -470,6 +470,24 @@ public void WriteToStringBuilder(StringBuilder stringBuilder) stringBuilder.Append("\n]}"); } + public void WriteKeyMomentToStringBuilder(StringBuilder stringBuilder) + { + stringBuilder.Append("{\"apiVersion\":"); + IntJsonConverter.WriteToStringBuilder(stringBuilder, apiVersion); + stringBuilder.Append(",\"mouseActions\":[\n"); + var mouseActionsCount = mouseActions.Count; + for (var i = 0; i < mouseActionsCount; i++) + { + var mouseAction = mouseActions[i]; + mouseAction.WriteKeyMomentToStringBuilder(stringBuilder); + if (i + 1 < mouseActionsCount) + { + stringBuilder.Append(",\n"); + } + } + stringBuilder.Append("\n]}"); + } + public int EffectiveApiVersion() { return apiVersion; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/IKeyMomentStringBuilderWriteable.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/IKeyMomentStringBuilderWriteable.cs new file mode 100644 index 00000000..cbc03318 --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/IKeyMomentStringBuilderWriteable.cs @@ -0,0 +1,22 @@ +using System.Text; +using System.Threading; + +namespace RegressionGames.StateRecorder +{ + public interface IKeyMomentStringBuilderWriteable + { + + static readonly ThreadLocal StringBuilder = new (() => new()); + + public void WriteKeyMomentToStringBuilder(StringBuilder stringBuilder); + + public string ToKeyMomentJsonString() + { + var sb = StringBuilder.Value; + sb.Clear(); + WriteKeyMomentToStringBuilder(sb); + return sb.ToString(); + } + } + +} diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/IKeyMomentStringBuilderWriteable.cs.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/IKeyMomentStringBuilderWriteable.cs.meta new file mode 100644 index 00000000..d3499bbb --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/IKeyMomentStringBuilderWriteable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2bc7a34c795b4541ac601166cade59f8 +timeCreated: 1733407323 \ No newline at end of file diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/JsonConverters/MouseInputActionDataJsonConverter.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/JsonConverters/MouseInputActionDataJsonConverter.cs new file mode 100644 index 00000000..fbcf58b2 --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/JsonConverters/MouseInputActionDataJsonConverter.cs @@ -0,0 +1,21 @@ +using System; +using Newtonsoft.Json; +using RegressionGames.StateRecorder.Models; + +namespace RegressionGames.StateRecorder.JsonConverters +{ + public class MouseInputActionDataJsonConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, MouseInputActionData value, JsonSerializer serializer) + { + writer.WriteRawValue(value.ToJsonString()); + } + + public override bool CanRead => false; + + public override MouseInputActionData ReadJson(JsonReader reader, Type objectType, MouseInputActionData existingValue, bool hasExistingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/JsonConverters/MouseInputActionDataJsonConverter.cs.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/JsonConverters/MouseInputActionDataJsonConverter.cs.meta new file mode 100644 index 00000000..ee0af51a --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/JsonConverters/MouseInputActionDataJsonConverter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3d48b7e0a1ba41d9aa84675511ab90d3 +timeCreated: 1733407245 \ No newline at end of file diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs index 4c48350b..921048a8 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs @@ -10,16 +10,16 @@ namespace RegressionGames.StateRecorder.Models { [Serializable] [SuppressMessage("ReSharper", "InconsistentNaming")] - public class MouseInputActionData + public class MouseInputActionData : IStringBuilderWriteable, IKeyMomentStringBuilderWriteable { // version of this schema, update this if fields change public int apiVersion = SdkApiVersion.VERSION_1; public double startTime; - public Vector2Int screenSize; + public Vector2Int screenSize = Vector2Int.zero; - public Vector2Int position; + public Vector2Int position = Vector2Int.zero; public Vector3? worldPosition; @@ -128,13 +128,49 @@ public void WriteToStringBuilder(StringBuilder stringBuilder) stringBuilder.Append("]}"); } - internal string ToJsonString() + public string ToJsonString() { _stringBuilder.Value.Clear(); WriteToStringBuilder(_stringBuilder.Value); return _stringBuilder.Value.ToString(); } + public void WriteKeyMomentToStringBuilder(StringBuilder stringBuilder) + { + stringBuilder.Append("{\"apiVersion\":"); + IntJsonConverter.WriteToStringBuilder(stringBuilder, apiVersion); + stringBuilder.Append(",\"leftButton\":"); + BooleanJsonConverter.WriteToStringBuilder(stringBuilder,leftButton); + stringBuilder.Append(",\"middleButton\":"); + BooleanJsonConverter.WriteToStringBuilder(stringBuilder,middleButton); + stringBuilder.Append(",\"rightButton\":"); + BooleanJsonConverter.WriteToStringBuilder(stringBuilder,rightButton); + stringBuilder.Append(",\"forwardButton\":"); + BooleanJsonConverter.WriteToStringBuilder(stringBuilder,forwardButton); + stringBuilder.Append(",\"backButton\":"); + BooleanJsonConverter.WriteToStringBuilder(stringBuilder,backButton); + stringBuilder.Append(",\"scroll\":"); + Vector2JsonConverter.WriteToStringBuilder(stringBuilder, scroll); + stringBuilder.Append(",\"clickedObjectNormalizedPaths\":["); + var clickedObjectPathsLength = clickedObjectNormalizedPaths.Length; + for (var i = 0; i < clickedObjectPathsLength; i++) + { + StringJsonConverter.WriteToStringBuilder(stringBuilder, clickedObjectNormalizedPaths[i]); + if (i + 1 < clickedObjectPathsLength) + { + stringBuilder.Append(","); + } + } + stringBuilder.Append("]}"); + } + + public string ToKeyMomentJsonString() + { + _stringBuilder.Value.Clear(); + WriteKeyMomentToStringBuilder(_stringBuilder.Value); + return _stringBuilder.Value.ToString(); + } + //gives the position relative to the current screen size public Vector2Int NormalizedPosition => new() { @@ -158,19 +194,4 @@ internal string ToJsonString() public double Replay_StartTime => startTime + Replay_OffsetTime; } - - public class MouseInputActionDataJsonConverter : JsonConverter - { - public override void WriteJson(JsonWriter writer, MouseInputActionData value, JsonSerializer serializer) - { - writer.WriteRawValue(value.ToJsonString()); - } - - public override bool CanRead => false; - - public override MouseInputActionData ReadJson(JsonReader reader, Type objectType, MouseInputActionData existingValue, bool hasExistingValue, JsonSerializer serializer) - { - throw new NotImplementedException(); - } - } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs index df292317..f02e91ab 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs @@ -743,7 +743,7 @@ private void EvaluateKeyMoments() }; // write out the botSegmentList - var jsonData = Encoding.UTF8.GetBytes(botSegment.ToJsonString()); + var jsonData = Encoding.UTF8.GetBytes(botSegment.ToKeyMomentJsonString()); RecordKeyMoment(_currentGameplaySessionKeyMomentsDirectoryPrefix, _keyMomentNumber, jsonData); } From 579430f513c1a2c959ef645f4021da67dbc87583 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Thu, 5 Dec 2024 09:45:32 -0500 Subject: [PATCH 04/31] Separate Key Moment class out of Screen Recorder --- .../Runtime/Fonts/Regression.asset | 4 +- .../BotSegments/BotSegmentDirectoryParser.cs | 4 +- .../BotSegments/Models/BotSegmentList.cs | 34 ++++- .../StateRecorder/KeyMomentEvaluator.cs | 123 ++++++++++++++++++ .../StateRecorder/KeyMomentEvaluator.cs.meta | 3 + .../Scripts/StateRecorder/ScreenRecorder.cs | 117 ++--------------- 6 files changed, 172 insertions(+), 113 deletions(-) create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs.meta diff --git a/src/gg.regression.unity.bots/Runtime/Fonts/Regression.asset b/src/gg.regression.unity.bots/Runtime/Fonts/Regression.asset index 916b52f0..44d8728f 100644 --- a/src/gg.regression.unity.bots/Runtime/Fonts/Regression.asset +++ b/src/gg.regression.unity.bots/Runtime/Fonts/Regression.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fac3f5b1478c0e1f4a6cad4a1670e1cdb0d4e029e150ad382b063de6314582f5 -size 2185363 +oid sha256:1abac230bf22e4d1a41f9be7b7019f77392b45a6df3d6313253bc88102f672d1 +size 2185893 diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentDirectoryParser.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentDirectoryParser.cs index 87afcea9..633dc2eb 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentDirectoryParser.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentDirectoryParser.cs @@ -70,7 +70,7 @@ public static List ParseBotSegmentResourcePath(string resourcePath, */ public static List OrderJsonFiles(string[] jsonFiles) { - RGDebug.LogInfo($"OrderJsonFiles - Input File List [{jsonFiles.Length}] - {string.Join(", ", jsonFiles)}"); + RGDebug.LogDebug($"OrderJsonFiles - Input File List [{jsonFiles.Length}] - {string.Join(", ", jsonFiles)}"); Exception lambdaException = null; // sort by numeric value of entries (not string comparison of filenames) .. normalize to front slashes var entries = jsonFiles.Where(e=>e.EndsWith(".json")).Select(e =>e = e.Replace('\\', '/')).ToList(); @@ -114,7 +114,7 @@ public static List OrderJsonFiles(string[] jsonFiles) entries = jsonFiles.OrderBy(e => e.Substring(0, e.IndexOf('.'))).ToList(); } - RGDebug.LogInfo($"OrderJsonFiles - Output File List [{entries.Count}] - {string.Join(", ", entries)}"); + RGDebug.LogDebug($"OrderJsonFiles - Output File List [{entries.Count}] - {string.Join(", ", entries)}"); return entries; } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegmentList.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegmentList.cs index 1002e66d..9a4d070a 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegmentList.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegmentList.cs @@ -8,7 +8,7 @@ namespace RegressionGames.StateRecorder.BotSegments.Models { [Serializable] - public class BotSegmentList + public class BotSegmentList : IStringBuilderWriteable, IKeyMomentStringBuilderWriteable { // re-usable and large enough to fit all sizes private static readonly ThreadLocal _stringBuilder = new(() => new(10_000)); @@ -27,7 +27,7 @@ public class BotSegmentList // NOT WRITTEN TO JSON - The resource path for this botSegmentList... normally populated during load of saved lists public string resourcePath; - + // NOT WRITTEN TO JSON - Populated at load time public bool isOverride; @@ -62,7 +62,6 @@ public BotSegmentList(string name, List botSegments) FixupNames(); } - public string ToJsonString() { _stringBuilder.Value.Clear(); @@ -91,5 +90,34 @@ public void WriteToStringBuilder(StringBuilder stringBuilder) } stringBuilder.Append("\n]}"); } + + public string ToKeyMomentJsonString() + { + _stringBuilder.Value.Clear(); + WriteKeyMomentToStringBuilder(_stringBuilder.Value); + return _stringBuilder.Value.ToString(); + } + + public void WriteKeyMomentToStringBuilder(StringBuilder stringBuilder) + { + stringBuilder.Append("{\n\"name\":"); + StringJsonConverter.WriteToStringBuilder(stringBuilder, name ); + stringBuilder.Append(",\n\"description\":"); + StringJsonConverter.WriteToStringBuilder(stringBuilder, description ); + stringBuilder.Append(",\n\"apiVersion\":"); + IntJsonConverter.WriteToStringBuilder(stringBuilder, apiVersion); + stringBuilder.Append(",\n\"segments\":[\n"); + var segmentsCount = segments.Count; + for (var i = 0; i < segmentsCount; i++) + { + var segment = segments[i]; + segment.WriteKeyMomentToStringBuilder(stringBuilder); + if (i + 1 < segmentsCount) + { + stringBuilder.Append(",\n"); + } + } + stringBuilder.Append("\n]}"); + } } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs new file mode 100644 index 00000000..00c2328f --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using RegressionGames.StateRecorder.BotSegments.Models; +using RegressionGames.StateRecorder.BotSegments.Models.BotCriteria; +using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions; +using RegressionGames.StateRecorder.Models; + +namespace RegressionGames.StateRecorder +{ + public class KeyMomentEvaluator + { + + public readonly List MouseDataBuffer = new(); + + private MouseInputActionData _previousKeyMomentMouseInputState = null; + + private long _keyMomentNumber; + + public void Reset() + { + _keyMomentNumber = 0; + } + + [CanBeNull] + public BotSegment EvaluateKeyMoment(long tickNumber, out long keyMomentNumber) + { + var meaningfulInputs = MouseDataBuffer.Select((data, i) => (i,data)).Where(tuple => tuple.data.clickedObjectNormalizedPaths.Length > 0).ToList(); + if (meaningfulInputs.Count > 0) + { + List inputsToProcess = new(); + // only take from the buffer up to the last 'un-clicked' state + // this is 'hard'... we want to encapsulate click down/up as a single action together + // but in cases like FPS where I might 'hold' right click to ADS then repeatedly click/release the left mouse button to fire.. i don't want that ALL as one action + // each shot of the weapon would be an 'action', AND .. ads was an action.. so we break this up by any button release + + // find the index range we want + var firstInput = meaningfulInputs[0]; + + if (firstInput.data.IsButtonUnClick(_previousKeyMomentMouseInputState)) + { + inputsToProcess.Add(firstInput.data); + // clean out all the entries up to here + MouseDataBuffer.RemoveRange(0, firstInput.i+1); + } + else if (meaningfulInputs.Count > 1) + { + var priorData = _previousKeyMomentMouseInputState; + var foundUnClick = false; + // go through all the inputs until we find an un-click + // we speculatively update the list as we go, but if we never hit an un-click we wipe it back out before moving on + foreach (var meaningfulInput in meaningfulInputs) + { + if (meaningfulInput.i - 1 >= 0) + { + var previousInput = MouseDataBuffer[meaningfulInput.i - 1]; + + // this may seem odd, but we use this API backwards so that we see if the previous mouse spot before the click had a different click state than the current one.. it 'should' ,but better to be safe + // ultimately.. we're trying to add the mouse position event before the click so that the mouse is 'in position' before clicking down to avoid any snafu's with the input system + if (previousInput.IsButtonUnClick(meaningfulInput.data)) + { + inputsToProcess.Add(previousInput); + } + } + + inputsToProcess.Add(meaningfulInput.data); + if (meaningfulInput.data.IsButtonUnClick(priorData)) + { + // clean out all the entries up to here + MouseDataBuffer.RemoveRange(0, meaningfulInput.i+1); + foundUnClick = true; + break; + } + + priorData = meaningfulInput.data; + } + + if (!foundUnClick) + { + // never found an un-click.. don't process the list so far + inputsToProcess.Clear(); + } + } + + if (inputsToProcess.Count > 0) + { + // update the last state based on the end entry in our list + _previousKeyMomentMouseInputState = inputsToProcess[^1]; + + ++_keyMomentNumber; + + var botSegment = new BotSegment + { + name = $"KeyMoment: {_keyMomentNumber}, Tick: {tickNumber} - Mouse Action Segment", + + endCriteria = new List + { + new() + { + type = KeyFrameCriteriaType.ActionComplete, + data = new ActionCompleteKeyFrameCriteriaData() + } + }, + botAction = new BotAction + { + type = BotActionType.KeyMoment_MouseAction, + data = new KeyMomentMouseActionData + { + mouseActions = inputsToProcess + } + } + }; + + keyMomentNumber = _keyMomentNumber; + return botSegment; + } + } + + keyMomentNumber = _keyMomentNumber; + return null; + } + } +} diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs.meta new file mode 100644 index 00000000..c24bcaea --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9d301a7e8a124423bba79b4892466184 +timeCreated: 1733408401 \ No newline at end of file diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs index f02e91ab..87cfca7a 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs @@ -102,7 +102,6 @@ public class ScreenRecorder : MonoBehaviour private readonly ConcurrentQueue _texture2Ds = new(); private long _tickNumber; - private long _keyMomentNumber; private DateTime _startTime; @@ -116,6 +115,8 @@ public class ScreenRecorder : MonoBehaviour private ProfilerObserver _profilerObserver; private LoggingObserver _loggingObserver; + private KeyMomentEvaluator _keyMomentEvaluator = new(); + #if UNITY_EDITOR private bool _needToRefreshAssets; #endif @@ -573,7 +574,7 @@ private IEnumerator StartRecordingCoroutine(string referenceSessionId) } IsRecording = true; _tickNumber = 0; - _keyMomentNumber = 0; + _keyMomentEvaluator.Reset(); _currentSessionId = Guid.NewGuid().ToString("n"); _referenceSessionId = referenceSessionId; _startTime = DateTime.Now; @@ -651,105 +652,6 @@ public void StartRecording(string referenceSessionId) // cache this to avoid re-alloc on every frame private readonly List _keyFrameTypeList = new(10); - private MouseInputActionData _previousKeyMomentMouseInputState = null; - - private void EvaluateKeyMoments() - { - var meaningfulInputs = _keyMomentMouseDataBuffer.Select((data, i) => (i,data)).Where(tuple => tuple.data.clickedObjectNormalizedPaths.Length > 0).ToList(); - if (meaningfulInputs.Count > 0) - { - - List inputsToProcess = new(); - // only take from the buffer up to the last 'un-clicked' state - // this is 'hard'... we want to encapsulate click down/up as a single action together - // but in cases like FPS where I might 'hold' right click to ADS then repeatedly click/release the left mouse button to fire.. i don't want that ALL as one action - // each shot of the weapon would be an 'action', AND .. ads was an action.. so we break this up by any button release - - // find the index range we want - var firstInput = meaningfulInputs[0]; - - if (firstInput.data.IsButtonUnClick(_previousKeyMomentMouseInputState)) - { - inputsToProcess.Add(firstInput.data); - // clean out all the entries up to here - _keyMomentMouseDataBuffer.RemoveRange(0, firstInput.i+1); - } - else if (meaningfulInputs.Count > 1) - { - var priorData = _previousKeyMomentMouseInputState; - var foundUnClick = false; - // go through all the inputs until we find an un-click - // we speculatively update the list as we go, but if we never hit an un-click we wipe it back out before moving on - foreach (var meaningfulInput in meaningfulInputs) - { - if (meaningfulInput.i - 1 >= 0) - { - var previousInput = _keyMomentMouseDataBuffer[meaningfulInput.i - 1]; - - // this may seem odd, but we use this API backwards so that we see if the previous mouse spot before the click had a different click state than the current one.. it 'should' ,but better to be safe - // ultimately.. we're trying to add the mouse position event before the click so that the mouse is 'in position' before clicking down to avoid any snafu's with the input system - if (previousInput.IsButtonUnClick(meaningfulInput.data)) - { - inputsToProcess.Add(previousInput); - } - } - - inputsToProcess.Add(meaningfulInput.data); - if (meaningfulInput.data.IsButtonUnClick(priorData)) - { - // clean out all the entries up to here - _keyMomentMouseDataBuffer.RemoveRange(0, meaningfulInput.i+1); - foundUnClick = true; - break; - } - - priorData = meaningfulInput.data; - } - - if (!foundUnClick) - { - // never found an un-click.. don't process the list so far - inputsToProcess.Clear(); - } - } - - if (inputsToProcess.Count > 0) - { - // update the last state based on the end entry in our list - _previousKeyMomentMouseInputState = inputsToProcess[^1]; - - ++_keyMomentNumber; - - var botSegment = new BotSegment - { - name = $"KeyMoment: {_keyMomentNumber}, Tick: {_tickNumber} - Mouse Action Segment", - - endCriteria = new List - { - new() - { - type = KeyFrameCriteriaType.ActionComplete, - data = new ActionCompleteKeyFrameCriteriaData() - } - }, - botAction = new BotAction - { - type = BotActionType.KeyMoment_MouseAction, - data = new KeyMomentMouseActionData - { - mouseActions = inputsToProcess - } - } - }; - - // write out the botSegmentList - var jsonData = Encoding.UTF8.GetBytes(botSegment.ToKeyMomentJsonString()); - - RecordKeyMoment(_currentGameplaySessionKeyMomentsDirectoryPrefix, _keyMomentNumber, jsonData); - } - } - } - private void GetKeyFrameType(bool firstFrame, bool hasDeltas, bool pixelHashChanged, bool endRecording) { _keyFrameTypeList.Clear(); @@ -900,8 +802,6 @@ public void StopRecording(bool toolbarButtonTriggered = false) private readonly List _segmentMouseDataBuffer = new(); - private readonly List _keyMomentMouseDataBuffer = new(); - private IEnumerator RecordFrame(bool endRecording = false, bool endRecordingFromToolbarButton = false) { if (_tickQueue is { IsAddingCompleted: false }) @@ -964,11 +864,16 @@ private IEnumerator RecordFrame(bool endRecording = false, bool endRecordingFrom var mouseInputData = _mouseObserver.FlushInputDataBuffer(endRecordingFromToolbarButton, minimizeOutput: minimizeRecordingMouseMovements); _segmentMouseDataBuffer.AddRange(mouseInputData); - _keyMomentMouseDataBuffer.AddRange(mouseInputData); + _keyMomentEvaluator.MouseDataBuffer.AddRange(mouseInputData); // Compute key moment criteria / action - EvaluateKeyMoments(); - + var keyMomentBotSegment = _keyMomentEvaluator.EvaluateKeyMoment(_tickNumber, out var keyMomentNumber); + if (keyMomentBotSegment != null) + { + // write out the botSegment + var jsonData = Encoding.UTF8.GetBytes(keyMomentBotSegment.ToKeyMomentJsonString()); + RecordKeyMoment(_currentGameplaySessionKeyMomentsDirectoryPrefix, keyMomentNumber, jsonData); + } // estimating the time in int milliseconds .. won't exactly match target FPS.. but will be close if (_keyFrameTypeList.Count > 0 From 7eeaa3eb9c0624e98db73e5b0c4258665a07e7c0 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Thu, 5 Dec 2024 12:43:39 -0500 Subject: [PATCH 05/31] Harden mouse click location logic --- .../BotActions/KeyMomentMouseActionData.cs | 181 ++++++++++++------ 1 file changed, 126 insertions(+), 55 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 1d48882d..463c18c3 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -14,13 +14,13 @@ namespace RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions [Serializable] public class PreconditionNormalizedPathData { - public readonly string path; + public readonly string normalizedPath; public readonly List tokenData; - public PreconditionNormalizedPathData(string path) + public PreconditionNormalizedPathData(string normalizedPath) { - this.path = path; - this.tokenData = TokenizeObjectPath(path); + this.normalizedPath = normalizedPath; + this.tokenData = TokenizeObjectPath(normalizedPath); } public static List TokenizeObjectPath(string path) @@ -229,6 +229,7 @@ public void StartAction(int segmentNumber, Dictionary curren return result; } + // we queue up all the actions to do before doing them in case we can't do one based on paths.. we don't want a partial operation private readonly List _mouseActionsToDo = new(); public bool ProcessAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) @@ -246,6 +247,10 @@ public bool ProcessAction(int segmentNumber, Dictionary curr MouseInputActionData pendingAction = null; + Vector2? lastClickPosition = null; + + (ObjectStatus, (int[],int[]))[] previousPreconditionMatches = null; + for (var m = 0; m < mouseActionsCount; m++) { var mouseAction = mouseActions[m]; @@ -271,36 +276,62 @@ public bool ProcessAction(int segmentNumber, Dictionary curr { possibleTransformToClick.TokenizedObjectPath ??= PreconditionNormalizedPathData.TokenizeObjectPath(possibleTransformToClick.NormalizedPath); + var breakoutCount = 0;// optimization for when we've found exact matches for all preconditions // this should nearly always be smaller than the # of possibleTransformToClick for (var j = 0; j < preconditionsLength; j++) { - var precondition = preConditionNormalizedPaths[j]; - var tokenMatches = EvaluateTokenMatches(precondition.tokenData, possibleTransformToClick.TokenizedObjectPath); - if (tokenMatches.Item1 != null) + // optimization - only keep processing this precondition when we didn't have an exact match for it already + if (preconditionMatches[j].Item1 == null || preconditionMatches[j].Item2.Item1 != null) { - // got something of the same path length with some token matches in each part - if (preconditionMatches[j].Item1 != null) + var precondition = preConditionNormalizedPaths[j]; + + // prefer exact path match (which we don't have available here... yet), then normalized path match, then tokenized path matching logic + if (precondition.normalizedPath == possibleTransformToClick.NormalizedPath) { - // compare - if (IsNewTokenMatchesBetter(preconditionMatches[j].Item2, tokenMatches)) - { - preconditionMatches[j] = (possibleTransformToClick, tokenMatches); - } + // normalized matching logic + preconditionMatches[j] = (possibleTransformToClick, (null, null)); + break; // the for } - else + // else { - //set - preconditionMatches[j] = (possibleTransformToClick, tokenMatches); + // tokenized matching logic + var tokenMatches = EvaluateTokenMatches(precondition.tokenData, possibleTransformToClick.TokenizedObjectPath); + if (tokenMatches.Item1 != null) + { + // got something of the same path length with some token matches in each part + if (preconditionMatches[j].Item1 != null) + { + // compare + if (IsNewTokenMatchesBetter(preconditionMatches[j].Item2, tokenMatches)) + { + preconditionMatches[j] = (possibleTransformToClick, tokenMatches); + } + } + else + { + //set + preconditionMatches[j] = (possibleTransformToClick, tokenMatches); + } + + break; // the for + } } - break; // the for + } else + { + ++breakoutCount; } } + + if (breakoutCount == preconditionsLength) + { + break; // the foreach - we found exact matches for everything + } } // now that we have all the precondition matches mapped out, let's see if we have the leftmost object.. // if the leftmost object is a UI object, then it should already be the smallest / most precise UI thing to click as we solve this by sorting - // the UI elements in mouseinputactionobserver.FindObjectsAtPosition based on the smallest screenspace bounds + // the UI elements in mouseinputactionobserver.FindObjectsAtPosition based on the smallest screen-space bounds if (preconditionMatches[0].Item1 != null) { // yay.. we found something.. first let's make sure it's ready to be clicked @@ -324,6 +355,7 @@ public bool ProcessAction(int segmentNumber, Dictionary curr if (isInteractable) { + Vector2? clickPosition = null; // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds var smallestBounds = preconditionMatches[0].Item1.screenSpaceBounds.Value; @@ -331,77 +363,111 @@ public bool ProcessAction(int segmentNumber, Dictionary curr var minY = smallestBounds.min.y; var maxX = smallestBounds.max.x; var maxY = smallestBounds.max.y; - // now let's narrow down the screen space bounds more precisely based on all our preconditions - for (var i = 1; i < preconditionMatches.Length; i++) + + if (lastClickPosition.HasValue) { - var preconditionMatch = preconditionMatches[i]; - if (preconditionMatch.Item1 != null) + if (lastClickPosition.Value.x >= minX + && lastClickPosition.Value.x <= maxX + && lastClickPosition.Value.y >= minY + && lastClickPosition.Value.y <= maxY) { - // not the same logic as MouseEventSender.FindBestClickObject.. this version narrows in on the smallest bounding area - - // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds - var newBounds = preconditionMatch.Item1.screenSpaceBounds.Value; - var newMinX = newBounds.min.x; - var newMinY = newBounds.min.y; - var newMaxX = newBounds.max.x; - var newMaxY = newBounds.max.y; - - if (newMinX > minX && newMinX < maxX) - { - minX = newMinX; - } - - if (newMaxX > minX && newMaxX < maxX) - { - maxX = newMaxX; - } - - if (newMinY > minY && newMinY < maxY) + RGDebug.LogDebug($"Leaving mouse action index {m} at previous action position based on bounds overlaps to original path: {preconditionMatches[0].Item1.NormalizedPath}"); + // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the + // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame + clickPosition = lastClickPosition.Value; + } + else if (previousPreconditionMatches != null) + { + // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) + // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button + // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen + // isn't important.. we want to un-click exactly where we clicked + if (previousPreconditionMatches.Any(a => a.Item1 == preconditionMatches[0].Item1)) { - minY = newMinY; + RGDebug.LogDebug($"Leaving mouse action index {m} at previous action position based on object path existing at time of click: {preconditionMatches[0].Item1.NormalizedPath}"); + clickPosition = lastClickPosition.Value; } - - if (newMaxY > minY && newMaxY < maxY) + } + } + if (!clickPosition.HasValue) + { + RGDebug.LogDebug($"Starting with bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} , target object: {preconditionMatches[0].Item1.NormalizedPath}"); + // now let's narrow down the screen space bounds more precisely based on all our preconditions + for (var i = 1; i < preconditionMatches.Length; i++) + { + var preconditionMatch = preconditionMatches[i]; + if (preconditionMatch.Item1 != null) { - maxY = newMaxY; + // not the same logic as MouseEventSender.FindBestClickObject.. this version narrows in on the smallest bounding area + + // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds + var newBounds = preconditionMatch.Item1.screenSpaceBounds.Value; + var newMinX = newBounds.min.x; + var newMinY = newBounds.min.y; + var newMaxX = newBounds.max.x; + var newMaxY = newBounds.max.y; + + if (newMinX > minX && newMinX < maxX) + { + minX = newMinX; + } + + if (newMaxX > minX && newMaxX < maxX) + { + maxX = newMaxX; + } + + if (newMinY > minY && newMinY < maxY) + { + minY = newMinY; + } + + if (newMaxY > minY && newMaxY < maxY) + { + maxY = newMaxY; + } + RGDebug.LogDebug($"Narrowed bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} for overlap with object path: {preconditionMatch.Item1.NormalizedPath}"); } } + clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); } - var position = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); - + var myClickPosition = clickPosition.Value; // we fill this in for all code paths.. if you get a null here, something above has bad logic as this should be filled in always here + lastClickPosition = myClickPosition; if (pendingAction != null) { var myPendingAction = pendingAction; _mouseActionsToDo.Add(() => { - RGDebug.LogInfo($"KeyMoment - Mouse Pending Action applied at position: ({(int)position.x}, {(int)position.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); + RGDebug.LogInfo($"KeyMoment - Mouse Pending Action applied at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); // perform the mouse action at the center of our new smallest bounds - MouseEventSender.SendRawPositionMouseEvent(segmentNumber, position, myPendingAction.leftButton, myPendingAction.middleButton, myPendingAction.rightButton, myPendingAction.forwardButton, myPendingAction.backButton, myPendingAction.scroll); + MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, myPendingAction.leftButton, myPendingAction.middleButton, myPendingAction.rightButton, myPendingAction.forwardButton, myPendingAction.backButton, myPendingAction.scroll); }); } _mouseActionsToDo.Add(() => { - RGDebug.LogInfo($"KeyMoment - Mouse Action at position: ({(int)position.x}, {(int)position.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); + RGDebug.LogInfo($"KeyMoment - Mouse Action at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); // perform the mouse action at the center of our new smallest bounds - MouseEventSender.SendRawPositionMouseEvent(segmentNumber, position, mouseAction.leftButton, mouseAction.middleButton, mouseAction.rightButton, mouseAction.forwardButton, mouseAction.backButton, mouseAction.scroll); + MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, mouseAction.leftButton, mouseAction.middleButton, mouseAction.rightButton, mouseAction.forwardButton, mouseAction.backButton, mouseAction.scroll); }); pendingAction = null; } else { + _mouseActionsToDo.Clear(); // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse action object found for paths:\n{string.Join("\n", preConditionNormalizedPaths.Select(a => a.path))}"; + error = $"No valid mouse action object found for paths:\n{string.Join("\n", preConditionNormalizedPaths.Select(a => a.normalizedPath))}"; return false; } } else { + _mouseActionsToDo.Clear(); // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse action object found for path:\n{preConditionNormalizedPaths[0].path}"; + error = $"No valid mouse action object found for path:\n{preConditionNormalizedPaths[0].normalizedPath}"; return false; } } @@ -424,6 +490,11 @@ public bool ProcessAction(int segmentNumber, Dictionary curr private bool IsNewTokenMatchesBetter((int[], int[]) oldMatches, (int[], int[]) newMatches) { + if (oldMatches.Item1 == null) + { + // we already had an exact match report + return false; + } // all 4 lengths are the same at this point var length = oldMatches.Item2.Length; for (int i = 0; i < length; i++) From 86a7e91f35e0ee16bda385c6cc807ee09294513a Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Thu, 5 Dec 2024 13:38:06 -0500 Subject: [PATCH 06/31] Add new patterned last 3 actions exploration --- .../BotSegments/ActionExplorationDriver.cs | 102 ++++++++++++------ .../BotSegmentsPlaybackController.cs | 7 +- 2 files changed, 77 insertions(+), 32 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index 42c8e103..6b0bba08 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using RegressionGames; using RegressionGames.StateRecorder.BotSegments.Models; using RegressionGames.StateRecorder.Models; using UnityEngine; @@ -8,17 +10,54 @@ namespace StateRecorder.BotSegments public class ActionExplorationDriver : MonoBehaviour { - // normally the first thing we do is retry the previous action - private IBotActionData _previouslyCompletedAction = null; + // normally the first thing we do is retry the previous actions + // these are sorted newest to oldest + private readonly List PreviouslyCompletedActions = new(3); + + private readonly List> _previousActions = new(3); + + private int _previousActionIndex = 0; public bool IsExploring { get; private set;} - private IBotActionData _activeAction = null; + public void ReportPreviouslyCompletedAction(IBotActionData action) + { + // limit to 3 prior actions + while (PreviouslyCompletedActions.Count > 2) + { + // remove at front + PreviouslyCompletedActions.RemoveAt(0); + } + + if (!PreviouslyCompletedActions.Contains(action)) + { + // insert at end + PreviouslyCompletedActions.Add(action); + } + } - public void StartExploring(IBotActionData previousAction) + public void StartExploring() { - _previouslyCompletedAction = previousAction; - IsExploring = true; + if (!IsExploring) + { + _previousActions.Clear(); + _previousActionIndex = 0; + List priorList = null; + foreach (var previouslyCompletedAction in PreviouslyCompletedActions) + { + // populate these lists so that we have a pattern of 0, 10, 210 where 0 is the newest and 2 is the oldest and we try them in patterns as listed + var actionList = new List (3); + actionList.Add(previouslyCompletedAction); + if (priorList != null) + { + actionList.AddRange(priorList); + } + priorList = actionList; + _previousActions.Add(actionList); + } + RGDebug.LogInfo("ActionExplorationDriver - Starting Exploratory Actions"); + IsExploring = true; + } } public void PerformExploratoryAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) @@ -29,43 +68,46 @@ public void PerformExploratoryAction(int segmentNumber, Dictionary= _previousActions.Count) { - // TODO: Implement hooks to exploration algorithms here + _previousActionIndex = 0; } - if (_activeAction != null) + // retry prior actions in pattern .. 0, 10, 210 where 2 is the oldest and 0 is the most recent + if (_previousActionIndex < _previousActions.Count) { - if (!_activeAction.IsCompleted()) + var previousActions = _previousActions[_previousActionIndex]; + ++_previousActionIndex; + // process all N actions in the list + if (previousActions != null) { - _activeAction.StartAction(segmentNumber, currentTransforms, currentEntities); - _activeAction.ProcessAction(segmentNumber, currentTransforms, currentEntities, out error); - } - else - { - _activeAction = null; + foreach (var botActionData in previousActions) + { + RGDebug.LogInfo($"ActionExplorationDriver - Performing Exploratory Action of Type: {botActionData.GetType().Name}"); + try + { + botActionData.ReplayReset(); + botActionData.StartAction(segmentNumber, currentTransforms, currentEntities); + botActionData.ProcessAction(segmentNumber, currentTransforms, currentEntities, out error); + botActionData.AbortAction(segmentNumber); + } + catch (Exception) + { + // no op + } + } } } + // TODO: Implement hooks to other exploration algorithms + } public void StopExploring(int segmentNumber) { IsExploring = false; - if (_activeAction != null) - { - _activeAction.AbortAction(segmentNumber); - _activeAction = null; - } + RGDebug.LogInfo("ActionExplorationDriver - Stopped Exploratory Actions"); } } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs index 8e8b7e68..713d7cd9 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs @@ -464,7 +464,10 @@ private void EvaluateBotSegments() { // allow the main action to retry between every exploratory action var didAction = firstActionSegment.ProcessAction(transformStatuses, entityStatuses, out var error); - // only log this if we're really stuck on it + if (firstActionSegment.Replay_ActionCompleted) + { + _explorationDriver.ReportPreviouslyCompletedAction(firstActionSegment.botAction.data); + } if (error == null && didAction) { _explorationDriver.StopExploring(firstActionSegment.Replay_SegmentNumber); @@ -481,7 +484,7 @@ private void EvaluateBotSegments() { var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\r\n" + error + "\r\nStarting exploratory actions..."; LogPlaybackWarning(loggedMessage); - _explorationDriver.StartExploring(_lastSuccessfulAction); + _explorationDriver.StartExploring(); } else { From 81b8c8052b7c3df170a3490dfcb291154b16a71a Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Thu, 5 Dec 2024 13:47:16 -0500 Subject: [PATCH 07/31] Fix N last actions pattern to allow current action to try between each sub action --- .../BotSegments/ActionExplorationDriver.cs | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index 6b0bba08..1510c6e8 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -17,6 +17,7 @@ public class ActionExplorationDriver : MonoBehaviour private readonly List> _previousActions = new(3); private int _previousActionIndex = 0; + private int _previousActionSubIndex = 0; public bool IsExploring { get; private set;} @@ -42,6 +43,7 @@ public void StartExploring() { _previousActions.Clear(); _previousActionIndex = 0; + _previousActionSubIndex = 0; List priorList = null; foreach (var previouslyCompletedAction in PreviouslyCompletedActions) { @@ -77,25 +79,36 @@ public void PerformExploratoryAction(int segmentNumber, Dictionary= previousActions.Count) { - foreach (var botActionData in previousActions) + // move to the next list + _previousActionSubIndex = 0; + ++_previousActionIndex; + if (_previousActionIndex >= _previousActions.Count) { - RGDebug.LogInfo($"ActionExplorationDriver - Performing Exploratory Action of Type: {botActionData.GetType().Name}"); - try - { - botActionData.ReplayReset(); - botActionData.StartAction(segmentNumber, currentTransforms, currentEntities); - botActionData.ProcessAction(segmentNumber, currentTransforms, currentEntities, out error); - botActionData.AbortAction(segmentNumber); - } - catch (Exception) - { - // no op - } + _previousActionIndex = 0; } + previousActions = _previousActions[_previousActionIndex]; + } + + var nextAction = previousActions[_previousActionSubIndex]; + ++_previousActionSubIndex; + // process next action in the list + if (nextAction != null) + { + RGDebug.LogInfo($"ActionExplorationDriver - Performing Exploratory Action of Type: {nextAction.GetType().Name}"); + try + { + nextAction.ReplayReset(); + nextAction.StartAction(segmentNumber, currentTransforms, currentEntities); + nextAction.ProcessAction(segmentNumber, currentTransforms, currentEntities, out error); + nextAction.AbortAction(segmentNumber); + } + catch (Exception) + { + // no op + } + } } From 1ab72fcc7f82cac06050c6bfa6786e9fcbca4480 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Fri, 6 Dec 2024 08:37:12 -0500 Subject: [PATCH 08/31] zOffset object fixes so that particle effects don't block the consider of real objects --- .../BotSegments/ActionExplorationDriver.cs | 58 +++++++++++++------ .../BotSegmentsPlaybackController.cs | 34 ++++++++--- .../StateRecorder/MouseInputActionObserver.cs | 3 +- .../StateRecorder/TransformObjectFinder.cs | 35 ++++++++++- 4 files changed, 100 insertions(+), 30 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index 1510c6e8..c7ddaa0d 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using RegressionGames; using RegressionGames.StateRecorder.BotSegments.Models; +using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions; using RegressionGames.StateRecorder.Models; using UnityEngine; @@ -32,6 +33,12 @@ public void ReportPreviouslyCompletedAction(IBotActionData action) if (!PreviouslyCompletedActions.Contains(action)) { + var extraLog = ""; + if (action is KeyMomentMouseActionData keyMomentMouseActionData) + { + extraLog += " with first object path: " + keyMomentMouseActionData.mouseActions[1].clickedObjectNormalizedPaths[0]; + } + RGDebug.LogInfo($"ActionExplorationDriver - Adding previously completed action of Type: {action.GetType().Name}" + extraLog); // insert at end PreviouslyCompletedActions.Add(action); } @@ -70,6 +77,8 @@ public void PerformExploratoryAction(int segmentNumber, Dictionary= _previousActions.Count) { _previousActionIndex = 0; @@ -91,36 +100,49 @@ public void PerformExploratoryAction(int segmentNumber, Dictionary()?.SetKeyFrameWarningText(null); + if (didAction) + { + _explorationDriver.StopExploring(firstActionSegment.Replay_SegmentNumber); + // for every non error action, reset the timer + _lastTimeLoggedKeyFrameConditions = now; + FindObjectOfType()?.SetKeyFrameWarningText(null); + } + if (firstActionSegment.botAction?.data is IKeyMomentExploration) + { + if (firstActionSegment.botAction.data.IsCompleted()) + { + _explorationDriver.ReportPreviouslyCompletedAction(firstActionSegment.botAction.data); + } + } } if (error != null) @@ -492,7 +498,17 @@ private void EvaluateBotSegments() LogPlaybackWarning(loggedMessage); } } + _explorationDriver.PerformExploratoryAction(firstActionSegment.Replay_SegmentNumber, transformStatuses, entityStatuses, out var explorationError); + if (explorationError != null) + { + // only log this if we've gone 2x the interval without a success + if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL*2) + { + var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing exploratory BotAction\r\n" + explorationError; + LogPlaybackWarning(loggedMessage); + } + } } } catch (Exception ex) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index 22c62e91..5e9333b9 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -232,7 +232,8 @@ private List FindObjectsAtPosition(Vector2 position, IEnumerable-0.00001f) { // filter out to only world space objects or interactable UI objects diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs index 9ec792b3..2ceb74e1 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs @@ -372,8 +372,24 @@ public static (Bounds?, float, Bounds?) SelectBoundsForTransform(Camera mainCame var worldSize = new Vector3((maxWorldX - minWorldX), (maxWorldY - minWorldY), (maxWorldZ - minWorldZ)); var worldCenter = new Vector3(minWorldX + worldSize.x, minWorldY + worldSize.y / 2, minWorldZ + worldSize.z / 2); + + var zOffset = Math.Min(min.z, max.z); + // if the zOffset is negative, our camera is inside the bounding volume for this object, choose the farthest z instead.. this happens for particle effects like fx_ImpSpawner in bossroom + // we still need to track down why this thing has HUGE screenspace bounds compared to what is shown in the editor + if (zOffset < 0.0f) + { + zOffset = Math.Max(min.z, max.z); + } + // don't let world space objects be <= 0.0f as they would appear on top of true ui overlay objects in our processing + if (zOffset < 0.00001f) + { + zOffset = 0.0001f; + } + + // get the screen point values for the world max / min and find the screen space z offset closest the camera - return (ssBounds, Math.Min(min.z, max.z), new Bounds(worldCenter, worldSize)); + return (ssBounds, zOffset, new Bounds(worldCenter, worldSize)); + } return (ssBounds, 0f, null); @@ -558,7 +574,22 @@ public static (Bounds?, float, Bounds?) SelectBoundsForTransform(Camera mainCame var worldSize = new Vector3((maxWorldX - minWorldX), (maxWorldY - minWorldY), (maxWorldZ - minWorldZ)); var worldCenter = new Vector3(minWorldX + worldSize.x / 2, minWorldY + worldSize.y / 2, minWorldZ + worldSize.z / 2); - return (new Bounds(center, size), Math.Min(minZ, maxZ), new Bounds(worldCenter, worldSize)); + + var zOffset = Math.Min(minZ, maxZ); + // if the zOffset is negative, our camera is inside the bounding volume for this object, choose the farthest z instead.. this happens for particle effects like fx_ImpSpawner in bossroom + // we still need to track down why this thing has HUGE screenspace bounds compared to what is shown in the editor + if (zOffset < 0.0f) + { + zOffset = Math.Max(minZ, maxZ); + } + // don't let world space objects be <= 0.0f as they would appear on top of true ui overlay objects in our processing + if (zOffset < 0.00001f) + { + zOffset = 0.0001f; + } + + // get the screen point values for the world max / min and find the screen space z offset closest the camera + return (new Bounds(center, size), zOffset, new Bounds(worldCenter, worldSize)); } } } From 51433df58da4a7e09ecac5bd54a28e4ac567c137 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Fri, 6 Dec 2024 10:11:13 -0500 Subject: [PATCH 09/31] Handle obstructed click objects --- .../BotActions/KeyMomentMouseActionData.cs | 18 +++++++++++++++--- .../StateRecorder/MouseInputActionObserver.cs | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 463c18c3..5f3afaa7 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -432,15 +432,28 @@ public bool ProcessAction(int segmentNumber, Dictionary curr clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); } - var myClickPosition = clickPosition.Value; // we fill this in for all code paths.. if you get a null here, something above has bad logic as this should be filled in always here + var myClickPosition = clickPosition.Value; // we fill 'clickPosition' in for all code paths.. if you get a null here, something above has bad logic as this should be filled in always here lastClickPosition = myClickPosition; + if (pendingAction != null) { + // we are on the 2nd action, which is the 'click' + + // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over + // our intended click item (iow.. it's there/ready, but obstructed) + var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); + + if (objectsAtClickPosition.Count < 1 || objectsAtClickPosition[0].NormalizedPath != mouseAction.clickedObjectNormalizedPaths[0]) + { + error = $"Unable to perform Key Moment Mouse Action at at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path: {mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path: {objectsAtClickPosition[0].NormalizedPath}"; + _mouseActionsToDo.Clear(); + return false; + } + var myPendingAction = pendingAction; _mouseActionsToDo.Add(() => { RGDebug.LogInfo($"KeyMoment - Mouse Pending Action applied at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); - // perform the mouse action at the center of our new smallest bounds MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, myPendingAction.leftButton, myPendingAction.middleButton, myPendingAction.rightButton, myPendingAction.forwardButton, myPendingAction.backButton, myPendingAction.scroll); }); @@ -449,7 +462,6 @@ public bool ProcessAction(int segmentNumber, Dictionary curr _mouseActionsToDo.Add(() => { RGDebug.LogInfo($"KeyMoment - Mouse Action at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); - // perform the mouse action at the center of our new smallest bounds MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, mouseAction.leftButton, mouseAction.middleButton, mouseAction.rightButton, mouseAction.forwardButton, mouseAction.backButton, mouseAction.scroll); }); diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index 5e9333b9..0e443015 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -222,7 +222,7 @@ public List FlushInputDataBuffer(bool ignoreLastClick, boo return result; } - private List FindObjectsAtPosition(Vector2 position, IEnumerable statefulObjects, out float maxZDepth) + public static List FindObjectsAtPosition(Vector2 position, IEnumerable statefulObjects, out float maxZDepth) { // make sure screen space position Z is around 0 var vec3Position = new Vector3(position.x, position.y, 0); From cd3ecb5c128a3082241c2700aede9a9705e3daaa Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Fri, 6 Dec 2024 12:36:50 -0500 Subject: [PATCH 10/31] UI obstruction checks - still working on world space --- .../BotActions/KeyMomentMouseActionData.cs | 95 +++++++++++++++++-- .../StateRecorder/MouseInputActionObserver.cs | 1 - .../StateRecorder/TransformObjectFinder.cs | 1 + 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 5f3afaa7..500067c9 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -5,7 +5,9 @@ using RegressionGames.StateRecorder.JsonConverters; using RegressionGames.StateRecorder.Models; using UnityEngine; +using UnityEngine.EventSystems; using UnityEngine.UI; +using Random = UnityEngine.Random; // ReSharper disable InconsistentNaming @@ -429,7 +431,16 @@ public bool ProcessAction(int segmentNumber, Dictionary curr RGDebug.LogDebug($"Narrowed bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} for overlap with object path: {preconditionMatch.Item1.NormalizedPath}"); } } - clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); + + // we started with clicking the center.. but this was limiting + //clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); + // instead... make the click-position a random position within the bounds + // 1. for more variability in testing + // 2. to get around cases where we were say clicking on a floor tile, but there is something on that floor tile and we wanted to click on the open space of the floor tile + // on the next attempt, it picks a new position to try thus giving us a better chance of passing + // TODO: Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? + // This would allow us to know that we clicked about X far into this floor tile and replicate that positioning regardless of the actual worldspace positioning in the replay... + clickPosition = new Vector2(Random.Range(minX+0.00001f, maxX-0.00001f), Random.Range(minY+0.00001f, maxY-0.00001f)); } var myClickPosition = clickPosition.Value; // we fill 'clickPosition' in for all code paths.. if you get a null here, something above has bad logic as this should be filled in always here @@ -440,14 +451,84 @@ public bool ProcessAction(int segmentNumber, Dictionary curr // we are on the 2nd action, which is the 'click' // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over - // our intended click item (iow.. it's there/ready, but obstructed) - var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); + // our intended click item (iow.. it's there/ready, but obstructed) ... we only sort by zDepth(not UI bounds) here so we can find proper obstructions - if (objectsAtClickPosition.Count < 1 || objectsAtClickPosition[0].NormalizedPath != mouseAction.clickedObjectNormalizedPaths[0]) + if (oStatus is TransformStatus transformStatus) { - error = $"Unable to perform Key Moment Mouse Action at at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path: {mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path: {objectsAtClickPosition[0].NormalizedPath}"; - _mouseActionsToDo.Clear(); - return false; + + if (transformStatus.worldSpaceBounds == null) + { + // UI element.. make sure it is the highest UI thing hit + + var eventSystemHits = new List(); + // ray-cast into the scene and see if this object is the first thing hit + var pointerEventData = new PointerEventData(EventSystem.current) + { + button = PointerEventData.InputButton.Left, + position = myClickPosition + }; + EventSystem.current.RaycastAll(pointerEventData, eventSystemHits); + + if (eventSystemHits.Count > 0) + { + eventSystemHits.Sort((a,b) => + { + if (a.distance < b.distance) + { + return -1; + } + + if (a.distance > b.distance) + { + return 1; + } + + // higher depth == closer to camera.. don't even get me started + return b.depth - a.depth; + }); + } + if (eventSystemHits.Count <= 0) + { + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\nno selectable UI objects detected at point"; + _mouseActionsToDo.Clear(); + return false; + } + else + { + var hitNormalizedPath = TransformStatus.GetOrCreateTransformStatus(eventSystemHits[0].gameObject.transform).NormalizedPath; + if (!hitNormalizedPath.StartsWith(mouseAction.clickedObjectNormalizedPaths[0])) + { + // if this isn't the exact object or a child object (like a button text of the button we're clicking).. then error out + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{hitNormalizedPath}"; + _mouseActionsToDo.Clear(); + return false; + } + } + } + else + { + // Handle world space obstructions... this is easier, since any overlay UI element will be in the path list over world objects, we just need to make sure our + // object is at the front of the list and thus at the closest Z depth at that point + + // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over + // our intended click item (iow.. it's there/ready, but obstructed) ... we only sort by zDepth(not UI bounds) here so we can find proper obstructions + var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); + + if (objectsAtClickPosition.Count < 1) + { + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\nno objects at position"; + _mouseActionsToDo.Clear(); + return false; + } + + if (objectsAtClickPosition[0].NormalizedPath != mouseAction.clickedObjectNormalizedPaths[0]) + { + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{objectsAtClickPosition[0].NormalizedPath}"; + _mouseActionsToDo.Clear(); + return false; + } + } + } var myPendingAction = pendingAction; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index 0e443015..baf8c335 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -287,7 +287,6 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer // if a isn't smaller then we don't much care between == vs > as in floating point land.. == is so unlikely as for us to not worry about 'stable' sort for this case return 1; - } if (a.screenSpaceZOffset < b.screenSpaceZOffset) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs index 2ceb74e1..1acaa38b 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs @@ -406,6 +406,7 @@ public static (Bounds?, float, Bounds?) SelectBoundsForTransform(Camera mainCame // find the full bounds of the statefulGameObject var statefulGameObjectTransform = theTransform.transform; + var transformName = ""+statefulGameObjectTransform.name; // used for debugging object bounds and easily seeing the name in the debugger.. don't remove RendererQueryList.Clear(); statefulGameObjectTransform.GetComponentsInChildren(RendererQueryList); From 324f23d63573ceceb72ec6ee17eace3a5228dd67 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Fri, 6 Dec 2024 14:05:14 -0500 Subject: [PATCH 11/31] Closer on world space obstructions, but still some bugs --- .../BotActions/KeyMomentMouseActionData.cs | 36 ++++--- .../StateRecorder/MouseInputActionObserver.cs | 99 ++++++++++--------- .../StateRecorder/TransformObjectFinder.cs | 8 +- 3 files changed, 81 insertions(+), 62 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 500067c9..7d6612b2 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -511,21 +511,33 @@ public bool ProcessAction(int segmentNumber, Dictionary curr // object is at the front of the list and thus at the closest Z depth at that point // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over - // our intended click item (iow.. it's there/ready, but obstructed) ... we only sort by zDepth(not UI bounds) here so we can find proper obstructions - var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); + // our intended click item (iow.. it's there/ready, but obstructed) ... + // we have to be careful though, because the 'zDepth' computed for the object may NOT be the zDepth at this exact click point on said object... + //var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); - if (objectsAtClickPosition.Count < 1) + var mainCamera = Camera.main; + if (mainCamera != null) { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\nno objects at position"; - _mouseActionsToDo.Clear(); - return false; - } + var ray = mainCamera.ScreenPointToRay(myClickPosition); - if (objectsAtClickPosition[0].NormalizedPath != mouseAction.clickedObjectNormalizedPaths[0]) - { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{objectsAtClickPosition[0].NormalizedPath}"; - _mouseActionsToDo.Clear(); - return false; + var didHit = Physics.Raycast(ray, out var raycastHit); + + if (!didHit) + { + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\nno objects at position"; + _mouseActionsToDo.Clear(); + return false; + } + + var normalizedHitPath = TransformStatus.GetOrCreateTransformStatus(raycastHit.transform).NormalizedPath; + + // this handles cases like in bossroom where the collider is on EntranceStaticNetworkObjects/BreakablePot , but the renderer is on EntranceStaticNetworkObjects/BreakablePot/pot + if (!mouseAction.clickedObjectNormalizedPaths[0].StartsWith(normalizedHitPath)) + { + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{normalizedHitPath}"; + _mouseActionsToDo.Clear(); + return false; + } } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index baf8c335..e037a2f0 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -11,22 +11,15 @@ namespace RegressionGames.StateRecorder { public class MouseInputActionObserver : MonoBehaviour { - private Type URPType = Type.GetType("UnityEngine.Rendering.Universal.UniversalAdditionalCameraData", false); - private MouseInputActionData _priorMouseState; - // limit this to 5 hits... any hit should be good enough to guess the proper screen x,y based on precise world x,y,z .. but sometimes - // we get slight positional variances in games from run to run and need to account for edge cases where objects get hidden by other objects - // based on a few pixel shift in relative camera position - private readonly RaycastHit[] _cachedRaycastHits = new RaycastHit[5]; - - private readonly HashSet _clickedObjectNormalizedPaths = new(100); + private readonly Dictionary _clickedObjectNormalizedPaths = new(100); private readonly Comparer _mouseHitComparer = Comparer.Create( (x1, x2) => { var d = x1.distance - x2.distance; - return (d > 0) ? 1 : (d < 0) ? -1 : 0; + return (d > 0f) ? 1 : (d < 0f) ? -1 : 0; } ); public void ObserveMouse(IEnumerable statefulObjects) @@ -43,58 +36,60 @@ public void ObserveMouse(IEnumerable statefulObjects) Vector3? worldPosition = null; var clickedOnObjects = FindObjectsAtPosition(newMouseState.position, statefulObjects, out var maxZDepth); - var mouseRayHits = 0; + var didMouseRayHit = false; + + RaycastHit hitInfo = default; var mainCamera = Camera.main; if (mainCamera != null) { var ray = mainCamera.ScreenPointToRay(mousePosition); - mouseRayHits = Physics.RaycastNonAlloc(ray, - _cachedRaycastHits, - maxZDepth * 2f + 1f); // make sure we go deep enough to hit the collider on that object.. we hope - } - - if (mouseRayHits > 0) - { - // order raycast hits by distance from camera - Array.Sort(_cachedRaycastHits, 0, mouseRayHits, _mouseHitComparer); + didMouseRayHit = Physics.Raycast(ray, out hitInfo); // make sure we go deep enough to hit the collider on that object.. we hope } _clickedObjectNormalizedPaths.Clear(); - var bestIndex = int.MaxValue; - if (mouseRayHits > 0) + if (didMouseRayHit) { + // we hit a collider and can compute the world position clicked + // we can also then sort the results based on said collision hit so that the best objects are in the front foreach (var clickedTransformStatus in clickedOnObjects) { - _clickedObjectNormalizedPaths.Add(clickedTransformStatus.NormalizedPath); + var zDepth = clickedTransformStatus.screenSpaceZOffset; if (clickedTransformStatus.worldSpaceBounds != null) { - // compare to any raycast hits and pick the one closest to the camera to set the world position - if (bestIndex > 0) + var rayHit = hitInfo; + try { - for (var i = 0; i < mouseRayHits; i++) + var rayHitObjectStatus = TransformStatus.GetOrCreateTransformStatus(rayHit.transform); + // this handles cases like in bossroom where the collider is on EntranceStaticNetworkObjects/BreakablePot , but the renderer is on EntranceStaticNetworkObjects/BreakablePot/pot + if (clickedTransformStatus.NormalizedPath.StartsWith(rayHitObjectStatus.NormalizedPath)) { - var rayHit = _cachedRaycastHits[i]; - try - { - if (rayHit.transform.GetInstanceID() == clickedTransformStatus.Id) - { - if (i < bestIndex) - { - worldPosition = rayHit.point; - bestIndex = i; - } - - break; - } - } - catch (Exception e) - { - RGDebug.LogException(e, "Exception handling raycast hits for in game object click"); - } + worldPosition = rayHit.point; + // we do some fuzzy math here... we know for sure we hit this object first, but ... if we just use its hit distance that may not sort + // correctly with the zDepth of other 3d objects.. there could be a giant box and we're clicking on something just on the back top edge of it + // but the front of the box is wayy closer to us... but.. isn't obstructing that click... so for this optimization + // we know ui overlay things are at depth 0f... so we put this just behind that, but should be in front of anything else + zDepth = 0.00001f; // Logic with these numbers is also in TransformObjectFinder.. this value needs to be CLOSER from the camera than the one in TransformObjectFinder } } + catch (Exception e) + { + RGDebug.LogException(e, "Exception handling raycast hits for in game object click"); + } + } + + // track the shortest z depth + if (!_clickedObjectNormalizedPaths.TryGetValue(clickedTransformStatus.NormalizedPath, out var depth)) + { + _clickedObjectNormalizedPaths[clickedTransformStatus.NormalizedPath] = zDepth; + } + else + { + if (zDepth < depth) + { + _clickedObjectNormalizedPaths[clickedTransformStatus.NormalizedPath] = zDepth; + } } } } @@ -103,12 +98,24 @@ public void ObserveMouse(IEnumerable statefulObjects) // without a collider hit, we can't set a worldPosition foreach (var recordedGameObjectState in clickedOnObjects) { - _clickedObjectNormalizedPaths.Add(recordedGameObjectState.NormalizedPath); + // track the shortest z depth + if (!_clickedObjectNormalizedPaths.TryGetValue(recordedGameObjectState.NormalizedPath, out var depth)) + { + _clickedObjectNormalizedPaths[recordedGameObjectState.NormalizedPath] = recordedGameObjectState.screenSpaceZOffset; + } + else + { + if (recordedGameObjectState.screenSpaceZOffset < depth) + { + _clickedObjectNormalizedPaths[recordedGameObjectState.NormalizedPath] = recordedGameObjectState.screenSpaceZOffset; + } + } } } newMouseState.worldPosition = worldPosition; - newMouseState.clickedObjectNormalizedPaths = _clickedObjectNormalizedPaths.ToArray(); + // order by zDepth - this should keep UI elements that were already sorted in the same relative positions as they have exactly 0f zDepths + newMouseState.clickedObjectNormalizedPaths = _clickedObjectNormalizedPaths.OrderBy(kvp => kvp.Value).Select(a => a.Key).ToArray(); _mouseInputActions.Enqueue(newMouseState); } else if (_priorMouseState?.PositionsEqual(newMouseState) != true) @@ -233,7 +240,7 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer if (recordedGameObjectState.screenSpaceBounds.HasValue) { // make sure the z offset is actually in front of the camera - if (recordedGameObjectState.screenSpaceBounds.Value.Contains(vec3Position) && recordedGameObjectState.screenSpaceZOffset >-0.00001f) + if (recordedGameObjectState.screenSpaceBounds.Value.Contains(vec3Position) && recordedGameObjectState.screenSpaceZOffset >= 0f) { // filter out to only world space objects or interactable UI objects diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs index 1acaa38b..7aa25796 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs @@ -381,9 +381,9 @@ public static (Bounds?, float, Bounds?) SelectBoundsForTransform(Camera mainCame zOffset = Math.Max(min.z, max.z); } // don't let world space objects be <= 0.0f as they would appear on top of true ui overlay objects in our processing - if (zOffset < 0.00001f) + if (zOffset <= 0f) { - zOffset = 0.0001f; + zOffset = 0.0001f; // Logic with these numbers is also in MouseInputActionObserver.. this value needs to be further from the camera than the one in MouseInputActionObserver } @@ -584,9 +584,9 @@ public static (Bounds?, float, Bounds?) SelectBoundsForTransform(Camera mainCame zOffset = Math.Max(minZ, maxZ); } // don't let world space objects be <= 0.0f as they would appear on top of true ui overlay objects in our processing - if (zOffset < 0.00001f) + if (zOffset <= 0f) { - zOffset = 0.0001f; + zOffset = 0.0001f; // Logic with these numbers is also in MouseInputActionObserver.. this value needs to be further from the camera than the one in MouseInputActionObserver } // get the screen point values for the world max / min and find the screen space z offset closest the camera From 39b55b95705a81d1d792d7fea8f0babfaf786e64 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Mon, 9 Dec 2024 13:15:57 -0500 Subject: [PATCH 12/31] Added exact match best compares +click hold times ... object selection on click hold broken right now !!! --- .../BotSegments/ActionExplorationDriver.cs | 69 +- .../BotSegmentsPlaybackController.cs | 15 +- .../BotActions/KeyMomentMouseActionData.cs | 658 +++++++++++------- .../Models/MouseInputActionData.cs | 2 + 4 files changed, 463 insertions(+), 281 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index c7ddaa0d..0967c75a 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -69,6 +69,8 @@ public void StartExploring() } } + private IBotActionData _inProgressAction = null; + public void PerformExploratoryAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) { error = null; @@ -77,32 +79,36 @@ public void PerformExploratoryAction(int segmentNumber, Dictionary= _previousActions.Count) + if (nextAction == null) { - _previousActionIndex = 0; - } + if (_previousActionIndex >= _previousActions.Count) + { + _previousActionIndex = 0; + } - // retry prior actions in pattern .. 0, 10, 210 where 2 is the oldest and 0 is the most recent - if (_previousActionIndex < _previousActions.Count) - { - var previousActions = _previousActions[_previousActionIndex]; - if (_previousActionSubIndex >= previousActions.Count) + // retry prior actions in pattern .. 0, 10, 210 where 2 is the oldest and 0 is the most recent + if (_previousActionIndex < _previousActions.Count) { - // move to the next list - _previousActionSubIndex = 0; - ++_previousActionIndex; - if (_previousActionIndex >= _previousActions.Count) + var previousActions = _previousActions[_previousActionIndex]; + if (_previousActionSubIndex >= previousActions.Count) { - _previousActionIndex = 0; + // move to the next list + _previousActionSubIndex = 0; + ++_previousActionIndex; + if (_previousActionIndex >= _previousActions.Count) + { + _previousActionIndex = 0; + } + + previousActions = _previousActions[_previousActionIndex]; } - previousActions = _previousActions[_previousActionIndex]; - } - nextAction = previousActions[_previousActionSubIndex]; - ++_previousActionSubIndex; - // process next action in the list + nextAction = previousActions[_previousActionSubIndex]; + ++_previousActionSubIndex; + // process next action in the list + } } if (nextAction != null) @@ -115,10 +121,22 @@ public void PerformExploratoryAction(int segmentNumber, Dictionary()?.SetKeyFrameWarningText(loggedMessage); if (pauseEditorOnPlaybackWarning) @@ -514,7 +521,7 @@ private void EvaluateBotSegments() catch (Exception ex) { var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Exception processing BotAction\r\n" + ex.Message; - LogPlaybackWarning(loggedMessage); + LogPlaybackWarning(loggedMessage, ex); // uncaught exception... stop and unload the segment UnloadSegmentsAndReset(); throw; @@ -638,7 +645,7 @@ private void EvaluateBotSegments() catch (Exception ex) { var loggedMessage = $"(?) - Bot Segment - Exception processing BotSegments\r\n" + ex.Message; - LogPlaybackWarning(loggedMessage); + LogPlaybackWarning(loggedMessage, ex); // uncaught exception... stop the segment UnloadSegmentsAndReset(); throw; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 7d6612b2..1c7471b7 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -102,6 +102,9 @@ public void StartAction(int segmentNumber, Dictionary curren // should tokenize to something like [HeroesObjects],[Unit,Hero,Gunner],[Spine,Mecanim,GameObject,unit,hero,gunner,RenderTexture] // thus any of the following examples... preference given to the one with the highest tokens matched.. note that tokens will ONLY match when IN ORDER // MATCH[1,3,7] (perfect) - "HeroesObjects/Unit_Hero_Gunner/Spine Mecanim GameObject (unit_hero_gunner) RenderTexture" + // -- but what if there are 2 or more 'perfect' matches.. then we need to narrow down based on the # of other path elements that overlap each of these + // MATCH[1,3,7] (perfect) - "HeroesObjects/Unit_Hero_Gunner/Spine Mecanim GameObject (unit_hero_gunner) RenderTexture" + // MATCH[1,3,7] (perfect) - "HeroesObjects/Unit_Hero_Gunner/Spine Mecanim GameObject (unit_hero_gunner) RenderTexture" // -- these next two are 'equal' matches.. chooses first one encountered // MATCH[1,3,6] - "HeroesObjects/Unit_Hero_Gunner/Foot Mecanim GameObject (unit_hero_gunner) RenderTexture" // MATCH[1,3,6] - "HeroesObjects/Unit_Hero_Gunner/Spleen Mecanim GameObject (unit_hero_gunner) RenderTexture" @@ -150,8 +153,6 @@ public void StartAction(int segmentNumber, Dictionary curren return (null,null); } - var valid = true; - var segmentCount = toMatch.Count; var result = (new int[segmentCount], new int[segmentCount]); @@ -232,7 +233,9 @@ public void StartAction(int segmentNumber, Dictionary curren } // we queue up all the actions to do before doing them in case we can't do one based on paths.. we don't want a partial operation - private readonly List _mouseActionsToDo = new(); + private readonly List<(Action, double)> _mouseActionsToDo = new(); + + private double _clickDownTime = 0d; public bool ProcessAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) { @@ -241,20 +244,39 @@ public bool ProcessAction(int segmentNumber, Dictionary curr _isStopped = true; } + if (_mouseActionsToDo.Count > 0) + { + // wait until the right time, then do the un-click mouse action + if (Time.unscaledTimeAsDouble >= _mouseActionsToDo[0].Item2) + { + _mouseActionsToDo[0].Item1.Invoke(); + _mouseActionsToDo.Clear(); + _isDoneWaitOneFrame = true; + + error = null; + return true; + } + else + { + error = null; + return false; + } + } + _mouseActionsToDo.Clear(); if (!_isStopped) { - var mouseActionsCount = mouseActions.Count; - MouseInputActionData pendingAction = null; Vector2? lastClickPosition = null; - (ObjectStatus, (int[],int[]))[] previousPreconditionMatches = null; + List previousOverlappingObjects = new(); + var mouseActionsCount = mouseActions.Count; for (var m = 0; m < mouseActionsCount; m++) { + List overlappingObjects = new(); var mouseAction = mouseActions[m]; var preConditionNormalizedPaths = _preconditionNormalizedPaths[m]; @@ -264,86 +286,141 @@ public bool ProcessAction(int segmentNumber, Dictionary curr continue; // the for - basically.. this is a mouse positional update, but we need the next click to know which position to put it in } - var possibleTransformsToClick = currentTransforms.Values.Where(a => a.screenSpaceBounds.HasValue).ToList(); + var preconditionMatches = BuildPreconditions(segmentNumber, currentTransforms, currentEntities, preConditionNormalizedPaths); - var preconditionsLength = preConditionNormalizedPaths.Count; + // now that we have all the precondition matches mapped out, let's see if we have the leftmost object.. - var preconditionMatches = new (ObjectStatus, (int[],int[]))[preconditionsLength]; - for (var i = 0; i < preconditionsLength; i++) + // if the leftmost object is a UI object, then it should already be the smallest / most precise UI thing to click as we solve this by sorting + // the UI elements in mouseinputactionobserver.FindObjectsAtPosition based on the smallest screen-space bounds + if (preconditionMatches[0].Count > 0) { - preconditionMatches[i] = (null, (null,null)); - } + // yay.. we found something(s).. figure out which one of them is the 'best' based on number of overlapping matches - foreach (var possibleTransformToClick in possibleTransformsToClick) - { - possibleTransformToClick.TokenizedObjectPath ??= PreconditionNormalizedPathData.TokenizeObjectPath(possibleTransformToClick.NormalizedPath); + // for each of the left most preconditionMatches[0] .. go through each of other precondition matches and count how many overlap.. can only count 0 or 1 per each index + // we will also compute the 'smallest' click bounds for these overlaps as we go + var matchResults = new List<(ObjectStatus, int, (int,int,int,int))>(preconditionMatches[0].Count); - var breakoutCount = 0;// optimization for when we've found exact matches for all preconditions - // this should nearly always be smaller than the # of possibleTransformToClick - for (var j = 0; j < preconditionsLength; j++) + foreach (var preconditionMatch0 in preconditionMatches[0]) { - // optimization - only keep processing this precondition when we didn't have an exact match for it already - if (preconditionMatches[j].Item1 == null || preconditionMatches[j].Item2.Item1 != null) + var smallestBounds = preconditionMatch0.Item1.screenSpaceBounds.Value; + // adjust in for the min to ensure we don't miss a click + var minX = (int)(smallestBounds.min.x + 0.51f); + var minY = (int)(smallestBounds.min.y + 0.51f); + // adjust in for the max to ensure we don't miss a click + var maxX = (int)(smallestBounds.max.x - 0.51f); + var maxY = (int)(smallestBounds.max.y - 0.51f); + + RGDebug.LogDebug($"Starting with bounds: ({minX}, {minY}),({maxX}, {maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} , target object: {preconditionMatch0.Item1.NormalizedPath}"); + // now let's narrow down the screen space bounds more precisely based on all our preconditions + // count the number of indexes where we had an overlap while doing it + var overlapCount = 0; + for (var i = 1; i < preconditionMatches.Length; i++) { - var precondition = preConditionNormalizedPaths[j]; - - // prefer exact path match (which we don't have available here... yet), then normalized path match, then tokenized path matching logic - if (precondition.normalizedPath == possibleTransformToClick.NormalizedPath) - { - // normalized matching logic - preconditionMatches[j] = (possibleTransformToClick, (null, null)); - break; // the for - } - // else + var preconditionMatchesI = preconditionMatches[i]; + if (preconditionMatchesI.Count > 0) { - // tokenized matching logic - var tokenMatches = EvaluateTokenMatches(precondition.tokenData, possibleTransformToClick.TokenizedObjectPath); - if (tokenMatches.Item1 != null) + var didOverlap = false; + foreach (var preconditionMatchI in preconditionMatchesI) { - // got something of the same path length with some token matches in each part - if (preconditionMatches[j].Item1 != null) + // not the same logic as MouseEventSender.FindBestClickObject.. this version narrows in on the smallest bounding area + + // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds + var newBounds = preconditionMatchI.Item1.screenSpaceBounds.Value; + // adjust in for the min to ensure we don't miss a click + var newMinX = (int)(newBounds.min.x + 0.51f); + var newMinY = (int)(newBounds.min.y + 0.51f); + // adjust in for the max to ensure we don't miss a click + var newMaxX = (int)(newBounds.max.x - 0.51f); + var newMaxY = (int)(newBounds.max.y - 0.51f); + + // since these are pixel bounds... we just do (int) cast + var startingMinX = minX; + var startingMinY = minY; + var startingMaxX = maxX; + var startingMaxY = maxY; + + if (newMinX > minX && newMinX < maxX) { - // compare - if (IsNewTokenMatchesBetter(preconditionMatches[j].Item2, tokenMatches)) - { - preconditionMatches[j] = (possibleTransformToClick, tokenMatches); - } + minX = newMinX; } - else + + if (newMinY > minY && newMinY < maxY) { - //set - preconditionMatches[j] = (possibleTransformToClick, tokenMatches); + minY = newMinY; + } + + if (newMaxX > minX && newMaxX < maxX) + { + maxX = newMaxX; + } + + if (newMaxY > minY && newMaxY < maxY) + { + maxY = newMaxY; } - break; // the for + if (minX != startingMinX || minY != startingMinY || maxX != startingMaxX || maxY != startingMaxY) + { + overlappingObjects.Add(preconditionMatchI.Item1); + didOverlap = true; + RGDebug.LogDebug($"Narrowed bounds: ({minX}, {minY}),({maxX}, {maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} for overlap with object path [{i}]: {preconditionMatchI.Item1.NormalizedPath}"); + } + } + + if (didOverlap) + { + ++overlapCount; } } - } else - { - ++breakoutCount; } + + matchResults.Add((preconditionMatch0.Item1, overlapCount, (minX,minY,maxX,maxY))); + } - if (breakoutCount == preconditionsLength) + matchResults.Sort((a, b) => { - break; // the foreach - we found exact matches for everything - } - } + if (a.Item2 < b.Item2) + { + // sort lower match counts to the end + return 1; + } - // now that we have all the precondition matches mapped out, let's see if we have the leftmost object.. + if (a.Item2 > b.Item2) + { + // sort highest match counts to the front + return -1; + } - // if the leftmost object is a UI object, then it should already be the smallest / most precise UI thing to click as we solve this by sorting - // the UI elements in mouseinputactionobserver.FindObjectsAtPosition based on the smallest screen-space bounds - if (preconditionMatches[0].Item1 != null) - { - // yay.. we found something.. first let's make sure it's ready to be clicked - // visible (already true by the time we get here)/active-enabled(we already know that from a canvas perspective this is visible, but need to check UI component info) + // else sort by smallest bounds + var aArea = (a.Item3.Item3 - a.Item3.Item1) * (a.Item3.Item4 - a.Item3.Item2); + var bArea = (b.Item3.Item3 - b.Item3.Item1) * (b.Item3.Item4 - b.Item3.Item2); - var oStatus = preconditionMatches[0].Item1; + if (aArea < bArea) + { + return -1; + } - var isInteractable = true; + // floating point math.. don't really care about equality in the zillionth of a percent chance that happens here + return 1; + }); + + var bestObjectStatus = matchResults[0].Item1; + // we started with clicking the center.. but this was limiting + //clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); + // instead... make the click-position a random position within the bounds + // 1. for more variability in testing + // 2. to get around cases where we were say clicking on a floor tile, but there is something on that floor tile and we wanted to click on the open space of the floor tile + // on the next attempt, it picks a new position to try thus giving us a better chance of passing + // TODO: Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? + // This would allow us to know that we clicked about X far into this floor tile and replicate that positioning regardless of the actual worldspace positioning in the replay... + // +1 because int range is max exclusive.. if these were floats.. remove the +1 + Vector2 clickPosition = new Vector2(Random.Range(matchResults[0].Item3.Item1, matchResults[0].Item3.Item3+1), Random.Range(matchResults[0].Item3.Item2, matchResults[0].Item3.Item4+1)); - if (oStatus is TransformStatus tStatus) + var isInteractable = true; + // now that we have the 'best' match ... first let's make sure it's ready to be clicked + // visible (already true by the time we get here)/active-enabled(we already know that from a canvas perspective this is visible, but need to check UI component info) + if (bestObjectStatus is TransformStatus tStatus) { var theTransform = tStatus.Transform; if (theTransform is RectTransform) @@ -355,245 +432,301 @@ public bool ProcessAction(int segmentNumber, Dictionary curr } } - if (isInteractable) + if (!isInteractable) { - Vector2? clickPosition = null; - - // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds - var smallestBounds = preconditionMatches[0].Item1.screenSpaceBounds.Value; - var minX = smallestBounds.min.x; - var minY = smallestBounds.min.y; - var maxX = smallestBounds.max.x; - var maxY = smallestBounds.max.y; + _mouseActionsToDo.Clear(); + // didn't find it.. this is where 'exploration' is going to start happening based on our result + error = $"No valid mouse action object found for path:\n{preConditionNormalizedPaths[0].normalizedPath}"; + return false; + } - if (lastClickPosition.HasValue) + // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not + // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds + if (lastClickPosition.HasValue) + { + if (lastClickPosition.Value.x >= matchResults[0].Item3.Item1 + && lastClickPosition.Value.x <= matchResults[0].Item3.Item3 + && lastClickPosition.Value.y >= matchResults[0].Item3.Item2 + && lastClickPosition.Value.y <= matchResults[0].Item3.Item4) + { + RGDebug.LogDebug($"Leaving mouse action index {m} at previous action position based on bounds overlaps to original path: {bestObjectStatus.NormalizedPath}"); + // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the + // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame + clickPosition = lastClickPosition.Value; + } + else if (previousOverlappingObjects.Count > 0) { - if (lastClickPosition.Value.x >= minX - && lastClickPosition.Value.x <= maxX - && lastClickPosition.Value.y >= minY - && lastClickPosition.Value.y <= maxY) + // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) + // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button + // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen + // isn't important.. we want to un-click exactly where we clicked + if (previousOverlappingObjects.Any(a => a == bestObjectStatus)) { - RGDebug.LogDebug($"Leaving mouse action index {m} at previous action position based on bounds overlaps to original path: {preconditionMatches[0].Item1.NormalizedPath}"); - // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the - // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame + RGDebug.LogDebug($"Leaving mouse action index {m} at previous action position based on object path existing at time of click: {bestObjectStatus.NormalizedPath}"); clickPosition = lastClickPosition.Value; } - else if (previousPreconditionMatches != null) - { - // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) - // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button - // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen - // isn't important.. we want to un-click exactly where we clicked - if (previousPreconditionMatches.Any(a => a.Item1 == preconditionMatches[0].Item1)) - { - RGDebug.LogDebug($"Leaving mouse action index {m} at previous action position based on object path existing at time of click: {preconditionMatches[0].Item1.NormalizedPath}"); - clickPosition = lastClickPosition.Value; - } - } } - if (!clickPosition.HasValue) - { - RGDebug.LogDebug($"Starting with bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} , target object: {preconditionMatches[0].Item1.NormalizedPath}"); - // now let's narrow down the screen space bounds more precisely based on all our preconditions - for (var i = 1; i < preconditionMatches.Length; i++) - { - var preconditionMatch = preconditionMatches[i]; - if (preconditionMatch.Item1 != null) - { - // not the same logic as MouseEventSender.FindBestClickObject.. this version narrows in on the smallest bounding area + } - // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds - var newBounds = preconditionMatch.Item1.screenSpaceBounds.Value; - var newMinX = newBounds.min.x; - var newMinY = newBounds.min.y; - var newMaxX = newBounds.max.x; - var newMaxY = newBounds.max.y; + if (!QueueClickForObjectAtPosition(segmentNumber, pendingAction, mouseAction, bestObjectStatus, clickPosition, out error)) + { + _mouseActionsToDo.Clear(); + return false; + } - if (newMinX > minX && newMinX < maxX) - { - minX = newMinX; - } + if (m == 1) + { + previousOverlappingObjects = overlappingObjects; + // on the 'click'.. save the position + lastClickPosition = clickPosition; + } - if (newMaxX > minX && newMaxX < maxX) - { - maxX = newMaxX; - } + pendingAction = null; + } + else + { + _mouseActionsToDo.Clear(); + // didn't find it.. this is where 'exploration' is going to start happening based on our result + error = $"No valid mouse action object found for paths:\n{string.Join("\n", preConditionNormalizedPaths.Select(a => a.normalizedPath))}"; + return false; + } + } - if (newMinY > minY && newMinY < maxY) - { - minY = newMinY; - } + var _mouseActionsToDoCount = _mouseActionsToDo.Count; + if (_mouseActionsToDoCount > 0) + { + // only do the first 2 actions + for (var i = 0; i < 2; i++) + { + _mouseActionsToDo[i].Item1.Invoke(); + } + // remove the first 2 things we already did + _mouseActionsToDo.RemoveRange(0,2); - if (newMaxY > minY && newMaxY < maxY) - { - maxY = newMaxY; - } - RGDebug.LogDebug($"Narrowed bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} for overlap with object path: {preconditionMatch.Item1.NormalizedPath}"); - } - } + error = null; + return true; + } - // we started with clicking the center.. but this was limiting - //clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); - // instead... make the click-position a random position within the bounds - // 1. for more variability in testing - // 2. to get around cases where we were say clicking on a floor tile, but there is something on that floor tile and we wanted to click on the open space of the floor tile - // on the next attempt, it picks a new position to try thus giving us a better chance of passing - // TODO: Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? - // This would allow us to know that we clicked about X far into this floor tile and replicate that positioning regardless of the actual worldspace positioning in the replay... - clickPosition = new Vector2(Random.Range(minX+0.00001f, maxX-0.00001f), Random.Range(minY+0.00001f, maxY-0.00001f)); - } + } - var myClickPosition = clickPosition.Value; // we fill 'clickPosition' in for all code paths.. if you get a null here, something above has bad logic as this should be filled in always here - lastClickPosition = myClickPosition; + error = null; + return false; + } - if (pendingAction != null) - { - // we are on the 2nd action, which is the 'click' + private List<(ObjectStatus, (int[], int[]))>[] BuildPreconditions(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, List preConditionNormalizedPaths) + { + var possibleTransformsToClick = currentTransforms.Values.Where(a => a.screenSpaceBounds.HasValue).ToList(); + var preconditionsLength = preConditionNormalizedPaths.Count; + var preconditionMatches = new List<(ObjectStatus, (int[],int[]))>[preconditionsLength]; + for (var i = 0; i < preconditionsLength; i++) + { + preconditionMatches[i] = new List<(ObjectStatus, (int[], int[]))>(); + } - // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over - // our intended click item (iow.. it's there/ready, but obstructed) ... we only sort by zDepth(not UI bounds) here so we can find proper obstructions + foreach (var possibleTransformToClick in possibleTransformsToClick) + { + possibleTransformToClick.TokenizedObjectPath ??= PreconditionNormalizedPathData.TokenizeObjectPath(possibleTransformToClick.NormalizedPath); - if (oStatus is TransformStatus transformStatus) - { + var breakoutCount = 0;// optimization for when we've found exact matches for all preconditions + // this should nearly always be smaller than the # of possibleTransformToClick + for (var j = 0; j < preconditionsLength; j++) + { + // optimization - only keep processing this precondition when we didn't have an exact match for it already + if (preconditionMatches[j].Count == 0 || preconditionMatches[j][0].Item2.Item1 != null) + { + var precondition = preConditionNormalizedPaths[j]; - if (transformStatus.worldSpaceBounds == null) + // prefer normalized path match, then tokenized path matching logic + if (precondition.normalizedPath == possibleTransformToClick.NormalizedPath) + { + // normalized matching logic + if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 != null) + { + // wipe out all the tokenized matches if we get an exact + preconditionMatches[j].Clear(); + } + preconditionMatches[j].Add((possibleTransformToClick, (null, null))); + break; // the for + } + else + { + // tokenized matching logic + if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 == null) + { + // we already have exact matches.. ignore the tokenized ones + break; // the for + } + var tokenMatches = EvaluateTokenMatches(precondition.tokenData, possibleTransformToClick.TokenizedObjectPath); + if (tokenMatches.Item1 != null) + { + // got something of the same path length with some token matches in each part + if (preconditionMatches[j].Count > 0) + { + var isBetter = AreNewTokenMatchesBetter(preconditionMatches[j][0].Item2, tokenMatches); + // compare + if (true == isBetter) { - // UI element.. make sure it is the highest UI thing hit - - var eventSystemHits = new List(); - // ray-cast into the scene and see if this object is the first thing hit - var pointerEventData = new PointerEventData(EventSystem.current) - { - button = PointerEventData.InputButton.Left, - position = myClickPosition - }; - EventSystem.current.RaycastAll(pointerEventData, eventSystemHits); - - if (eventSystemHits.Count > 0) - { - eventSystemHits.Sort((a,b) => - { - if (a.distance < b.distance) - { - return -1; - } - - if (a.distance > b.distance) - { - return 1; - } - - // higher depth == closer to camera.. don't even get me started - return b.depth - a.depth; - }); - } - if (eventSystemHits.Count <= 0) - { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\nno selectable UI objects detected at point"; - _mouseActionsToDo.Clear(); - return false; - } - else - { - var hitNormalizedPath = TransformStatus.GetOrCreateTransformStatus(eventSystemHits[0].gameObject.transform).NormalizedPath; - if (!hitNormalizedPath.StartsWith(mouseAction.clickedObjectNormalizedPaths[0])) - { - // if this isn't the exact object or a child object (like a button text of the button we're clicking).. then error out - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{hitNormalizedPath}"; - _mouseActionsToDo.Clear(); - return false; - } - } + // better match.. clear the list + preconditionMatches[j].Clear(); + preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); } - else + else if (isBetter == null) { - // Handle world space obstructions... this is easier, since any overlay UI element will be in the path list over world objects, we just need to make sure our - // object is at the front of the list and thus at the closest Z depth at that point + // equal match.. add to the list + preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); + } + // else worse match.. leave it alone + } + else + { + //set the first match + preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); + } - // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over - // our intended click item (iow.. it's there/ready, but obstructed) ... - // we have to be careful though, because the 'zDepth' computed for the object may NOT be the zDepth at this exact click point on said object... - //var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); + break; // the for + } + } + } + else + { + ++breakoutCount; + } + } - var mainCamera = Camera.main; - if (mainCamera != null) - { - var ray = mainCamera.ScreenPointToRay(myClickPosition); - - var didHit = Physics.Raycast(ray, out var raycastHit); - - if (!didHit) - { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\nno objects at position"; - _mouseActionsToDo.Clear(); - return false; - } - - var normalizedHitPath = TransformStatus.GetOrCreateTransformStatus(raycastHit.transform).NormalizedPath; - - // this handles cases like in bossroom where the collider is on EntranceStaticNetworkObjects/BreakablePot , but the renderer is on EntranceStaticNetworkObjects/BreakablePot/pot - if (!mouseAction.clickedObjectNormalizedPaths[0].StartsWith(normalizedHitPath)) - { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{normalizedHitPath}"; - _mouseActionsToDo.Clear(); - return false; - } - } - } + if (breakoutCount == preconditionsLength) + { + break; // the foreach - we found exact matches for everything + } + } + + return preconditionMatches; + } + private bool QueueClickForObjectAtPosition(int segmentNumber, MouseInputActionData pendingAction, MouseInputActionData mouseAction, ObjectStatus bestObjectStatus, Vector2 myClickPosition, out string error) + { + // we fill 'myClickPosition' in for all code paths.. if you get a null here, something above has bad logic as this should be filled in always here + if (pendingAction != null) + { + // we are on the 2nd action, which is the 'click' + + // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over + // our intended click item (iow.. it's there/ready, but obstructed) ... we only sort by zDepth(not UI bounds) here so we can find proper obstructions + + if (bestObjectStatus is TransformStatus transformStatus) + { + + if (transformStatus.worldSpaceBounds == null) + { + // UI element.. make sure it is the highest UI thing hit + + var eventSystemHits = new List(); + // ray-cast into the scene and see if this object is the first thing hit + var pointerEventData = new PointerEventData(EventSystem.current) + { + button = PointerEventData.InputButton.Left, + position = myClickPosition + }; + EventSystem.current.RaycastAll(pointerEventData, eventSystemHits); + + if (eventSystemHits.Count > 0) + { + eventSystemHits.Sort((a,b) => + { + if (a.distance < b.distance) + { + return -1; } - var myPendingAction = pendingAction; - _mouseActionsToDo.Add(() => + if (a.distance > b.distance) { - RGDebug.LogInfo($"KeyMoment - Mouse Pending Action applied at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); - // perform the mouse action at the center of our new smallest bounds - MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, myPendingAction.leftButton, myPendingAction.middleButton, myPendingAction.rightButton, myPendingAction.forwardButton, myPendingAction.backButton, myPendingAction.scroll); - }); - } + return 1; + } - _mouseActionsToDo.Add(() => - { - RGDebug.LogInfo($"KeyMoment - Mouse Action at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); - // perform the mouse action at the center of our new smallest bounds - MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, mouseAction.leftButton, mouseAction.middleButton, mouseAction.rightButton, mouseAction.forwardButton, mouseAction.backButton, mouseAction.scroll); + // higher depth == closer to camera.. don't even get me started + return b.depth - a.depth; }); - pendingAction = null; } - else + if (eventSystemHits.Count <= 0) { - _mouseActionsToDo.Clear(); - // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse action object found for paths:\n{string.Join("\n", preConditionNormalizedPaths.Select(a => a.normalizedPath))}"; + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\nno selectable UI objects detected at point"; return false; } + else + { + var hitNormalizedPath = TransformStatus.GetOrCreateTransformStatus(eventSystemHits[0].gameObject.transform).NormalizedPath; + if (!hitNormalizedPath.StartsWith(mouseAction.clickedObjectNormalizedPaths[0])) + { + // if this isn't the exact object or a child object (like a button text of the button we're clicking).. then error out + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{hitNormalizedPath}"; + return false; + } + } } else { - _mouseActionsToDo.Clear(); - // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse action object found for path:\n{preConditionNormalizedPaths[0].normalizedPath}"; - return false; + // Handle world space obstructions... this is easier, since any overlay UI element will be in the path list over world objects, we just need to make sure our + // object is at the front of the list and thus at the closest Z depth at that point + + // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over + // our intended click item (iow.. it's there/ready, but obstructed) ... + // we have to be careful though, because the 'zDepth' computed for the object may NOT be the zDepth at this exact click point on said object... + //var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); + + var mainCamera = Camera.main; + if (mainCamera != null) + { + var ray = mainCamera.ScreenPointToRay(myClickPosition); + + var didHit = Physics.Raycast(ray, out var raycastHit); + + if (!didHit) + { + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\nno objects at position"; + return false; + } + + var normalizedHitPath = TransformStatus.GetOrCreateTransformStatus(raycastHit.transform).NormalizedPath; + + // this handles cases like in bossroom where the collider is on EntranceStaticNetworkObjects/BreakablePot , but the renderer is on EntranceStaticNetworkObjects/BreakablePot/pot + if (!mouseAction.clickedObjectNormalizedPaths[0].StartsWith(normalizedHitPath)) + { + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{normalizedHitPath}"; + return false; + } + } } + } + + var myPendingAction = pendingAction; + _mouseActionsToDo.Add((() => + { + RGDebug.LogInfo($"KeyMoment - Mouse Pending Action applied at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); + // perform the mouse action at the center of our new smallest bounds + MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, myPendingAction.leftButton, myPendingAction.middleButton, myPendingAction.rightButton, myPendingAction.forwardButton, myPendingAction.backButton, myPendingAction.scroll); + }, 0d)); + } - if (_mouseActionsToDo.Count > 0) + _mouseActionsToDo.Add((() => { - foreach (var action in _mouseActionsToDo) - { - action.Invoke(); - } - _isDoneWaitOneFrame = true; - error = null; - return true; + RGDebug.LogInfo($"KeyMoment - Mouse Action at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); + // perform the mouse action at the center of our new smallest bounds + MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, mouseAction.leftButton, mouseAction.middleButton, mouseAction.rightButton, mouseAction.forwardButton, mouseAction.backButton, mouseAction.scroll); + // handle setting the delay time between the click down and up for the [2] index item + }, _clickDownTime == 0d ? 0d : Time.unscaledTimeAsDouble + mouseAction.startTime - _clickDownTime)); + + if (pendingAction != null) + { + // handle setting the delay time between the click down and up for the [1] index item + _clickDownTime = mouseAction.startTime; } error = null; - return false; + return true; } - private bool IsNewTokenMatchesBetter((int[], int[]) oldMatches, (int[], int[]) newMatches) + private bool? AreNewTokenMatchesBetter((int[], int[]) oldMatches, (int[], int[]) newMatches) { if (oldMatches.Item1 == null) { @@ -602,6 +735,7 @@ private bool IsNewTokenMatchesBetter((int[], int[]) oldMatches, (int[], int[]) n } // all 4 lengths are the same at this point var length = oldMatches.Item2.Length; + var exactMatch = true; for (int i = 0; i < length; i++) { if (oldMatches.Item1[i] < newMatches.Item1[i]) @@ -610,15 +744,31 @@ private bool IsNewTokenMatchesBetter((int[], int[]) oldMatches, (int[], int[]) n return true; } + if (oldMatches.Item1[i] != newMatches.Item1[i]) + { + exactMatch = false; + } + if (oldMatches.Item2[i] < newMatches.Item2[i]) { // num token matches in segment is higher return true; } + if (oldMatches.Item2[i] != newMatches.Item2[i]) + { + exactMatch = false; + } + // else move onto next segment } + if (exactMatch) + { + return null; + } + + // oldMatch was better return false; } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs index 921048a8..79c21d81 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs @@ -139,6 +139,8 @@ public void WriteKeyMomentToStringBuilder(StringBuilder stringBuilder) { stringBuilder.Append("{\"apiVersion\":"); IntJsonConverter.WriteToStringBuilder(stringBuilder, apiVersion); + stringBuilder.Append(",\"startTime\":"); + DoubleJsonConverter.WriteToStringBuilder(stringBuilder, startTime); stringBuilder.Append(",\"leftButton\":"); BooleanJsonConverter.WriteToStringBuilder(stringBuilder,leftButton); stringBuilder.Append(",\"middleButton\":"); From b48d0539f0d26d7acb07aec1c2925f59658f5075 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Mon, 9 Dec 2024 16:34:29 -0500 Subject: [PATCH 13/31] partial fix for exploration during click misses --- .../KeyMoments/BotActions/KeyMomentMouseActionData.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 1c7471b7..16082850 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -241,7 +241,11 @@ public bool ProcessAction(int segmentNumber, Dictionary curr { if (_isDoneWaitOneFrame) { + _isDoneWaitOneFrame = false; _isStopped = true; + // need to return true so the bot doesn't start exploring.. it needs to think this action is processing as intended since that wait was intended + error = null; + return true; } if (_mouseActionsToDo.Count > 0) @@ -259,7 +263,8 @@ public bool ProcessAction(int segmentNumber, Dictionary curr else { error = null; - return false; + // need to return true so the bot doesn't start exploring.. it needs to think this action is processing as intended while we wait + return true; } } From a0dad0e1bbab32380a42ef1c49cf517a6ccf41ec Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Tue, 10 Dec 2024 12:13:53 -0500 Subject: [PATCH 14/31] Refactor how we determine z depth and cleanup exploration status reporting --- .../BotSegments/ActionExplorationDriver.cs | 8 +- .../BotSegmentsPlaybackController.cs | 539 +++++++++--------- .../BotSegments/Models/BotAction.cs | 8 +- .../BotSegments/Models/BotSegment.cs | 11 +- .../BotSegments/Models/IBotActionData.cs | 2 +- .../BotActions/KeyMomentMouseActionData.cs | 97 +--- .../StateRecorder/Models/ObjectStatus.cs | 13 + .../StateRecorder/Models/TransformStatus.cs | 18 +- .../StateRecorder/MouseInputActionObserver.cs | 212 +++---- 9 files changed, 446 insertions(+), 462 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index 0967c75a..a869f408 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -44,7 +44,10 @@ public void ReportPreviouslyCompletedAction(IBotActionData action) } } - public void StartExploring() + /** + * Return true only when we start exploring, false if we were already exploring + */ + public bool StartExploring() { if (!IsExploring) { @@ -66,7 +69,10 @@ public void StartExploring() } RGDebug.LogInfo("ActionExplorationDriver - Starting Exploratory Actions"); IsExploring = true; + return true; } + + return false; } private IBotActionData _inProgressAction = null; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs index 6bd01906..06243ef0 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs @@ -60,6 +60,276 @@ public class BotSegmentsPlaybackController : MonoBehaviour private ActionExplorationDriver _explorationDriver; + private void EvaluateBotSegments() + { + var now = Time.unscaledTime; + + try + { + if (_unpaused) + { + _lastTimeLoggedKeyFrameConditions = now; + _unpaused = false; + } + + var objectFinders = FindObjectsByType(FindObjectsSortMode.None); + + Dictionary transformStatuses = null; + Dictionary entityStatuses = null; + + foreach (var objectFinder in objectFinders) + { + if (objectFinder is TransformObjectFinder) + { + transformStatuses = objectFinder.GetObjectStatusForCurrentFrame().Item2; + } + else + { + entityStatuses = objectFinder.GetObjectStatusForCurrentFrame().Item2; + } + } + + transformStatuses ??= new Dictionary(); + entityStatuses ??= new Dictionary(); + + // track if any segment matched this update + var matchedThisUpdate = false; + + // track if we have a new segment to evaluate... so long as we do, keep looping here before releasing from this Update call + // thus we process each new segment as soon as possible and don't have any artificial one frame delays before processing + var nextBotSegmentIndex = 0; + while (nextBotSegmentIndex < _nextBotSegments.Count) + { + // if we're working on the first entry in the list is the only time we do actions + if (nextBotSegmentIndex == 0) + { + BotSegment firstActionSegment = _nextBotSegments[0]; + try + { + if (firstActionSegment.Replay_ActionCompleted) + { + if (firstActionSegment.botAction?.data is IKeyMomentExploration) + { + _explorationDriver.ReportPreviouslyCompletedAction(firstActionSegment.botAction.data); + } + } + else + { + // allow the main action to retry between every exploratory action + var didAction = firstActionSegment.ProcessAction(transformStatuses, entityStatuses, out var error); + + if (error == null) + { + _explorationDriver.StopExploring(firstActionSegment.Replay_SegmentNumber); + + if (didAction) + { + // for every non error action, reset the timer + _lastTimeLoggedKeyFrameConditions = now; + FindObjectOfType()?.SetKeyFrameWarningText(null); + } + } + + if (error != null) + { + // arranges this to build up a status message with real action + exploratory status + // TODO: (REG-2213) Update all this to use a status manager to manage reporting + + string loggedMessage; + + if (firstActionSegment.botAction?.data is IKeyMomentExploration) + { + loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\n" + error + "\nRunning exploratory actions..."; + } + else + { + loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\n" + error; + } + + // wait the action warning interval before starting to explore actions + if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) + { + if (firstActionSegment.botAction?.data is IKeyMomentExploration) + { + _explorationDriver.StartExploring(); + } + } + + _explorationDriver.PerformExploratoryAction(firstActionSegment.Replay_SegmentNumber, transformStatuses, entityStatuses, out var explorationError); + + if (explorationError != null) + { + loggedMessage += $"\n\nError processing exploratory BotAction\n" + explorationError; + } + + if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) + { + LogPlaybackWarning(loggedMessage); + } + } + } + } + catch (Exception ex) + { + var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Exception processing BotAction\n" + ex.Message; + LogPlaybackWarning(loggedMessage, ex); + // uncaught exception... stop and unload the segment + UnloadSegmentsAndReset(); + throw; + } + } + + var nextBotSegment = _nextBotSegments[nextBotSegmentIndex]; + + var matched = nextBotSegment.Replay_Matched || nextBotSegment.endCriteria == null || nextBotSegment.endCriteria.Count == 0 || KeyFrameEvaluator.Evaluator.Matched( + nextBotSegmentIndex == 0, + nextBotSegment.Replay_SegmentNumber, + nextBotSegment.Replay_ActionCompleted, + nextBotSegment.endCriteria + ); + + if (matched) + { + // only update the time when the first index matches, but keeps us from logging this while waiting for actions to complete + if (nextBotSegmentIndex == 0) + { + if (!nextBotSegment.Replay_Matched) + { + // only mark this fully matched (as opposed to transient if it is the current segment) + nextBotSegment.Replay_Matched = true; + RGDebug.LogInfo($"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - Criteria Matched - {nextBotSegment.name ?? nextBotSegment.resourcePath} - {nextBotSegment.description}"); + + if (nextBotSegment.Replay_ActionStarted && !nextBotSegment.Replay_ActionCompleted) + { + // tell the action that our segment completed and it should stop when it finishes its current actions.. only do this the first time we pass through as matched + nextBotSegment.StopAction(transformStatuses, entityStatuses); + } + + _lastTimeLoggedKeyFrameConditions = now; + FindObjectOfType()?.SetKeyFrameWarningText(null); + matchedThisUpdate = true; + } + + // wait ACTION_WARNING_INTERVAL seconds between logging this as some actions take quite a while + if (nextBotSegment.Replay_ActionStarted && !nextBotSegment.Replay_ActionCompleted && _lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) + { + _lastTimeLoggedKeyFrameConditions = now; + var loggedMessage = $"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - Waiting for actions to complete"; + FindObjectOfType()?.SetKeyFrameWarningText(loggedMessage); + RGDebug.LogInfo(loggedMessage); + } + } + + if (nextBotSegment.Replay_Matched && nextBotSegment.Replay_ActionStarted && nextBotSegment.Replay_ActionCompleted) + { + _lastTimeLoggedKeyFrameConditions = now; + RGDebug.LogInfo($"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - DONE - Criteria Matched && Action Completed - {nextBotSegment.name ?? nextBotSegment.resourcePath} - {nextBotSegment.description}"); + //Process the inputs from that bot segment if necessary + _nextBotSegments.RemoveAt(nextBotSegmentIndex); + // don't update the index since we shortened the list + } + else + { + ++nextBotSegmentIndex; + } + } + else + { + // only log this every ACTION_WARNING_INTERVAL seconds for the first key frame being evaluated after its actions complete + if (nextBotSegmentIndex == 0 && nextBotSegment.Replay_ActionCompleted && _lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) + { + var warningText = KeyFrameEvaluator.Evaluator.GetUnmatchedCriteria(); + if (warningText != null) + { + var loggedMessage = $"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - Unmatched Criteria for \r\n" + warningText; + LogPlaybackWarning(loggedMessage); + } + } + ++nextBotSegmentIndex; + } + + // we possibly removed from the list above.. need this check + if (_nextBotSegments.Count > 0) + { + // see if the last entry has transient matches.. if so.. dequeue another up to a limit of 2 total segments being evaluated... we may need to come back to this.. but without this look ahead, loading screens like bossroom fail due to background loading + // but if you go too far.. you can match segments in the replay that you won't see for another 50 segments when you go back to the menu again.. which is obviously wrong + var lastSegment = _nextBotSegments[^1]; + if (lastSegment.Replay_TransientMatched) + { + if (_nextBotSegments.Count < 2) + { + var next = _dataPlaybackContainer.DequeueBotSegment(); + if (next != null) + { + _lastTimeLoggedKeyFrameConditions = now; + FindObjectOfType()?.SetKeyFrameWarningText(null); + RGDebug.LogInfo($"({next.Replay_SegmentNumber}) - Bot Segment - Added {(next.HasTransientCriteria ? "" : "Non-")}Transient BotSegment for Evaluation after Transient BotSegment - {next.name ?? next.resourcePath} - {next.description}"); + _nextBotSegments.Add(next); + //next while loop iteration will get this guy + } + } + } + } + else + { + // segment list empty.. dequeue another + var next = _dataPlaybackContainer.DequeueBotSegment(); + if (next != null) + { + _lastTimeLoggedKeyFrameConditions = now; + FindObjectOfType()?.SetKeyFrameWarningText(null); + RGDebug.LogInfo($"({next.Replay_SegmentNumber}) - Bot Segment - Added {(next.HasTransientCriteria ? "" : "Non-")}Transient BotSegment for Evaluation - {next.name ?? next.resourcePath} - {next.description}"); + _nextBotSegments.Add(next); + //next while loop iteration will get this guy + } + } + } + + if (matchedThisUpdate) + { + // only do this when a segment passed this update after all segments have been considered for this update + KeyFrameEvaluator.Evaluator.PersistPriorFrameStatus(); + } + } + catch (Exception ex) + { + var loggedMessage = $"(?) - Bot Segment - Exception processing BotSegments\r\n" + ex.Message; + LogPlaybackWarning(loggedMessage, ex); + // uncaught exception... stop the segment + UnloadSegmentsAndReset(); + throw; + } + } + + public void LateUpdate() + { + if (_playState == PlayState.Playing) + { + if (_dataPlaybackContainer != null) + { + EvaluateBotSegments(); + } + + if (_nextBotSegments.Count == 0) + { + MouseEventSender.MoveMouseOffScreen(); + + // we hit the end of the replay + if (_loopCount > -1) + { + PrepareForNextLoop(); + _loopCountCallback.Invoke(++_loopCount); + } + else + { + // stop ready to play again + Stop(); + _replaySuccessful = true; + } + } + } + } + private void Start() { _explorationDriver = GetComponent(); @@ -133,18 +403,18 @@ void OnEnable() _screenRecorder = GetComponentInParent(); } - private bool unpaused; + private bool _unpaused; #if UNITY_EDITOR private void ResetErrorTimer(PauseState pauseState) { if (pauseState == PauseState.Unpaused) { - unpaused = true; + _unpaused = true; } else { - unpaused = false; + _unpaused = false; } } #endif @@ -399,7 +669,8 @@ public void Update() private float _lastTimeLoggedKeyFrameConditions = 0; - private const int ACTION_WARNING_INTERVAL = 3; // seconds + // ReSharper disable once InconsistentNaming + private const int ACTION_WARNING_INTERVAL = 3; // seconds before we log or start exploring other bot actions private void LogPlaybackWarning(string loggedMessage, Exception ex = null) { @@ -421,266 +692,6 @@ private void LogPlaybackWarning(string loggedMessage, Exception ex = null) } } - private IBotActionData _lastSuccessfulAction = null; - - private void EvaluateBotSegments() - { - var now = Time.unscaledTime; - - try - { - if (unpaused) - { - _lastTimeLoggedKeyFrameConditions = now; - unpaused = false; - } - - var objectFinders = FindObjectsByType(FindObjectsSortMode.None); - - Dictionary transformStatuses = null; - Dictionary entityStatuses = null; - - foreach (var objectFinder in objectFinders) - { - if (objectFinder is TransformObjectFinder) - { - transformStatuses = objectFinder.GetObjectStatusForCurrentFrame().Item2; - } - else - { - entityStatuses = objectFinder.GetObjectStatusForCurrentFrame().Item2; - } - } - - transformStatuses ??= new Dictionary(); - entityStatuses ??= new Dictionary(); - - // track if any segment matched this update - var matchedThisUpdate = false; - - // track if we have a new segment to evaluate... so long as we do, keep looping here before releasing from this Update call - // thus we process each new segment as soon as possible and don't have any artificial one frame delays before processing - var nextBotSegmentIndex = 0; - while (nextBotSegmentIndex < _nextBotSegments.Count) - { - // if we're working on the first entry in the list is the only time we do actions - if (nextBotSegmentIndex == 0) - { - BotSegment firstActionSegment = _nextBotSegments[0]; - try - { - // allow the main action to retry between every exploratory action - var didAction = firstActionSegment.ProcessAction(transformStatuses, entityStatuses, out var error); - if (error == null) - { - if (didAction) - { - _explorationDriver.StopExploring(firstActionSegment.Replay_SegmentNumber); - // for every non error action, reset the timer - _lastTimeLoggedKeyFrameConditions = now; - FindObjectOfType()?.SetKeyFrameWarningText(null); - } - if (firstActionSegment.botAction?.data is IKeyMomentExploration) - { - if (firstActionSegment.botAction.data.IsCompleted()) - { - _explorationDriver.ReportPreviouslyCompletedAction(firstActionSegment.botAction.data); - } - } - } - - if (error != null) - { - if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) - { - if (firstActionSegment.botAction?.data is IKeyMomentExploration) - { - var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\r\n" + error + "\r\nStarting exploratory actions..."; - LogPlaybackWarning(loggedMessage); - _explorationDriver.StartExploring(); - } - else - { - var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\r\n" + error; - LogPlaybackWarning(loggedMessage); - } - } - - _explorationDriver.PerformExploratoryAction(firstActionSegment.Replay_SegmentNumber, transformStatuses, entityStatuses, out var explorationError); - if (explorationError != null) - { - // only log this if we've gone 2x the interval without a success - if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL*2) - { - var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing exploratory BotAction\r\n" + explorationError; - LogPlaybackWarning(loggedMessage); - } - } - } - } - catch (Exception ex) - { - var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Exception processing BotAction\r\n" + ex.Message; - LogPlaybackWarning(loggedMessage, ex); - // uncaught exception... stop and unload the segment - UnloadSegmentsAndReset(); - throw; - } - } - - var nextBotSegment = _nextBotSegments[nextBotSegmentIndex]; - - var matched = nextBotSegment.Replay_Matched || nextBotSegment.endCriteria == null || nextBotSegment.endCriteria.Count == 0 || KeyFrameEvaluator.Evaluator.Matched( - nextBotSegmentIndex == 0, - nextBotSegment.Replay_SegmentNumber, - nextBotSegment.Replay_ActionCompleted, - nextBotSegment.endCriteria - ); - - if (matched) - { - // only update the time when the first index matches, but keeps us from logging this while waiting for actions to complete - if (nextBotSegmentIndex == 0) - { - if (!nextBotSegment.Replay_Matched) - { - // only mark this fully matched (as opposed to transient if it is the current segment) - nextBotSegment.Replay_Matched = true; - RGDebug.LogInfo($"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - Criteria Matched - {nextBotSegment.name ?? nextBotSegment.resourcePath} - {nextBotSegment.description}"); - - if (nextBotSegment.Replay_ActionStarted && !nextBotSegment.Replay_ActionCompleted) - { - // tell the action that our segment completed and it should stop when it finishes its current actions.. only do this the first time we pass through as matched - nextBotSegment.StopAction(transformStatuses, entityStatuses); - } - - _lastTimeLoggedKeyFrameConditions = now; - FindObjectOfType()?.SetKeyFrameWarningText(null); - matchedThisUpdate = true; - } - - // wait ACTION_WARNING_INTERVAL seconds between logging this as some actions take quite a while - if (nextBotSegment.Replay_ActionStarted && !nextBotSegment.Replay_ActionCompleted && _lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) - { - _lastTimeLoggedKeyFrameConditions = now; - var loggedMessage = $"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - Waiting for actions to complete"; - FindObjectOfType()?.SetKeyFrameWarningText(loggedMessage); - RGDebug.LogInfo(loggedMessage); - } - } - - if (nextBotSegment.Replay_Matched && nextBotSegment.Replay_ActionStarted && nextBotSegment.Replay_ActionCompleted) - { - _lastSuccessfulAction = nextBotSegment.botAction?.data; - - _lastTimeLoggedKeyFrameConditions = now; - RGDebug.LogInfo($"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - DONE - Criteria Matched && Action Completed - {nextBotSegment.name ?? nextBotSegment.resourcePath} - {nextBotSegment.description}"); - //Process the inputs from that bot segment if necessary - _nextBotSegments.RemoveAt(nextBotSegmentIndex); - // don't update the index since we shortened the list - } - else - { - ++nextBotSegmentIndex; - } - } - else - { - // only log this every ACTION_WARNING_INTERVAL seconds for the first key frame being evaluated after its actions complete - if (nextBotSegmentIndex == 0 && nextBotSegment.Replay_ActionCompleted && _lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) - { - var warningText = KeyFrameEvaluator.Evaluator.GetUnmatchedCriteria(); - if (warningText != null) - { - var loggedMessage = $"({nextBotSegment.Replay_SegmentNumber}) - Bot Segment - Unmatched Criteria for \r\n" + warningText; - LogPlaybackWarning(loggedMessage); - } - } - ++nextBotSegmentIndex; - } - - // we possibly removed from the list above.. need this check - if (_nextBotSegments.Count > 0) - { - // see if the last entry has transient matches.. if so.. dequeue another up to a limit of 2 total segments being evaluated... we may need to come back to this.. but without this look ahead, loading screens like bossroom fail due to background loading - // but if you go too far.. you can match segments in the replay that you won't see for another 50 segments when you go back to the menu again.. which is obviously wrong - var lastSegment = _nextBotSegments[^1]; - if (lastSegment.Replay_TransientMatched) - { - if (_nextBotSegments.Count < 2) - { - var next = _dataPlaybackContainer.DequeueBotSegment(); - if (next != null) - { - _lastTimeLoggedKeyFrameConditions = now; - FindObjectOfType()?.SetKeyFrameWarningText(null); - RGDebug.LogInfo($"({next.Replay_SegmentNumber}) - Bot Segment - Added {(next.HasTransientCriteria ? "" : "Non-")}Transient BotSegment for Evaluation after Transient BotSegment - {next.name ?? next.resourcePath} - {next.description}"); - _nextBotSegments.Add(next); - //next while loop iteration will get this guy - } - } - } - } - else - { - // segment list empty.. dequeue another - var next = _dataPlaybackContainer.DequeueBotSegment(); - if (next != null) - { - _lastTimeLoggedKeyFrameConditions = now; - FindObjectOfType()?.SetKeyFrameWarningText(null); - RGDebug.LogInfo($"({next.Replay_SegmentNumber}) - Bot Segment - Added {(next.HasTransientCriteria ? "" : "Non-")}Transient BotSegment for Evaluation - {next.name ?? next.resourcePath} - {next.description}"); - _nextBotSegments.Add(next); - //next while loop iteration will get this guy - } - } - } - - if (matchedThisUpdate) - { - // only do this when a segment passed this update after all segments have been considered for this update - KeyFrameEvaluator.Evaluator.PersistPriorFrameStatus(); - } - } - catch (Exception ex) - { - var loggedMessage = $"(?) - Bot Segment - Exception processing BotSegments\r\n" + ex.Message; - LogPlaybackWarning(loggedMessage, ex); - // uncaught exception... stop the segment - UnloadSegmentsAndReset(); - throw; - } - } - - public void LateUpdate() - { - if (_playState == PlayState.Playing) - { - if (_dataPlaybackContainer != null) - { - EvaluateBotSegments(); - } - - if (_nextBotSegments.Count == 0) - { - MouseEventSender.MoveMouseOffScreen(); - - // we hit the end of the replay - if (_loopCount > -1) - { - PrepareForNextLoop(); - _loopCountCallback.Invoke(++_loopCount); - } - else - { - // stop ready to play again - Stop(); - _replaySuccessful = true; - } - } - } - } - public void OnGUI() { if (_playState == PlayState.Playing || _playState == PlayState.Paused) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotAction.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotAction.cs index 1587dc8d..a51a7c7a 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotAction.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotAction.cs @@ -3,6 +3,7 @@ using System.Text; using RegressionGames.StateRecorder.JsonConverters; using RegressionGames.StateRecorder.Models; +// ReSharper disable InconsistentNaming namespace RegressionGames.StateRecorder.BotSegments.Models { @@ -14,13 +15,17 @@ public class BotAction : IStringBuilderWriteable, IKeyMomentStringBuilderWriteab public BotActionType type; public IBotActionData data; - public bool? IsCompleted => data.IsCompleted(); // returns null if this action runs until the keyframecriteria are met + + public bool IsStarted { get; private set; } + + public bool? IsCompleted => IsStarted && data.IsCompleted(); public int EffectiveApiVersion => Math.Max(apiVersion, data?.EffectiveApiVersion() ?? SdkApiVersion.CURRENT_VERSION); // Called before the first call to ProcessAction to allow data setup by the action code public void StartAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities) { + IsStarted = true; data.StartAction(segmentNumber, currentTransforms, currentEntities); } @@ -64,6 +69,7 @@ public void PauseAction(int segmentNumber) public void ReplayReset() { + IsStarted = false; data.ReplayReset(); } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegment.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegment.cs index 19cfef5f..1a3949fc 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegment.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotSegment.cs @@ -62,8 +62,7 @@ public class BotSegment : IStringBuilderWriteable, IKeyMomentStringBuilderWritea public int Replay_SegmentNumber; // Replay only - tracks if we have started the action for this bot segment - [NonSerialized] - public bool Replay_ActionStarted; + public bool Replay_ActionStarted => botAction == null || botAction.IsStarted; // Replay only - tracks if we have completed the action for this bot segment // returns true if botAction.IsCompleted || botAction.IsCompleted==null && Replay_Matched @@ -80,16 +79,11 @@ public void OnGUI(Dictionary currentTransforms, Dictionary currentTransforms, Dictionary currentEntities, out string error) { - if (botAction == null) - { - Replay_ActionStarted = true; - } - else + if (botAction != null) { if (!Replay_ActionStarted) { botAction.StartAction(Replay_SegmentNumber, currentTransforms, currentEntities); - Replay_ActionStarted = true; } return botAction.ProcessAction(Replay_SegmentNumber, currentTransforms, currentEntities, out error); } @@ -157,7 +151,6 @@ public void ReplayReset() botAction.ReplayReset(); } - Replay_ActionStarted = false; Replay_Matched = false; } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/IBotActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/IBotActionData.cs index 83fb706d..87d224b6 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/IBotActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/IBotActionData.cs @@ -31,7 +31,7 @@ public void PauseAction(int segmentNumber) /** * Called at least once per frame * returns true if an action was performed - * Returns null or an error message string + * Returns null or a error string */ public bool ProcessAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error); diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 16082850..dfa2efe9 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -5,7 +5,6 @@ using RegressionGames.StateRecorder.JsonConverters; using RegressionGames.StateRecorder.Models; using UnityEngine; -using UnityEngine.EventSystems; using UnityEngine.UI; using Random = UnityEngine.Random; @@ -473,7 +472,7 @@ public bool ProcessAction(int segmentNumber, Dictionary curr } } - if (!QueueClickForObjectAtPosition(segmentNumber, pendingAction, mouseAction, bestObjectStatus, clickPosition, out error)) + if (!QueueClickForObjectAtPosition(segmentNumber, pendingAction, mouseAction, bestObjectStatus, clickPosition, currentTransforms, out error)) { _mouseActionsToDo.Clear(); return false; @@ -607,100 +606,34 @@ public bool ProcessAction(int segmentNumber, Dictionary curr return preconditionMatches; } - private bool QueueClickForObjectAtPosition(int segmentNumber, MouseInputActionData pendingAction, MouseInputActionData mouseAction, ObjectStatus bestObjectStatus, Vector2 myClickPosition, out string error) + private bool QueueClickForObjectAtPosition(int segmentNumber, MouseInputActionData pendingAction, MouseInputActionData mouseAction, ObjectStatus bestObjectStatus, Vector2 myClickPosition, Dictionary currentTransforms, out string error) { // we fill 'myClickPosition' in for all code paths.. if you get a null here, something above has bad logic as this should be filled in always here if (pendingAction != null) { // we are on the 2nd action, which is the 'click' - - // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over - // our intended click item (iow.. it's there/ready, but obstructed) ... we only sort by zDepth(not UI bounds) here so we can find proper obstructions - - if (bestObjectStatus is TransformStatus transformStatus) + if (bestObjectStatus is TransformStatus) { - - if (transformStatus.worldSpaceBounds == null) + // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over + // our intended click item (iow.. it's there/ready, but obstructed) ... + // make sure our object is at the front of the list and thus at the closest Z depth at that point.. findobjectsatposition handles z depth sorting for us + // or that the item at the front of the list is on the same path tree as us (we might have clicked on the text part of the button instead of the button, but both still click the button) + + var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); + // see if our object is 'first' or obstructed + if (objectsAtClickPosition.Count > 0) { - // UI element.. make sure it is the highest UI thing hit - - var eventSystemHits = new List(); - // ray-cast into the scene and see if this object is the first thing hit - var pointerEventData = new PointerEventData(EventSystem.current) + if (!mouseAction.clickedObjectNormalizedPaths[0].StartsWith(objectsAtClickPosition[0].NormalizedPath)) { - button = PointerEventData.InputButton.Left, - position = myClickPosition - }; - EventSystem.current.RaycastAll(pointerEventData, eventSystemHits); - - if (eventSystemHits.Count > 0) - { - eventSystemHits.Sort((a,b) => - { - if (a.distance < b.distance) - { - return -1; - } - - if (a.distance > b.distance) - { - return 1; - } - - // higher depth == closer to camera.. don't even get me started - return b.depth - a.depth; - }); - } - if (eventSystemHits.Count <= 0) - { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\nno selectable UI objects detected at point"; + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\na UI object is obstructing with path:\n{objectsAtClickPosition[0].NormalizedPath}"; return false; } - else - { - var hitNormalizedPath = TransformStatus.GetOrCreateTransformStatus(eventSystemHits[0].gameObject.transform).NormalizedPath; - if (!hitNormalizedPath.StartsWith(mouseAction.clickedObjectNormalizedPaths[0])) - { - // if this isn't the exact object or a child object (like a button text of the button we're clicking).. then error out - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{hitNormalizedPath}"; - return false; - } - } } else { - // Handle world space obstructions... this is easier, since any overlay UI element will be in the path list over world objects, we just need to make sure our - // object is at the front of the list and thus at the closest Z depth at that point - - // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over - // our intended click item (iow.. it's there/ready, but obstructed) ... - // we have to be careful though, because the 'zDepth' computed for the object may NOT be the zDepth at this exact click point on said object... - //var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); - - var mainCamera = Camera.main; - if (mainCamera != null) - { - var ray = mainCamera.ScreenPointToRay(myClickPosition); - - var didHit = Physics.Raycast(ray, out var raycastHit); - - if (!didHit) - { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\nno objects at position"; - return false; - } - - var normalizedHitPath = TransformStatus.GetOrCreateTransformStatus(raycastHit.transform).NormalizedPath; - - // this handles cases like in bossroom where the collider is on EntranceStaticNetworkObjects/BreakablePot , but the renderer is on EntranceStaticNetworkObjects/BreakablePot/pot - if (!mouseAction.clickedObjectNormalizedPaths[0].StartsWith(normalizedHitPath)) - { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\ntarget object is obstructed by path:\n{normalizedHitPath}"; - return false; - } - } + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\nno objects at that position"; + return false; } - } var myPendingAction = pendingAction; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/ObjectStatus.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/ObjectStatus.cs index a9ff9bb5..9527b2cf 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/ObjectStatus.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/ObjectStatus.cs @@ -39,6 +39,19 @@ public override string ToString() public Bounds? worldSpaceBounds; + /** + * Used only as a temporary tracking when finding object at point during playback evaluation.. used for sorting object depths correctly in that point evaluation pass + * default : -1 so it would be behind the camera and ignored if not found + */ + [NonSerialized] + public float zOffsetForMousePoint = -1f; + + /** + * Used only as a temporary tracking when finding object at point during playback evaluation.. used for computing world space click location accurately + * default : null + */ + public Vector3? worldSpaceCoordinatesForMousePoint = null; + public abstract bool PositionHitsCollider(Vector3 position); } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/TransformStatus.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/TransformStatus.cs index 96d3479b..0070eab8 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/TransformStatus.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/TransformStatus.cs @@ -10,7 +10,7 @@ public class TransformStatus : ObjectStatus private TransformStatus() {} - public Transform Transform; + public readonly Transform Transform; // re-use these objects private static readonly StringBuilder PathBuilder = new (500); @@ -20,6 +20,18 @@ private TransformStatus() // right now this resets on awake from InGameObjectFinder, but we may have to deal with dynamically re-parented transforms better at some point... private static readonly Dictionary TransformsIveSeen = new(1000); + private TransformStatus(Transform transform) + { + this.Transform = transform; + this.Id = transform.GetInstanceID(); + } + + private TransformStatus(Transform transform, long id) + { + this.Transform = transform; + this.Id = id; + } + public static void Reset() { TransformsIveSeen.Clear(); @@ -81,11 +93,9 @@ public static TransformStatus GetOrCreateTransformStatus(Transform theTransform) if (status == null) { - status = new TransformStatus + status = new TransformStatus(theTransform, id) { - Id = id, ParentId = theTransform.parent != null ? theTransform.parent.GetInstanceID() : null, - Transform = theTransform, LayerName = LayerMask.LayerToName(theGameObject.layer), Scene = theGameObject.scene.name, Tag = theTransform.tag diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index e037a2f0..6e1d31ef 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -4,6 +4,7 @@ using System.Linq; using RegressionGames.StateRecorder.Models; using UnityEngine; +using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.UI; @@ -13,14 +14,7 @@ public class MouseInputActionObserver : MonoBehaviour { private MouseInputActionData _priorMouseState; - private readonly Dictionary _clickedObjectNormalizedPaths = new(100); - - private readonly Comparer _mouseHitComparer = Comparer.Create( - (x1, x2) => - { - var d = x1.distance - x2.distance; - return (d > 0f) ? 1 : (d < 0f) ? -1 : 0; - } ); + private readonly List _clickedObjectNormalizedPaths = new(100); public void ObserveMouse(IEnumerable statefulObjects) { @@ -33,89 +27,23 @@ public void ObserveMouse(IEnumerable statefulObjects) { if (_priorMouseState == null && newMouseState.IsButtonClicked || _priorMouseState != null && !_priorMouseState.ButtonStatesEqual(newMouseState)) { - Vector3? worldPosition = null; - var clickedOnObjects = FindObjectsAtPosition(newMouseState.position, statefulObjects, out var maxZDepth); - - var didMouseRayHit = false; - - RaycastHit hitInfo = default; - - var mainCamera = Camera.main; - if (mainCamera != null) - { - var ray = mainCamera.ScreenPointToRay(mousePosition); - didMouseRayHit = Physics.Raycast(ray, out hitInfo); // make sure we go deep enough to hit the collider on that object.. we hope - } + // get the z depth sorted clicked on objects + var clickedOnObjects = FindObjectsAtPosition(newMouseState.position, statefulObjects, out _); _clickedObjectNormalizedPaths.Clear(); - if (didMouseRayHit) - { - // we hit a collider and can compute the world position clicked - // we can also then sort the results based on said collision hit so that the best objects are in the front - foreach (var clickedTransformStatus in clickedOnObjects) - { - var zDepth = clickedTransformStatus.screenSpaceZOffset; - if (clickedTransformStatus.worldSpaceBounds != null) - { - var rayHit = hitInfo; - try - { - var rayHitObjectStatus = TransformStatus.GetOrCreateTransformStatus(rayHit.transform); - // this handles cases like in bossroom where the collider is on EntranceStaticNetworkObjects/BreakablePot , but the renderer is on EntranceStaticNetworkObjects/BreakablePot/pot - if (clickedTransformStatus.NormalizedPath.StartsWith(rayHitObjectStatus.NormalizedPath)) - { - worldPosition = rayHit.point; - // we do some fuzzy math here... we know for sure we hit this object first, but ... if we just use its hit distance that may not sort - // correctly with the zDepth of other 3d objects.. there could be a giant box and we're clicking on something just on the back top edge of it - // but the front of the box is wayy closer to us... but.. isn't obstructing that click... so for this optimization - // we know ui overlay things are at depth 0f... so we put this just behind that, but should be in front of anything else - zDepth = 0.00001f; // Logic with these numbers is also in TransformObjectFinder.. this value needs to be CLOSER from the camera than the one in TransformObjectFinder - } - } - catch (Exception e) - { - RGDebug.LogException(e, "Exception handling raycast hits for in game object click"); - } - } - - // track the shortest z depth - if (!_clickedObjectNormalizedPaths.TryGetValue(clickedTransformStatus.NormalizedPath, out var depth)) - { - _clickedObjectNormalizedPaths[clickedTransformStatus.NormalizedPath] = zDepth; - } - else - { - if (zDepth < depth) - { - _clickedObjectNormalizedPaths[clickedTransformStatus.NormalizedPath] = zDepth; - } - } - } - } - else + // the objects found are already in order, but we do need to see if there was a worldPosition clicked + foreach (var clickedTransformStatus in clickedOnObjects) { - // without a collider hit, we can't set a worldPosition - foreach (var recordedGameObjectState in clickedOnObjects) + if (clickedTransformStatus.worldSpaceBounds != null && !newMouseState.worldPosition.HasValue) { - // track the shortest z depth - if (!_clickedObjectNormalizedPaths.TryGetValue(recordedGameObjectState.NormalizedPath, out var depth)) - { - _clickedObjectNormalizedPaths[recordedGameObjectState.NormalizedPath] = recordedGameObjectState.screenSpaceZOffset; - } - else - { - if (recordedGameObjectState.screenSpaceZOffset < depth) - { - _clickedObjectNormalizedPaths[recordedGameObjectState.NormalizedPath] = recordedGameObjectState.screenSpaceZOffset; - } - } + newMouseState.worldPosition = clickedTransformStatus.worldSpaceCoordinatesForMousePoint; } + _clickedObjectNormalizedPaths.Add(clickedTransformStatus.NormalizedPath); } - newMouseState.worldPosition = worldPosition; // order by zDepth - this should keep UI elements that were already sorted in the same relative positions as they have exactly 0f zDepths - newMouseState.clickedObjectNormalizedPaths = _clickedObjectNormalizedPaths.OrderBy(kvp => kvp.Value).Select(a => a.Key).ToArray(); + newMouseState.clickedObjectNormalizedPaths = _clickedObjectNormalizedPaths.Distinct().ToArray(); _mouseInputActions.Enqueue(newMouseState); } else if (_priorMouseState?.PositionsEqual(newMouseState) != true) @@ -128,7 +56,7 @@ public void ObserveMouse(IEnumerable statefulObjects) } } - public MouseInputActionData GetCurrentMouseState(Vector2? position = null) + private MouseInputActionData GetCurrentMouseState(Vector2? position = null) { var pointer = Pointer.current; if (pointer != null) @@ -195,9 +123,9 @@ public List FlushInputDataBuffer(bool ignoreLastClick, boo { _mouseInputActions.TryDequeue(out _); - // when minimizing output, only capture the inputs 1 before 1 after and during mouse clicks or holds... the normalized paths is populated for both clicks and unclicks so is useful for determining the unclick data + // when minimizing output, only capture the inputs 1 before 1 after and during mouse clicks or holds... the normalized paths is populated for both clicks and un-clicks so is useful for determining the un-click data - if (lastAction != null && (!minimizeOutput || action.IsButtonClicked || action.clickedObjectNormalizedPaths.Length > 0 || lastAction?.IsButtonClicked == true|| lastAction?.clickedObjectNormalizedPaths.Length > 0)) + if (lastAction != null && (!minimizeOutput || action.IsButtonClicked || action.clickedObjectNormalizedPaths.Length > 0 || lastAction.IsButtonClicked || lastAction.clickedObjectNormalizedPaths.Length > 0)) { result.Add(lastAction); } @@ -235,6 +163,13 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer var vec3Position = new Vector3(position.x, position.y, 0); List result = new(); maxZDepth = 0f; + var mainCamera = Camera.main; + Ray? ray = null; + if (mainCamera != null) + { + ray = mainCamera.ScreenPointToRay(position); + } + foreach (var recordedGameObjectState in statefulObjects) { if (recordedGameObjectState.screenSpaceBounds.HasValue) @@ -260,13 +195,46 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer if (isInteractable) { - if (recordedGameObjectState.worldSpaceBounds != null) + if (recordedGameObjectState.worldSpaceBounds.HasValue) { - if (recordedGameObjectState.screenSpaceZOffset > maxZDepth) + if (ray.HasValue) { - maxZDepth = recordedGameObjectState.screenSpaceZOffset; + // compute the zOffset at this point based on the ray + if (recordedGameObjectState.worldSpaceBounds.Value.IntersectRay(ray.Value, out var zDepthHit)) + { + if (zDepthHit >= 0f) + { + recordedGameObjectState.zOffsetForMousePoint = zDepthHit; + recordedGameObjectState.worldSpaceCoordinatesForMousePoint = ray.Value.GetPoint(zDepthHit); + if (zDepthHit > maxZDepth) + { + maxZDepth = zDepthHit; + } + } + } + else + { + // wasn't in front of the camera + recordedGameObjectState.zOffsetForMousePoint = -1f; + recordedGameObjectState.worldSpaceCoordinatesForMousePoint = null; + } + } + else + { + // go with the zOffset for the bounding box itself instead of at this exact point... this leads to really weird cases in 3d spaces with close objects and should be avoided + // example.. a stone you click sitting on the top back corner of a cube will be 'behind' the cube because the front plane of the cube is closer, just not at that screen point + // leaving this in here for now though as some sorting is still better than no sorting if we get here (have no camera) + if (recordedGameObjectState.screenSpaceZOffset > maxZDepth) + { + maxZDepth = recordedGameObjectState.screenSpaceZOffset; + } } } + else if (recordedGameObjectState.screenSpaceZOffset >= 0f) + { + recordedGameObjectState.zOffsetForMousePoint = recordedGameObjectState.screenSpaceZOffset; + recordedGameObjectState.worldSpaceCoordinatesForMousePoint = null; + } result.Add(recordedGameObjectState); } @@ -274,34 +242,78 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer } } + // compute the UI event system hits at this point for sorting + var eventSystemHits = new List(); + // ray-cast into the scene with the UI event system and see if this object is the first thing hit + var pointerEventData = new PointerEventData(EventSystem.current) + { + button = PointerEventData.InputButton.Left, + position = position + }; + EventSystem.current.RaycastAll(pointerEventData, eventSystemHits); + + var uiHitMapping = eventSystemHits.ToDictionary(a => (long)a.gameObject.transform.GetInstanceID(), a => a); + // sort with lower z-offsets first so we have UI things on top of game object things - // if both elements are from the UI.. then sort by smallest bounding area + // if both elements are from the UI.. then sort by the event system ray hits result.Sort((a, b) => { if (a.worldSpaceBounds == null && b.worldSpaceBounds == null) { - // 2 UI objects, sort by smallest render bounds - var aExtents = a.screenSpaceBounds.Value.extents; - var bExtents = b.screenSpaceBounds.Value.extents; - - var aAreaComparison = aExtents.x * aExtents.y; - var bAreaComparison = bExtents.x * bExtents.y; + RaycastResult? aHitData = null; + if (a is TransformStatus at) + { + if (uiHitMapping.TryGetValue(at.Id, out var value)) + { + aHitData = value; + } + } + RaycastResult? bHitData = null; + if (b is TransformStatus bt) + { + if (uiHitMapping.TryGetValue(bt.Id, out var value)) + { + bHitData = value; + } + } - if (aAreaComparison < bAreaComparison) + if (aHitData.HasValue) { + if (bHitData.HasValue) + { + if (aHitData.Value.distance < bHitData.Value.distance) + { + return -1; + } + + if (aHitData.Value.distance > bHitData.Value.distance) + { + return 1; + } + + // higher depth for ui hits == closer to camera.. don't even get me started + return bHitData.Value.depth - aHitData.Value.depth; + } + + // aDidHit.. but not b, put a in front return -1; } - // if a isn't smaller then we don't much care between == vs > as in floating point land.. == is so unlikely as for us to not worry about 'stable' sort for this case - return 1; + if (bHitData.HasValue) + { + //bDidHit.. but not a, put b in front + return 1; + } + + return 0; } - if (a.screenSpaceZOffset < b.screenSpaceZOffset) + if (a.zOffsetForMousePoint < b.zOffsetForMousePoint) { return -1; } - if (a.screenSpaceZOffset > b.screenSpaceZOffset) + if (a.zOffsetForMousePoint > b.zOffsetForMousePoint) { return 1; } From 6bb21c3ea1da4af6a4e8b2314800ff1abd3baca6 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Tue, 10 Dec 2024 14:12:04 -0500 Subject: [PATCH 15/31] Fix depth sorting of the clicks once and for all --- .../BotActions/KeyMomentMouseActionData.cs | 2 +- .../StateRecorder/MouseInputActionObserver.cs | 86 ++++++++++++++++++- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index dfa2efe9..0b1ca5ea 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -416,7 +416,7 @@ public bool ProcessAction(int segmentNumber, Dictionary curr // 1. for more variability in testing // 2. to get around cases where we were say clicking on a floor tile, but there is something on that floor tile and we wanted to click on the open space of the floor tile // on the next attempt, it picks a new position to try thus giving us a better chance of passing - // TODO: Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? + // TODO (REG-2223): Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? // This would allow us to know that we clicked about X far into this floor tile and replicate that positioning regardless of the actual worldspace positioning in the replay... // +1 because int range is max exclusive.. if these were floats.. remove the +1 Vector2 clickPosition = new Vector2(Random.Range(matchResults[0].Item3.Item1, matchResults[0].Item3.Item3+1), Random.Range(matchResults[0].Item3.Item2, matchResults[0].Item3.Item4+1)); diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index 6e1d31ef..66d65bc9 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -254,8 +254,18 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer var uiHitMapping = eventSystemHits.ToDictionary(a => (long)a.gameObject.transform.GetInstanceID(), a => a); + var worldSpaceHits = new Dictionary(); + + if (ray.HasValue) + { + // do a raycast all into the world and see what hits... we want to be able to sort things with colliders above those that don't have one + var raycastHits = Physics.RaycastAll(ray.Value); + worldSpaceHits = raycastHits.ToDictionary(a => a.transform.GetInstanceID(), a => a); + } + // sort with lower z-offsets first so we have UI things on top of game object things // if both elements are from the UI.. then sort by the event system ray hits + // for world space objects.. sort those with ray cast hits before those without result.Sort((a, b) => { if (a.worldSpaceBounds == null && b.worldSpaceBounds == null) @@ -308,12 +318,63 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer return 0; } - if (a.zOffsetForMousePoint < b.zOffsetForMousePoint) + RaycastHit? aWorldHitData = null; + if (a is TransformStatus awt) + { + aWorldHitData = IsHitOnParent(awt.Transform, worldSpaceHits); + } + RaycastHit? bWorldHitData = null; + if (b is TransformStatus bwt) + { + bWorldHitData = IsHitOnParent(bwt.Transform, worldSpaceHits); + } + + if (aWorldHitData.HasValue) { + if (bWorldHitData.HasValue) + { + if (aWorldHitData.Value.distance < bWorldHitData.Value.distance) + { + return -1; + } + + if (aWorldHitData.Value.distance > bWorldHitData.Value.distance) + { + return 1; + } + + return 0; + } + + // aWorldHitData.. but not b + if (b.worldSpaceBounds == null) + { + // b is UI overlay, leave it in front + return 1; + } + // else put a in front return -1; } - if (a.zOffsetForMousePoint > b.zOffsetForMousePoint) + if (bWorldHitData.HasValue) + { + //bWorldHitData.. but not a + if (a.worldSpaceBounds == null) + { + // a is UI overlay, leave it in front + return -1; + } + // else put b in front + return 1; + } + + // else no collider hit and not UI, so sort by the zOffset of the renderer itself (remember that -1f is used to represent 'behind the camera' so we have to consider that in our comparisons) + if (a.zOffsetForMousePoint >= 0f && a.zOffsetForMousePoint < b.zOffsetForMousePoint) + { + return -1; + } + + if (b.zOffsetForMousePoint >= 0f && a.zOffsetForMousePoint > b.zOffsetForMousePoint) { return 1; } @@ -322,5 +383,26 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer }); return result; } + + private static RaycastHit? IsHitOnParent(Transform transform, Dictionary worldSpaceHits) + { + foreach (var worldSpaceHit in worldSpaceHits) + { + // for each hit, walk up the hierarchy to see if a collider on myself or my parent was hit + while (transform != null) + { + var id = transform.GetInstanceID(); + if (worldSpaceHits.TryGetValue(id, out var hitInfo)) + { + return hitInfo; + } + + transform = transform.parent; + } + + } + + return null; + } } } From 20fb7ab1b5592a7359a65b70150aee6dc93bb1ba Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Tue, 10 Dec 2024 17:00:42 -0500 Subject: [PATCH 16/31] Completely re-write mouse exploration - still having an issue with unclick not triggering right though --- .../BotSegmentsPlaybackController.cs | 23 +- .../BotActions/KeyMomentMouseActionData.cs | 749 ++++++++++-------- .../KeyMoments/IKeyMomentExploration.cs | 1 + .../StateRecorder/MouseInputActionObserver.cs | 60 +- 4 files changed, 463 insertions(+), 370 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs index 06243ef0..377c93e8 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs @@ -106,14 +106,7 @@ private void EvaluateBotSegments() BotSegment firstActionSegment = _nextBotSegments[0]; try { - if (firstActionSegment.Replay_ActionCompleted) - { - if (firstActionSegment.botAction?.data is IKeyMomentExploration) - { - _explorationDriver.ReportPreviouslyCompletedAction(firstActionSegment.botAction.data); - } - } - else + if (firstActionSegment.botAction?.IsCompleted == false) { // allow the main action to retry between every exploratory action var didAction = firstActionSegment.ProcessAction(transformStatuses, entityStatuses, out var error); @@ -155,7 +148,13 @@ private void EvaluateBotSegments() } } - _explorationDriver.PerformExploratoryAction(firstActionSegment.Replay_SegmentNumber, transformStatuses, entityStatuses, out var explorationError); + string explorationError = null; + if (firstActionSegment.botAction?.data is IKeyMomentExploration keyMomentExploration) + { + _explorationDriver.PerformExploratoryAction(firstActionSegment.Replay_SegmentNumber, transformStatuses, entityStatuses, out explorationError); + // we just interfered mid action.. reset this thing to try again + keyMomentExploration.KeyMomentExplorationReset(); + } if (explorationError != null) { @@ -168,6 +167,12 @@ private void EvaluateBotSegments() } } } + + if (firstActionSegment.botAction?.IsCompleted == true && firstActionSegment.botAction?.data is IKeyMomentExploration) + { + _explorationDriver.ReportPreviouslyCompletedAction(firstActionSegment.botAction.data); + } + } catch (Exception ex) { diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 0b1ca5ea..5d48ad5a 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -74,6 +74,13 @@ public class KeyMomentMouseActionData : IBotActionData, IKeyMomentExploration, I // this is important as we don't want the next segment to run on this same frame until the result of the mouse click action has processed in the game engine private bool _isDoneWaitOneFrame = false; + + // These are all used to track the un-click work across multiple Update calls + private double _unClickTime = 0d; + private Vector2? _lastClickPosition = null; + private List _previousOverlappingObjects = new(); + private MouseInputActionData _unClickAction = null; + public bool IsCompleted() { return _isStopped; @@ -81,6 +88,23 @@ public bool IsCompleted() public void ReplayReset() { + _unClickAction = null; + _unClickTime = 0d; + _lastClickPosition = null; + _previousOverlappingObjects.Clear(); + + _isStopped = false; + _isDoneWaitOneFrame = false; + } + + public void KeyMomentExplorationReset() + { + // get ready to do this all over again + _unClickAction = null; + _unClickTime = 0d; + _lastClickPosition = null; + _previousOverlappingObjects.Clear(); + _isStopped = false; _isDoneWaitOneFrame = false; } @@ -141,101 +165,6 @@ public void StartAction(int segmentNumber, Dictionary curren } } - /** - * Return a tuple of (first non-matched token index by segment, token match count by segment) - */ - private (int[],int[]) EvaluateTokenMatches(IReadOnlyList toMatch, IReadOnlyList candidate) - { - if (toMatch == null || candidate == null || toMatch.Count != candidate.Count) - { - // not the correct path length - return (null,null); - } - - var segmentCount = toMatch.Count; - - var result = (new int[segmentCount], new int[segmentCount]); - - for (var i = 0; i < segmentCount; i++) - { - var toMatchSegment = toMatch[i]; - var candidateSegment = candidate[i]; - - var toMatchIndex = 0; - var candidateIndex = 0; - - var first0Index = -1; - var tokenMatchCount = 0; - - // handle candidate having extra tokens - if (candidateSegment.Length > toMatchSegment.Length) - { - var foundMatch = true; - for (; foundMatch && candidateIndex < candidateSegment.Length;) - { - foundMatch = false; - var candidateSegmentEntry = candidateSegment[candidateIndex]; - for (int j = toMatchIndex; j < toMatchSegment.Length; j++) - { - if (string.CompareOrdinal(candidateSegmentEntry, toMatchSegment[j] ) == 0) - { - foundMatch = true; - toMatchIndex = j; - ++tokenMatchCount; - break; // inner for loop - } - } - - if (!foundMatch && first0Index < 0) - { - first0Index = candidateIndex; - } - candidateIndex++; - } - } - else - { - var foundMatch = true; - for (; foundMatch && toMatchIndex < toMatchSegment.Length;) - { - foundMatch = false; - var toMatchSegmentEntry = toMatchSegment[toMatchIndex]; - for (int j = candidateIndex; j < candidateSegment.Length; j++) - { - if (string.CompareOrdinal(toMatchSegmentEntry, candidateSegment[j]) == 0) - { - foundMatch = true; - candidateIndex = j; - ++tokenMatchCount; - break; // inner for loop - } - } - if (!foundMatch && first0Index < 0) - { - first0Index = toMatchIndex; - } - toMatchIndex++; - } - } - // shortcut - leave as this wasn't valid - if (tokenMatchCount < 1) - { - return (null, null); - } - - result.Item1[i] = first0Index; - result.Item2[i] = tokenMatchCount; - - } - - return result; - } - - // we queue up all the actions to do before doing them in case we can't do one based on paths.. we don't want a partial operation - private readonly List<(Action, double)> _mouseActionsToDo = new(); - - private double _clickDownTime = 0d; - public bool ProcessAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) { if (_isDoneWaitOneFrame) @@ -247,179 +176,68 @@ public bool ProcessAction(int segmentNumber, Dictionary curr return true; } - if (_mouseActionsToDo.Count > 0) - { - // wait until the right time, then do the un-click mouse action - if (Time.unscaledTimeAsDouble >= _mouseActionsToDo[0].Item2) - { - _mouseActionsToDo[0].Item1.Invoke(); - _mouseActionsToDo.Clear(); - _isDoneWaitOneFrame = true; - - error = null; - return true; - } - else - { - error = null; - // need to return true so the bot doesn't start exploring.. it needs to think this action is processing as intended while we wait - return true; - } - } - - _mouseActionsToDo.Clear(); - if (!_isStopped) { - MouseInputActionData pendingAction = null; - Vector2? lastClickPosition = null; - - List previousOverlappingObjects = new(); - - var mouseActionsCount = mouseActions.Count; - for (var m = 0; m < mouseActionsCount; m++) + if (_unClickAction != null) { - List overlappingObjects = new(); - var mouseAction = mouseActions[m]; - var preConditionNormalizedPaths = _preconditionNormalizedPaths[m]; - - if (preConditionNormalizedPaths.Count == 0) - { - pendingAction = mouseAction; - continue; // the for - basically.. this is a mouse positional update, but we need the next click to know which position to put it in - } - - var preconditionMatches = BuildPreconditions(segmentNumber, currentTransforms, currentEntities, preConditionNormalizedPaths); + // handling [2] index.. the un-click - // now that we have all the precondition matches mapped out, let's see if we have the leftmost object.. - - // if the leftmost object is a UI object, then it should already be the smallest / most precise UI thing to click as we solve this by sorting - // the UI elements in mouseinputactionobserver.FindObjectsAtPosition based on the smallest screen-space bounds - if (preconditionMatches[0].Count > 0) + // wait until the right time, then do the un-click mouse action + if (Time.unscaledTimeAsDouble >= _unClickTime) { - // yay.. we found something(s).. figure out which one of them is the 'best' based on number of overlapping matches - - // for each of the left most preconditionMatches[0] .. go through each of other precondition matches and count how many overlap.. can only count 0 or 1 per each index - // we will also compute the 'smallest' click bounds for these overlaps as we go - var matchResults = new List<(ObjectStatus, int, (int,int,int,int))>(preconditionMatches[0].Count); - - foreach (var preconditionMatch0 in preconditionMatches[0]) + // find the best object and un-click on it if possible + var unClickPaths = _preconditionNormalizedPaths[2]; + var unClickPreconditionMatches = BuildPreconditions(segmentNumber, currentTransforms, currentEntities, unClickPaths); + var bestUnClickMatchResult = FindBestMatch(_unClickAction, unClickPreconditionMatches, out _); + if (bestUnClickMatchResult.HasValue) { - var smallestBounds = preconditionMatch0.Item1.screenSpaceBounds.Value; - // adjust in for the min to ensure we don't miss a click - var minX = (int)(smallestBounds.min.x + 0.51f); - var minY = (int)(smallestBounds.min.y + 0.51f); - // adjust in for the max to ensure we don't miss a click - var maxX = (int)(smallestBounds.max.x - 0.51f); - var maxY = (int)(smallestBounds.max.y - 0.51f); - - RGDebug.LogDebug($"Starting with bounds: ({minX}, {minY}),({maxX}, {maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} , target object: {preconditionMatch0.Item1.NormalizedPath}"); - // now let's narrow down the screen space bounds more precisely based on all our preconditions - // count the number of indexes where we had an overlap while doing it - var overlapCount = 0; - for (var i = 1; i < preconditionMatches.Length; i++) - { - var preconditionMatchesI = preconditionMatches[i]; - if (preconditionMatchesI.Count > 0) - { - var didOverlap = false; - foreach (var preconditionMatchI in preconditionMatchesI) - { - // not the same logic as MouseEventSender.FindBestClickObject.. this version narrows in on the smallest bounding area - - // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds - var newBounds = preconditionMatchI.Item1.screenSpaceBounds.Value; - // adjust in for the min to ensure we don't miss a click - var newMinX = (int)(newBounds.min.x + 0.51f); - var newMinY = (int)(newBounds.min.y + 0.51f); - // adjust in for the max to ensure we don't miss a click - var newMaxX = (int)(newBounds.max.x - 0.51f); - var newMaxY = (int)(newBounds.max.y - 0.51f); - - // since these are pixel bounds... we just do (int) cast - var startingMinX = minX; - var startingMinY = minY; - var startingMaxX = maxX; - var startingMaxY = maxY; - - if (newMinX > minX && newMinX < maxX) - { - minX = newMinX; - } - - if (newMinY > minY && newMinY < maxY) - { - minY = newMinY; - } + var bestObjectStatus = bestUnClickMatchResult.Value.Item1; - if (newMaxX > minX && newMaxX < maxX) - { - maxX = newMaxX; - } + var clickPosition = GetClickPositionForMatch(bestUnClickMatchResult.Value, out var usingOriginalClickPosition ); - if (newMaxY > minY && newMaxY < maxY) - { - maxY = newMaxY; - } - - if (minX != startingMinX || minY != startingMinY || maxX != startingMaxX || maxY != startingMaxY) - { - overlappingObjects.Add(preconditionMatchI.Item1); - didOverlap = true; - RGDebug.LogDebug($"Narrowed bounds: ({minX}, {minY}),({maxX}, {maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} for overlap with object path [{i}]: {preconditionMatchI.Item1.NormalizedPath}"); - } - } - - if (didOverlap) - { - ++overlapCount; - } - } + // only send the bestObjectStatus for filtering when NOT un-clicking at the original position + if (!DoActionForObjectAtPosition(segmentNumber, _unClickAction, usingOriginalClickPosition?null:bestObjectStatus, clickPosition, _unClickAction.clickedObjectNormalizedPaths[0], currentTransforms, out error)) + { + return false; } - matchResults.Add((preconditionMatch0.Item1, overlapCount, (minX,minY,maxX,maxY))); + _unClickAction = null; + _isDoneWaitOneFrame = true; + error = null; + return true; } - - matchResults.Sort((a, b) => + else { - if (a.Item2 < b.Item2) - { - // sort lower match counts to the end - return 1; - } + // didn't find it.. this is where 'exploration' is going to start happening based on our result + error = $"No valid mouse un-click action object found for path:\n{unClickPaths[0].normalizedPath}"; + return false; + } - if (a.Item2 > b.Item2) - { - // sort highest match counts to the front - return -1; - } + } + else + { + error = null; + // need to return true so the bot doesn't start exploring.. it needs to think this action is processing as intended while we wait + return true; + } + } + else + { + // handling [0] , [1] indexes.. the positioning and the clic + //these have the same position.. so we can compute it just once - // else sort by smallest bounds - var aArea = (a.Item3.Item3 - a.Item3.Item1) * (a.Item3.Item4 - a.Item3.Item2); - var bArea = (b.Item3.Item3 - b.Item3.Item1) * (b.Item3.Item4 - b.Item3.Item2); - if (aArea < bArea) - { - return -1; - } + var movementAction = mouseActions[0]; + var clickAction = mouseActions[1]; + var clickPaths = _preconditionNormalizedPaths[1]; - // floating point math.. don't really care about equality in the zillionth of a percent chance that happens here - return 1; - }); - - var bestObjectStatus = matchResults[0].Item1; - // we started with clicking the center.. but this was limiting - //clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); - // instead... make the click-position a random position within the bounds - // 1. for more variability in testing - // 2. to get around cases where we were say clicking on a floor tile, but there is something on that floor tile and we wanted to click on the open space of the floor tile - // on the next attempt, it picks a new position to try thus giving us a better chance of passing - // TODO (REG-2223): Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? - // This would allow us to know that we clicked about X far into this floor tile and replicate that positioning regardless of the actual worldspace positioning in the replay... - // +1 because int range is max exclusive.. if these were floats.. remove the +1 - Vector2 clickPosition = new Vector2(Random.Range(matchResults[0].Item3.Item1, matchResults[0].Item3.Item3+1), Random.Range(matchResults[0].Item3.Item2, matchResults[0].Item3.Item4+1)); + var preconditionMatches = BuildPreconditions(segmentNumber, currentTransforms, currentEntities, clickPaths); + var bestMatchResult = FindBestMatch(clickAction, preconditionMatches, out var overlappingObjects); + if (bestMatchResult.HasValue) + { + var bestObjectStatus = bestMatchResult.Value.Item1; var isInteractable = true; // now that we have the 'best' match ... first let's make sure it's ready to be clicked @@ -438,85 +256,98 @@ public bool ProcessAction(int segmentNumber, Dictionary curr if (!isInteractable) { - _mouseActionsToDo.Clear(); // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse action object found for path:\n{preConditionNormalizedPaths[0].normalizedPath}"; + error = $"No valid mouse action object found for path:\n{clickPaths[0].normalizedPath}"; return false; } - // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not - // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds - if (lastClickPosition.HasValue) - { - if (lastClickPosition.Value.x >= matchResults[0].Item3.Item1 - && lastClickPosition.Value.x <= matchResults[0].Item3.Item3 - && lastClickPosition.Value.y >= matchResults[0].Item3.Item2 - && lastClickPosition.Value.y <= matchResults[0].Item3.Item4) - { - RGDebug.LogDebug($"Leaving mouse action index {m} at previous action position based on bounds overlaps to original path: {bestObjectStatus.NormalizedPath}"); - // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the - // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame - clickPosition = lastClickPosition.Value; - } - else if (previousOverlappingObjects.Count > 0) - { - // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) - // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button - // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen - // isn't important.. we want to un-click exactly where we clicked - if (previousOverlappingObjects.Any(a => a == bestObjectStatus)) - { - RGDebug.LogDebug($"Leaving mouse action index {m} at previous action position based on object path existing at time of click: {bestObjectStatus.NormalizedPath}"); - clickPosition = lastClickPosition.Value; - } - } - } + var clickPosition = GetClickPositionForMatch(bestMatchResult.Value, out _); - if (!QueueClickForObjectAtPosition(segmentNumber, pendingAction, mouseAction, bestObjectStatus, clickPosition, currentTransforms, out error)) + // pass null for bestObjectStatus to avoid click object validation + if (!DoActionForObjectAtPosition(segmentNumber, movementAction, bestObjectStatus, clickPosition,clickAction.clickedObjectNormalizedPaths[0], currentTransforms, out error)) { - _mouseActionsToDo.Clear(); return false; } - if (m == 1) + if (!DoActionForObjectAtPosition(segmentNumber, clickAction, bestObjectStatus, clickPosition,clickAction.clickedObjectNormalizedPaths[0], currentTransforms, out error)) { - previousOverlappingObjects = overlappingObjects; - // on the 'click'.. save the position - lastClickPosition = clickPosition; + return false; } - pendingAction = null; + _previousOverlappingObjects = overlappingObjects; + // on the 'click'.. save the position + _lastClickPosition = clickPosition; + _unClickAction = mouseActions[2]; + // save off the time the unClick should happen here so mouse button holds work + _unClickTime = clickAction.startTime == 0d ? 0d : Time.unscaledTimeAsDouble + _unClickAction.startTime - clickAction.startTime; + + return true; } else { - _mouseActionsToDo.Clear(); - // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse action object found for paths:\n{string.Join("\n", preConditionNormalizedPaths.Select(a => a.normalizedPath))}"; + error = $"No valid mouse action object found for paths:\n{string.Join("\n", clickPaths.Select(a => a.normalizedPath))}"; return false; } } + } - var _mouseActionsToDoCount = _mouseActionsToDo.Count; - if (_mouseActionsToDoCount > 0) + error = null; + return false; + } + + private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float,float)) bestMatchResult, out bool usingOriginalClickPosition) + { + usingOriginalClickPosition = false; + // we started with clicking the center.. but this was limiting + //clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); + // instead... make the click-position a random position within the bounds + // 1. for more variability in testing + // 2. to get around cases where we were say clicking on a floor tile, but there is something on that floor tile and we wanted to click on the open space of the floor tile + // on the next attempt, it picks a new position to try thus giving us a better chance of passing + // TODO (REG-2223): Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? + // This would allow us to know that we clicked about X far into this floor tile and replicate that positioning regardless of the actual worldspace positioning in the replay... + // +1 because int range is max exclusive.. if these were floats.. remove the +1 + Vector2 result = new Vector2(Random.Range(bestMatchResult.Item3.Item1, bestMatchResult.Item3.Item3 + 1), Random.Range(bestMatchResult.Item3.Item2, bestMatchResult.Item3.Item4 + 1)); + + // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not + // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds + if (_lastClickPosition.HasValue) + { + if (_lastClickPosition.Value.x >= bestMatchResult.Item3.Item1 + && _lastClickPosition.Value.x <= bestMatchResult.Item3.Item3 + && _lastClickPosition.Value.y >= bestMatchResult.Item3.Item2 + && _lastClickPosition.Value.y <= bestMatchResult.Item3.Item4) + { + RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on bounds overlaps to original path: {bestMatchResult.Item1.NormalizedPath}"); + // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the + // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame + result = _lastClickPosition.Value; + usingOriginalClickPosition = true; + } + else if (_previousOverlappingObjects.Count > 0) { - // only do the first 2 actions - for (var i = 0; i < 2; i++) + // this can handle things where the unclick was recorded 'after' the element dis-appeared, but it is still there in the replay until the unclick happens... this is just a matter of how ui events and when we can observe during recording works + + // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) + // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button + // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen + // isn't important.. we want to un-click exactly where we clicked + if (_previousOverlappingObjects.Any(a => a == bestMatchResult.Item1)) { - _mouseActionsToDo[i].Item1.Invoke(); + RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on object path existing at time of click: {bestMatchResult.Item1.NormalizedPath}"); + result = _lastClickPosition.Value; + usingOriginalClickPosition = true; } - // remove the first 2 things we already did - _mouseActionsToDo.RemoveRange(0,2); - - error = null; - return true; } - } - error = null; - return false; + return result; } + /** + * Returns an array of Lists where each list has entries that are (ObjectStatus, (first non-matched token index by segment, token match count by segment)) + * array of lists, because for each precondition, there can be multiple possible matching objects to evaluate + */ private List<(ObjectStatus, (int[], int[]))>[] BuildPreconditions(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, List preConditionNormalizedPaths) { var possibleTransformsToClick = currentTransforms.Values.Where(a => a.screenSpaceBounds.HasValue).ToList(); @@ -606,60 +437,304 @@ public bool ProcessAction(int segmentNumber, Dictionary curr return preconditionMatches; } - private bool QueueClickForObjectAtPosition(int segmentNumber, MouseInputActionData pendingAction, MouseInputActionData mouseAction, ObjectStatus bestObjectStatus, Vector2 myClickPosition, Dictionary currentTransforms, out string error) + /** + * Return a tuple of (first non-matched token index by segment, token match count by segment) + */ + private (int[],int[]) EvaluateTokenMatches(IReadOnlyList toMatch, IReadOnlyList candidate) { - // we fill 'myClickPosition' in for all code paths.. if you get a null here, something above has bad logic as this should be filled in always here - if (pendingAction != null) + if (toMatch == null || candidate == null || toMatch.Count != candidate.Count) + { + // not the correct path length + return (null,null); + } + + var segmentCount = toMatch.Count; + + var result = (new int[segmentCount], new int[segmentCount]); + + for (var i = 0; i < segmentCount; i++) { - // we are on the 2nd action, which is the 'click' - if (bestObjectStatus is TransformStatus) + var toMatchSegment = toMatch[i]; + var candidateSegment = candidate[i]; + + var toMatchIndex = 0; + var candidateIndex = 0; + + var first0Index = -1; + var tokenMatchCount = 0; + + // handle candidate having extra tokens + if (candidateSegment.Length > toMatchSegment.Length) { - // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over - // our intended click item (iow.. it's there/ready, but obstructed) ... - // make sure our object is at the front of the list and thus at the closest Z depth at that point.. findobjectsatposition handles z depth sorting for us - // or that the item at the front of the list is on the same path tree as us (we might have clicked on the text part of the button instead of the button, but both still click the button) - - var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values, out _); - // see if our object is 'first' or obstructed - if (objectsAtClickPosition.Count > 0) + var foundMatch = true; + for (; foundMatch && candidateIndex < candidateSegment.Length;) { - if (!mouseAction.clickedObjectNormalizedPaths[0].StartsWith(objectsAtClickPosition[0].NormalizedPath)) + foundMatch = false; + var candidateSegmentEntry = candidateSegment[candidateIndex]; + for (int j = toMatchIndex; j < toMatchSegment.Length; j++) { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\na UI object is obstructing with path:\n{objectsAtClickPosition[0].NormalizedPath}"; - return false; + if (string.CompareOrdinal(candidateSegmentEntry, toMatchSegment[j] ) == 0) + { + foundMatch = true; + toMatchIndex = j; + ++tokenMatchCount; + break; // inner for loop + } + } + + if (!foundMatch && first0Index < 0) + { + first0Index = candidateIndex; } + candidateIndex++; } - else + } + else + { + var foundMatch = true; + for (; foundMatch && toMatchIndex < toMatchSegment.Length;) { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{mouseAction.clickedObjectNormalizedPaths[0]}\nno objects at that position"; - return false; + foundMatch = false; + var toMatchSegmentEntry = toMatchSegment[toMatchIndex]; + for (int j = candidateIndex; j < candidateSegment.Length; j++) + { + if (string.CompareOrdinal(toMatchSegmentEntry, candidateSegment[j]) == 0) + { + foundMatch = true; + candidateIndex = j; + ++tokenMatchCount; + break; // inner for loop + } + } + if (!foundMatch && first0Index < 0) + { + first0Index = toMatchIndex; + } + toMatchIndex++; } } - - var myPendingAction = pendingAction; - _mouseActionsToDo.Add((() => + // shortcut - leave as this wasn't valid + if (tokenMatchCount < 1) { - RGDebug.LogInfo($"KeyMoment - Mouse Pending Action applied at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); - // perform the mouse action at the center of our new smallest bounds - MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, myPendingAction.leftButton, myPendingAction.middleButton, myPendingAction.rightButton, myPendingAction.forwardButton, myPendingAction.backButton, myPendingAction.scroll); - }, 0d)); + return (null, null); + } + + result.Item1[i] = first0Index; + result.Item2[i] = tokenMatchCount; } - _mouseActionsToDo.Add((() => + return result; + } + + private (ObjectStatus, int, (float,float,float,float))? FindBestMatch(MouseInputActionData mouseAction, List<(ObjectStatus, (int[], int[]))>[] preconditionMatches, out List overlappingObjects) + { + overlappingObjects = new List(); + + // now that we have all the precondition matches mapped out, let's see if we have the leftmost object.. + if (preconditionMatches[0].Count > 0) { - RGDebug.LogInfo($"KeyMoment - Mouse Action at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {mouseAction.clickedObjectNormalizedPaths[0]}"); - // perform the mouse action at the center of our new smallest bounds - MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, mouseAction.leftButton, mouseAction.middleButton, mouseAction.rightButton, mouseAction.forwardButton, mouseAction.backButton, mouseAction.scroll); - // handle setting the delay time between the click down and up for the [2] index item - }, _clickDownTime == 0d ? 0d : Time.unscaledTimeAsDouble + mouseAction.startTime - _clickDownTime)); + // yay.. we found something(s).. figure out which one of them is the 'best' based on number of overlapping matches + + // for each of the left most preconditionMatches[0] .. go through each of other precondition matches and count how many overlap.. can only count 0 or 1 per each index + // we will also compute the 'smallest' click bounds for these overlaps as we go + var matchResults = new List<(ObjectStatus, int, (float, float, float, float))>(preconditionMatches[0].Count); - if (pendingAction != null) + foreach (var preconditionMatch0 in preconditionMatches[0]) + { + var isPreconditionMatch0Interactable = true; + // now that we have a candidate match ... first let's make sure it's ready to be clicked + // visible (already true by the time we get here)/active-enabled(we already know that from a canvas perspective this is visible, but need to check UI component info) + if (preconditionMatch0.Item1 is TransformStatus preconditionMatch0TransformStatus) + { + var theTransform = preconditionMatch0TransformStatus.Transform; + if (theTransform is RectTransform) + { + // ui object + var selectables = theTransform.GetComponents(); + // make sure 1 is interactable + isPreconditionMatch0Interactable = selectables.Any(a => a.interactable); + } + } + + if (isPreconditionMatch0Interactable) + { + // ReSharper disable once PossibleInvalidOperationException - already filtered in BuildPreconditions to only have entries with valid visible bounds + var smallestBounds = preconditionMatch0.Item1.screenSpaceBounds.Value; + // we tried doing this with int math.. but pixel fluctuations of objects with fractional render bounds are a thing (think shimmering/flicker along aliased edges in games) + var minX = smallestBounds.min.x; + var minY = smallestBounds.min.y; + var maxX = smallestBounds.max.x; + var maxY = smallestBounds.max.y; + + var originalMinX = minX; + var originalMinY = minY; + var originalMaxX = maxX; + var originalMaxY = maxY; + + RGDebug.LogDebug($"Starting with bounds: ({minX}, {minY}),({maxX}, {maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} , target object: {preconditionMatch0.Item1.NormalizedPath}"); + // now let's narrow down the screen space bounds more precisely based on all our preconditions + // count the number of indexes where we had an overlap while doing it + var overlapCount = 0; + for (var i = 1; i < preconditionMatches.Length; i++) + { + var preconditionMatchesI = preconditionMatches[i]; + if (preconditionMatchesI.Count > 0) + { + var didOverlap = false; + foreach (var preconditionMatchI in preconditionMatchesI) + { + var isInteractable = true; + // now that we have a candidate match ... first let's make sure it's ready to be clicked + // visible (already true by the time we get here)/active-enabled(we already know that from a canvas perspective this is visible, but need to check UI component info) + if (preconditionMatchI.Item1 is TransformStatus tStatus) + { + var theTransform = tStatus.Transform; + if (theTransform is RectTransform) + { + // ui object + var selectables = theTransform.GetComponents(); + // make sure 1 is interactable + isInteractable = selectables.Any(a => a.interactable); + } + } + if (isInteractable) + { + // ReSharper disable once PossibleInvalidOperationException - already filtered in BuildPreconditions to only have entries with valid visible bounds + var newBounds = preconditionMatchI.Item1.screenSpaceBounds.Value; + // adjust in for the min to ensure we don't miss a click + var newMinX = newBounds.min.x; + var newMinY = newBounds.min.y; + // adjust in for the max to ensure we don't miss a click + var newMaxX = newBounds.max.x; + var newMaxY = newBounds.max.y; + + if (newMinX >= minX && newMinX <= maxX) + { + minX = newMinX; + } + + if (newMinY >= minY && newMinY <= maxY) + { + minY = newMinY; + } + + if (newMaxX >= minX && newMaxX <= maxX) + { + maxX = newMaxX; + } + + if (newMaxY >= minY && newMaxY <= maxY) + { + maxY = newMaxY; + } + + + if (newMinX >= originalMinX && newMinX <= originalMaxX) + { + didOverlap = true; + } + + if (newMinY >= originalMinY && newMinY <= originalMaxY) + { + didOverlap = true; + } + + if (newMaxX >= originalMinX && newMaxX <= originalMaxX) + { + didOverlap = true; + } + + if (newMaxY >= originalMinY && newMaxY <= originalMaxY) + { + didOverlap = true; + } + + if (didOverlap) + { + overlappingObjects.Add(preconditionMatchI.Item1); + RGDebug.LogDebug($"Tightened bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} - overlap with object path [{i}]: {preconditionMatchI.Item1.NormalizedPath}"); + } + } + } + + if (didOverlap) + { + ++overlapCount; + } + } + } + + matchResults.Add((preconditionMatch0.Item1, overlapCount, (minX, minY, maxX, maxY))); + } + } + + matchResults.Sort((a, b) => + { + if (a.Item2 < b.Item2) + { + // sort lower match counts to the end + return 1; + } + + if (a.Item2 > b.Item2) + { + // sort highest match counts to the front + return -1; + } + + // else sort by smallest bounds + var aArea = (a.Item3.Item3 - a.Item3.Item1) * (a.Item3.Item4 - a.Item3.Item2); + var bArea = (b.Item3.Item3 - b.Item3.Item1) * (b.Item3.Item4 - b.Item3.Item2); + + if (aArea < bArea) + { + return -1; + } + + // floating point math.. don't really care about equality in the zillionth of a percent chance that happens here + return 1; + }); + + if (matchResults.Count > 0) + { + var bestMatchResult = matchResults[0]; + return bestMatchResult; + } + } + + return null; + } + + private bool DoActionForObjectAtPosition(int segmentNumber, MouseInputActionData mouseAction, ObjectStatus bestObjectStatus, Vector2 myClickPosition, string targetObjectPath, Dictionary currentTransforms, out string error) + { + if (bestObjectStatus is TransformStatus) { - // handle setting the delay time between the click down and up for the [1] index item - _clickDownTime = mouseAction.startTime; + // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over + // our intended click item (iow.. it's there/ready, but obstructed) ... + // make sure our object is at the front of the list and thus at the closest Z depth at that point.. FindObjectsAtPosition handles z depth/etc sorting for us + // or that the item at the front of the list is on the same path tree as us (we might have clicked on the text part of the button instead of the button, but both still click the button) + var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values); + // see if our object is 'first' or obstructed + if (objectsAtClickPosition.Count > 0) + { + if (!targetObjectPath.StartsWith(objectsAtClickPosition[0].NormalizedPath)) + { + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{targetObjectPath}\nanother object is obstructing with path:\n{objectsAtClickPosition[0].NormalizedPath}"; + return false; + } + } + else + { + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{targetObjectPath}\nno objects at that position"; + return false; + } } + RGDebug.LogInfo($"KeyMoment - Mouse Action at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {targetObjectPath}"); + // perform the mouse action at the center of our new smallest bounds + MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, mouseAction.leftButton, mouseAction.middleButton, mouseAction.rightButton, mouseAction.forwardButton, mouseAction.backButton, mouseAction.scroll); + error = null; return true; } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs index a4b2d994..1fdcc90f 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs @@ -2,5 +2,6 @@ { public interface IKeyMomentExploration { + void KeyMomentExplorationReset(); } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index 66d65bc9..3be73f54 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -28,7 +28,7 @@ public void ObserveMouse(IEnumerable statefulObjects) if (_priorMouseState == null && newMouseState.IsButtonClicked || _priorMouseState != null && !_priorMouseState.ButtonStatesEqual(newMouseState)) { // get the z depth sorted clicked on objects - var clickedOnObjects = FindObjectsAtPosition(newMouseState.position, statefulObjects, out _); + var clickedOnObjects = FindObjectsAtPosition(newMouseState.position, statefulObjects); _clickedObjectNormalizedPaths.Clear(); @@ -157,12 +157,11 @@ public List FlushInputDataBuffer(bool ignoreLastClick, boo return result; } - public static List FindObjectsAtPosition(Vector2 position, IEnumerable statefulObjects, out float maxZDepth) + public static List FindObjectsAtPosition(Vector2 position, IEnumerable statefulObjects) { // make sure screen space position Z is around 0 var vec3Position = new Vector3(position.x, position.y, 0); List result = new(); - maxZDepth = 0f; var mainCamera = Camera.main; Ray? ray = null; if (mainCamera != null) @@ -170,6 +169,15 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer ray = mainCamera.ScreenPointToRay(position); } + var worldSpaceHits = new Dictionary(); + + if (ray.HasValue) + { + // do a raycast all into the world and see what hits... we want to be able to sort things with colliders above those that don't have one + var raycastHits = Physics.RaycastAll(ray.Value); + worldSpaceHits = raycastHits.ToDictionary(a => a.transform.GetInstanceID(), a => a); + } + foreach (var recordedGameObjectState in statefulObjects) { if (recordedGameObjectState.screenSpaceBounds.HasValue) @@ -200,19 +208,33 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer if (ray.HasValue) { // compute the zOffset at this point based on the ray - if (recordedGameObjectState.worldSpaceBounds.Value.IntersectRay(ray.Value, out var zDepthHit)) + var didHit = false; + if (recordedGameObjectState is TransformStatus transformStatus) { - if (zDepthHit >= 0f) + var collider = transformStatus.Transform.GetComponentInParent(); + if (collider != null) { - recordedGameObjectState.zOffsetForMousePoint = zDepthHit; - recordedGameObjectState.worldSpaceCoordinatesForMousePoint = ray.Value.GetPoint(zDepthHit); - if (zDepthHit > maxZDepth) + // let's see if it has a collider to hit + var myHit = worldSpaceHits.Values.Select(a=> a as RaycastHit?).Where(a => a.Value.collider == collider).DefaultIfEmpty(null).FirstOrDefault(); + if (myHit.HasValue) { - maxZDepth = zDepthHit; + var hitDistance = myHit.Value.distance; + if (hitDistance >= 0f) + { + var hitPoint = ray.Value.GetPoint(hitDistance); + // make sure that even though our parent had this collider, that the hit is still within the bounding volume of the object we're checking + // we wouldn't want a parent collider to cause ALL of its children to be included as clicked, just the one ray pass of renderers we actually hit + if (recordedGameObjectState.worldSpaceBounds.Value.Contains(hitPoint)) + { + didHit = true; + recordedGameObjectState.zOffsetForMousePoint = hitDistance; + recordedGameObjectState.worldSpaceCoordinatesForMousePoint = hitPoint; + } + } } } } - else + if(!didHit) { // wasn't in front of the camera recordedGameObjectState.zOffsetForMousePoint = -1f; @@ -221,17 +243,16 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer } else { - // go with the zOffset for the bounding box itself instead of at this exact point... this leads to really weird cases in 3d spaces with close objects and should be avoided + // go with the zOffset for the bounding box itself instead of at this exact point... this leads to really weird cases in world space objects with other nearby objects and should be avoided // example.. a stone you click sitting on the top back corner of a cube will be 'behind' the cube because the front plane of the cube is closer, just not at that screen point // leaving this in here for now though as some sorting is still better than no sorting if we get here (have no camera) - if (recordedGameObjectState.screenSpaceZOffset > maxZDepth) - { - maxZDepth = recordedGameObjectState.screenSpaceZOffset; - } + recordedGameObjectState.zOffsetForMousePoint = recordedGameObjectState.screenSpaceZOffset; + recordedGameObjectState.worldSpaceCoordinatesForMousePoint = recordedGameObjectState.worldSpaceBounds.Value.center; } } else if (recordedGameObjectState.screenSpaceZOffset >= 0f) { + // screen space object recordedGameObjectState.zOffsetForMousePoint = recordedGameObjectState.screenSpaceZOffset; recordedGameObjectState.worldSpaceCoordinatesForMousePoint = null; } @@ -254,15 +275,6 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer var uiHitMapping = eventSystemHits.ToDictionary(a => (long)a.gameObject.transform.GetInstanceID(), a => a); - var worldSpaceHits = new Dictionary(); - - if (ray.HasValue) - { - // do a raycast all into the world and see what hits... we want to be able to sort things with colliders above those that don't have one - var raycastHits = Physics.RaycastAll(ray.Value); - worldSpaceHits = raycastHits.ToDictionary(a => a.transform.GetInstanceID(), a => a); - } - // sort with lower z-offsets first so we have UI things on top of game object things // if both elements are from the UI.. then sort by the event system ray hits // for world space objects.. sort those with ray cast hits before those without From baff2aa0d3da8d523e6021e862909d3d84918eab Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Tue, 10 Dec 2024 17:18:13 -0500 Subject: [PATCH 17/31] Fix overlap logic --- .../BotActions/KeyMomentMouseActionData.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 5d48ad5a..67921240 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -600,6 +600,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, } if (isInteractable) { + var pmIDidOverlap = false; // ReSharper disable once PossibleInvalidOperationException - already filtered in BuildPreconditions to only have entries with valid visible bounds var newBounds = preconditionMatchI.Item1.screenSpaceBounds.Value; // adjust in for the min to ensure we don't miss a click @@ -632,26 +633,27 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, if (newMinX >= originalMinX && newMinX <= originalMaxX) { - didOverlap = true; + pmIDidOverlap = true; } if (newMinY >= originalMinY && newMinY <= originalMaxY) { - didOverlap = true; + pmIDidOverlap = true; } if (newMaxX >= originalMinX && newMaxX <= originalMaxX) { - didOverlap = true; + pmIDidOverlap = true; } if (newMaxY >= originalMinY && newMaxY <= originalMaxY) { - didOverlap = true; + pmIDidOverlap = true; } - if (didOverlap) + if (pmIDidOverlap) { + didOverlap = true; overlappingObjects.Add(preconditionMatchI.Item1); RGDebug.LogDebug($"Tightened bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} - overlap with object path [{i}]: {preconditionMatchI.Item1.NormalizedPath}"); } From d294c4eca6e30798ce96c9b0827e0cb3c0e693b2 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Tue, 10 Dec 2024 17:24:30 -0500 Subject: [PATCH 18/31] Properly reset previous action exploration indexes --- .../BotSegments/ActionExplorationDriver.cs | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index a869f408..142500fd 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -89,32 +89,7 @@ public void PerformExploratoryAction(int segmentNumber, Dictionary= _previousActions.Count) - { - _previousActionIndex = 0; - } - - // retry prior actions in pattern .. 0, 10, 210 where 2 is the oldest and 0 is the most recent - if (_previousActionIndex < _previousActions.Count) - { - var previousActions = _previousActions[_previousActionIndex]; - if (_previousActionSubIndex >= previousActions.Count) - { - // move to the next list - _previousActionSubIndex = 0; - ++_previousActionIndex; - if (_previousActionIndex >= _previousActions.Count) - { - _previousActionIndex = 0; - } - - previousActions = _previousActions[_previousActionIndex]; - } - - nextAction = previousActions[_previousActionSubIndex]; - ++_previousActionSubIndex; - // process next action in the list - } + nextAction = GetPriorActionToDo(); } if (nextAction != null) @@ -160,6 +135,39 @@ public void PerformExploratoryAction(int segmentNumber, Dictionary= _previousActions.Count) + { + // reset both so we don't skip one in the list below + _previousActionSubIndex = 0; + _previousActionIndex = 0; + } + + // retry prior actions in pattern .. 0, 10, 210 where 2 is the oldest and 0 is the most recent + if (_previousActionIndex < _previousActions.Count) + { + var previousActions = _previousActions[_previousActionIndex]; + if (_previousActionSubIndex >= previousActions.Count) + { + // move to the next list + _previousActionSubIndex = 0; + ++_previousActionIndex; + if (_previousActionIndex >= _previousActions.Count) + { + _previousActionIndex = 0; + } + + previousActions = _previousActions[_previousActionIndex]; + } + + return previousActions[_previousActionSubIndex++]; + // process next action in the list + } + + return null; + } + public void StopExploring(int segmentNumber) { if (_inProgressAction != null) From 02a2eb4f2d530b4285c7a679804adce3500dfa3c Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Wed, 11 Dec 2024 12:37:56 -0500 Subject: [PATCH 19/31] almost there... just need to debug why the toggle god mode doesn't click --- .../BotSegments/ActionExplorationDriver.cs | 129 ++++--- .../BotSegmentsPlaybackController.cs | 49 ++- .../BotActions/KeyMomentMouseActionData.cs | 342 ++++++++++-------- .../StateRecorder/KeyMomentEvaluator.cs | 114 +++--- .../StateRecorder/MouseInputActionObserver.cs | 81 ++--- 5 files changed, 387 insertions(+), 328 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index 142500fd..d9620378 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -8,24 +8,30 @@ namespace StateRecorder.BotSegments { + + public enum ExplorationState + { + STOPPED, + PAUSED, + EXPLORING + } public class ActionExplorationDriver : MonoBehaviour { + private const int PRIOR_ACTION_LIMIT = 5; // normally the first thing we do is retry the previous actions // these are sorted newest to oldest - private readonly List PreviouslyCompletedActions = new(3); - - private readonly List> _previousActions = new(3); + private readonly List PreviouslyCompletedActions = new(PRIOR_ACTION_LIMIT); - private int _previousActionIndex = 0; - private int _previousActionSubIndex = 0; + private int _previousActionsNextIndex = 0; + private int _previousActionsStartIndex = 0; - public bool IsExploring { get; private set;} + public ExplorationState ExplorationState { get; private set; } = ExplorationState.STOPPED; public void ReportPreviouslyCompletedAction(IBotActionData action) { - // limit to 3 prior actions - while (PreviouslyCompletedActions.Count > 2) + // limit to PRIOR_ACTION_LIMIT prior actions + while (PreviouslyCompletedActions.Count >= PRIOR_ACTION_LIMIT) { // remove at front PreviouslyCompletedActions.RemoveAt(0); @@ -41,38 +47,28 @@ public void ReportPreviouslyCompletedAction(IBotActionData action) RGDebug.LogInfo($"ActionExplorationDriver - Adding previously completed action of Type: {action.GetType().Name}" + extraLog); // insert at end PreviouslyCompletedActions.Add(action); + _previousActionsNextIndex = PreviouslyCompletedActions.Count-1; + _previousActionsStartIndex = PreviouslyCompletedActions.Count-1; } } - /** - * Return true only when we start exploring, false if we were already exploring - */ - public bool StartExploring() + public void StartExploring() { - if (!IsExploring) + switch (ExplorationState) { - _previousActions.Clear(); - _previousActionIndex = 0; - _previousActionSubIndex = 0; - List priorList = null; - foreach (var previouslyCompletedAction in PreviouslyCompletedActions) - { - // populate these lists so that we have a pattern of 0, 10, 210 where 0 is the newest and 2 is the oldest and we try them in patterns as listed - var actionList = new List (3); - actionList.Add(previouslyCompletedAction); - if (priorList != null) - { - actionList.AddRange(priorList); - } - priorList = actionList; - _previousActions.Add(actionList); - } - RGDebug.LogInfo("ActionExplorationDriver - Starting Exploratory Actions"); - IsExploring = true; - return true; + case ExplorationState.STOPPED: + _previousActionsNextIndex = PreviouslyCompletedActions.Count-1; + _previousActionsStartIndex = PreviouslyCompletedActions.Count-1; + RGDebug.LogInfo("ActionExplorationDriver - Starting Exploratory Actions"); + ExplorationState = ExplorationState.EXPLORING; + break; + case ExplorationState.PAUSED: + RGDebug.LogInfo("ActionExplorationDriver - Resuming Exploratory Actions"); + ExplorationState = ExplorationState.EXPLORING; + break; + } - return false; } private IBotActionData _inProgressAction = null; @@ -80,7 +76,7 @@ public bool StartExploring() public void PerformExploratoryAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) { error = null; - if (!IsExploring) + if (ExplorationState.EXPLORING != ExplorationState) { return; } @@ -99,7 +95,7 @@ public void PerformExploratoryAction(int segmentNumber, Dictionary= _previousActions.Count) + // retry prior actions in pattern .. 0, 10, 210, etc... where 2 is the oldest and 0 is the most recent + if (PreviouslyCompletedActions.Count > 0) { - // reset both so we don't skip one in the list below - _previousActionSubIndex = 0; - _previousActionIndex = 0; - } - - // retry prior actions in pattern .. 0, 10, 210 where 2 is the oldest and 0 is the most recent - if (_previousActionIndex < _previousActions.Count) - { - var previousActions = _previousActions[_previousActionIndex]; - if (_previousActionSubIndex >= previousActions.Count) + if (_previousActionsStartIndex < 0) { - // move to the next list - _previousActionSubIndex = 0; - ++_previousActionIndex; - if (_previousActionIndex >= _previousActions.Count) - { - _previousActionIndex = 0; - } + // reset both + _previousActionsNextIndex = PreviouslyCompletedActions.Count - 1; + _previousActionsStartIndex = PreviouslyCompletedActions.Count - 1; + } - previousActions = _previousActions[_previousActionIndex]; + var nextAction = PreviouslyCompletedActions[_previousActionsNextIndex]; + + if (--_previousActionsNextIndex < _previousActionsStartIndex) + { + // update the startIndex and reset the next index + _previousActionsNextIndex = PreviouslyCompletedActions.Count - 1; + _previousActionsStartIndex--; } - return previousActions[_previousActionSubIndex++]; - // process next action in the list + return nextAction; } + _previousActionsNextIndex = PreviouslyCompletedActions.Count - 1; + _previousActionsStartIndex = PreviouslyCompletedActions.Count - 1; return null; } + public void PauseExploring(int segmentNumber) + { + if (_inProgressAction != null) + { + _inProgressAction.AbortAction(segmentNumber); + _inProgressAction = null; + } + if (ExplorationState == ExplorationState.EXPLORING) + { + RGDebug.LogInfo("ActionExplorationDriver - Paused Exploratory Actions"); + } + + ExplorationState = ExplorationState.PAUSED; + } + public void StopExploring(int segmentNumber) { if (_inProgressAction != null) @@ -175,11 +187,12 @@ public void StopExploring(int segmentNumber) _inProgressAction.AbortAction(segmentNumber); _inProgressAction = null; } - if (IsExploring) + if (ExplorationState == ExplorationState.EXPLORING) { RGDebug.LogInfo("ActionExplorationDriver - Stopped Exploratory Actions"); } - IsExploring = false; + + ExplorationState = ExplorationState.STOPPED; } } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs index 377c93e8..1d01002d 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs @@ -104,6 +104,7 @@ private void EvaluateBotSegments() if (nextBotSegmentIndex == 0) { BotSegment firstActionSegment = _nextBotSegments[0]; + string logPrefix = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - "; try { if (firstActionSegment.botAction?.IsCompleted == false) @@ -113,9 +114,9 @@ private void EvaluateBotSegments() if (error == null) { - _explorationDriver.StopExploring(firstActionSegment.Replay_SegmentNumber); - - if (didAction) + // we're going to 'pause' exploring, but not reset the exploration state quite yet until this action fully finishes + _explorationDriver.PauseExploring(firstActionSegment.Replay_SegmentNumber); + if (didAction && _explorationDriver.ExplorationState == ExplorationState.STOPPED) { // for every non error action, reset the timer _lastTimeLoggedKeyFrameConditions = now; @@ -132,16 +133,18 @@ private void EvaluateBotSegments() if (firstActionSegment.botAction?.data is IKeyMomentExploration) { - loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\n" + error + "\nRunning exploratory actions..."; + loggedMessage = $"Error processing BotAction\n\n" + error + "\n\n\nRunning exploratory actions..."; } else { - loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Error processing BotAction\n" + error; + loggedMessage = $"Error processing BotAction\n\n" + error; } // wait the action warning interval before starting to explore actions if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) { + // log this before we start exploring so we can see why it started exploring + LogPlaybackWarning(logPrefix + loggedMessage); if (firstActionSegment.botAction?.data is IKeyMomentExploration) { _explorationDriver.StartExploring(); @@ -158,26 +161,32 @@ private void EvaluateBotSegments() if (explorationError != null) { - loggedMessage += $"\n\nError processing exploratory BotAction\n" + explorationError; + // put this on top to minimize screen flicker + loggedMessage = $"Error processing exploratory BotAction\n\n" + explorationError +"\n\n\nPre-Exploration Error - " + loggedMessage; + LogPlaybackWarning(logPrefix + loggedMessage); } - - if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) + else if (_explorationDriver.ExplorationState != ExplorationState.STOPPED || _lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) { - LogPlaybackWarning(loggedMessage); + LogPlaybackWarning(logPrefix + loggedMessage); } } } if (firstActionSegment.botAction?.IsCompleted == true && firstActionSegment.botAction?.data is IKeyMomentExploration) { + // for every non error action, reset the timer + _lastTimeLoggedKeyFrameConditions = now; + FindObjectOfType()?.SetKeyFrameWarningText(null); + + _explorationDriver.StopExploring(firstActionSegment.Replay_SegmentNumber); _explorationDriver.ReportPreviouslyCompletedAction(firstActionSegment.botAction.data); } } catch (Exception ex) { - var loggedMessage = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - Exception processing BotAction\n" + ex.Message; - LogPlaybackWarning(loggedMessage, ex); + var loggedMessage = "Exception processing BotAction\n\n" + ex.Message; + LogPlaybackWarning(logPrefix + loggedMessage, ex); // uncaught exception... stop and unload the segment UnloadSegmentsAndReset(); throw; @@ -681,15 +690,19 @@ private void LogPlaybackWarning(string loggedMessage, Exception ex = null) { var now = Time.unscaledTime; _lastTimeLoggedKeyFrameConditions = now; - if (ex != null) - { - RGDebug.LogException(ex, loggedMessage); - } - else + // avoid spamming the log + if (loggedMessage != _lastSegmentPlaybackWarning) { - RGDebug.LogWarning(loggedMessage); + _lastSegmentPlaybackWarning = loggedMessage; + if (ex != null) + { + RGDebug.LogException(ex, loggedMessage); + } + else + { + RGDebug.LogWarning(loggedMessage); + } } - _lastSegmentPlaybackWarning = loggedMessage; FindObjectOfType()?.SetKeyFrameWarningText(loggedMessage); if (pauseEditorOnPlaybackWarning) { diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 67921240..c2450657 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -179,6 +179,18 @@ public bool ProcessAction(int segmentNumber, Dictionary curr if (!_isStopped) { + if (mouseActions.Count == 2 && mouseActions[^1].clickedObjectNormalizedPaths.Length < 1) + { + // we had an un-click only segment on something without any paths.. basically an un-click on nothing + // this can happen when there is a random click/un-click on nothing in the game or on something that is excluded from RG seeing it in the state + // while this 'should' be not recorded in the first place, we cover it here also + + // we just say this worked and is done + _isStopped = true; + error = null; + return true; + } + if (_unClickAction != null) { // handling [2] index.. the un-click @@ -189,45 +201,101 @@ public bool ProcessAction(int segmentNumber, Dictionary curr // find the best object and un-click on it if possible var unClickPaths = _preconditionNormalizedPaths[2]; var unClickPreconditionMatches = BuildPreconditions(segmentNumber, currentTransforms, currentEntities, unClickPaths); - var bestUnClickMatchResult = FindBestMatch(_unClickAction, unClickPreconditionMatches, out _); - if (bestUnClickMatchResult.HasValue) + + ObjectStatus bestObjectStatus = null; + Vector2 clickPosition; + if (unClickPreconditionMatches.Length > 0) { - var bestObjectStatus = bestUnClickMatchResult.Value.Item1; + var bestUnClickMatchResult = FindBestMatch(_unClickAction, unClickPreconditionMatches, out _); - var clickPosition = GetClickPositionForMatch(bestUnClickMatchResult.Value, out var usingOriginalClickPosition ); + if (bestUnClickMatchResult.HasValue) + { + bestObjectStatus = bestUnClickMatchResult.Value.Item1; - // only send the bestObjectStatus for filtering when NOT un-clicking at the original position - if (!DoActionForObjectAtPosition(segmentNumber, _unClickAction, usingOriginalClickPosition?null:bestObjectStatus, clickPosition, _unClickAction.clickedObjectNormalizedPaths[0], currentTransforms, out error)) + clickPosition = GetClickPositionForMatch(bestUnClickMatchResult.Value); + + // if our best match path isn't the same as the click path.. do some extra evaluation + if (bestObjectStatus.NormalizedPath != mouseActions[1].clickedObjectNormalizedPaths[0]) + { + // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not + if (_lastClickPosition.HasValue) + { + if (_lastClickPosition.Value.x >= bestUnClickMatchResult.Value.Item3.Item1 + && _lastClickPosition.Value.x <= bestUnClickMatchResult.Value.Item3.Item3 + && _lastClickPosition.Value.y >= bestUnClickMatchResult.Value.Item3.Item2 + && _lastClickPosition.Value.y <= bestUnClickMatchResult.Value.Item3.Item4) + { + RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on bounds overlaps to original path: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); + // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the + // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame + clickPosition = _lastClickPosition.Value; + bestObjectStatus = null; + } + else if (_previousOverlappingObjects.Count > 0) + { + // this can handle things where the un-click was recorded 'after' the element dis-appeared, but it is still there in the replay until the un-click happens... this is just a matter of how ui events and when we can observe during recording works + + // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) + // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button + // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen + // isn't important.. we want to un-click exactly where we clicked + if (_previousOverlappingObjects.Any(a => a == bestUnClickMatchResult.Value.Item1)) + { + RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on object path existing at time of click: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); + clickPosition = _lastClickPosition.Value; + bestObjectStatus = null; + } + } + } + } + } + else { + // didn't find it.. this is where 'exploration' is going to start happening based on our result + error = $"No valid mouse un-click action object found for path:\n{unClickPaths[0].normalizedPath}"; return false; } - - _unClickAction = null; - _isDoneWaitOneFrame = true; - - error = null; - return true; } else { - // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse un-click action object found for path:\n{unClickPaths[0].normalizedPath}"; + // there were no object paths on the un-click. assume it is at the same position as the click + if (_lastClickPosition.HasValue) + { + clickPosition = _lastClickPosition.Value; + } + else + { + error = $"No valid mouse un-click position... This is a code bug in Regression Games and should NOT happen"; + return false; + } + } + + // only send the bestObjectStatus for filtering when NOT un-clicking at the original position + var targetObjectPath = _unClickAction.clickedObjectNormalizedPaths.Length > 0 ? _unClickAction.clickedObjectNormalizedPaths[0] : null; + if (!DoActionForObjectAtPosition(segmentNumber, _unClickAction, bestObjectStatus, clickPosition, targetObjectPath, currentTransforms, out error)) + { return false; } + _unClickAction = null; + _isDoneWaitOneFrame = true; + + error = null; + return true; + } else { + // need to return null error so the bot doesn't start exploring.. it needs to think this action is processing as intended while we wait error = null; - // need to return true so the bot doesn't start exploring.. it needs to think this action is processing as intended while we wait - return true; + return false; } } else { - // handling [0] , [1] indexes.. the positioning and the clic - //these have the same position.. so we can compute it just once - + // handling [0] , [1] indexes.. the positioning and the click[length=3] or un-click action[length=2] + // these have the same position.. so we can compute it just once + // we have to be aware that you can get a segment with only a position and an un-click (length 2) .. so we have to be careful there var movementAction = mouseActions[0]; var clickAction = mouseActions[1]; @@ -239,31 +307,9 @@ public bool ProcessAction(int segmentNumber, Dictionary curr { var bestObjectStatus = bestMatchResult.Value.Item1; - var isInteractable = true; - // now that we have the 'best' match ... first let's make sure it's ready to be clicked - // visible (already true by the time we get here)/active-enabled(we already know that from a canvas perspective this is visible, but need to check UI component info) - if (bestObjectStatus is TransformStatus tStatus) - { - var theTransform = tStatus.Transform; - if (theTransform is RectTransform) - { - // ui object - var selectables = theTransform.GetComponents(); - // make sure 1 is interactable - isInteractable = selectables.Any(a => a.interactable); - } - } - - if (!isInteractable) - { - // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse action object found for path:\n{clickPaths[0].normalizedPath}"; - return false; - } + var clickPosition = GetClickPositionForMatch(bestMatchResult.Value); - var clickPosition = GetClickPositionForMatch(bestMatchResult.Value, out _); - // pass null for bestObjectStatus to avoid click object validation if (!DoActionForObjectAtPosition(segmentNumber, movementAction, bestObjectStatus, clickPosition,clickAction.clickedObjectNormalizedPaths[0], currentTransforms, out error)) { return false; @@ -277,15 +323,27 @@ public bool ProcessAction(int segmentNumber, Dictionary curr _previousOverlappingObjects = overlappingObjects; // on the 'click'.. save the position _lastClickPosition = clickPosition; - _unClickAction = mouseActions[2]; - // save off the time the unClick should happen here so mouse button holds work - _unClickTime = clickAction.startTime == 0d ? 0d : Time.unscaledTimeAsDouble + _unClickAction.startTime - clickAction.startTime; + if (mouseActions.Count > 2) + { + _unClickAction = mouseActions[2]; + // save off the time the unClick should happen here so mouse button holds work + _unClickTime = clickAction.startTime == 0d ? 0d : Time.unscaledTimeAsDouble + _unClickAction.startTime - clickAction.startTime; + } + else + { + // this was just a position and un-click segment.. we're done + _unClickAction = null; + _unClickTime = 0d; + _isDoneWaitOneFrame = true; + } + + error = null; return true; } else { - error = $"No valid mouse action object found for paths:\n{string.Join("\n", clickPaths.Select(a => a.normalizedPath))}"; + error = $"No valid mouse action object found for path:\n{clickPaths[0].normalizedPath}"; return false; } } @@ -295,9 +353,8 @@ public bool ProcessAction(int segmentNumber, Dictionary curr return false; } - private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float,float)) bestMatchResult, out bool usingOriginalClickPosition) + private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float,float)) bestMatchResult) { - usingOriginalClickPosition = false; // we started with clicking the center.. but this was limiting //clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); // instead... make the click-position a random position within the bounds @@ -306,40 +363,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, // on the next attempt, it picks a new position to try thus giving us a better chance of passing // TODO (REG-2223): Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? // This would allow us to know that we clicked about X far into this floor tile and replicate that positioning regardless of the actual worldspace positioning in the replay... - // +1 because int range is max exclusive.. if these were floats.. remove the +1 - Vector2 result = new Vector2(Random.Range(bestMatchResult.Item3.Item1, bestMatchResult.Item3.Item3 + 1), Random.Range(bestMatchResult.Item3.Item2, bestMatchResult.Item3.Item4 + 1)); - - // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not - // ReSharper disable once PossibleInvalidOperationException - already filtered at the top of the method to only have entries with valid visible bounds - if (_lastClickPosition.HasValue) - { - if (_lastClickPosition.Value.x >= bestMatchResult.Item3.Item1 - && _lastClickPosition.Value.x <= bestMatchResult.Item3.Item3 - && _lastClickPosition.Value.y >= bestMatchResult.Item3.Item2 - && _lastClickPosition.Value.y <= bestMatchResult.Item3.Item4) - { - RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on bounds overlaps to original path: {bestMatchResult.Item1.NormalizedPath}"); - // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the - // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame - result = _lastClickPosition.Value; - usingOriginalClickPosition = true; - } - else if (_previousOverlappingObjects.Count > 0) - { - // this can handle things where the unclick was recorded 'after' the element dis-appeared, but it is still there in the replay until the unclick happens... this is just a matter of how ui events and when we can observe during recording works - - // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) - // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button - // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen - // isn't important.. we want to un-click exactly where we clicked - if (_previousOverlappingObjects.Any(a => a == bestMatchResult.Item1)) - { - RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on object path existing at time of click: {bestMatchResult.Item1.NormalizedPath}"); - result = _lastClickPosition.Value; - usingOriginalClickPosition = true; - } - } - } + Vector2 result = new Vector2(Random.Range(bestMatchResult.Item3.Item1, bestMatchResult.Item3.Item3), Random.Range(bestMatchResult.Item3.Item2, bestMatchResult.Item3.Item4)); return result; } @@ -352,86 +376,92 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, { var possibleTransformsToClick = currentTransforms.Values.Where(a => a.screenSpaceBounds.HasValue).ToList(); var preconditionsLength = preConditionNormalizedPaths.Count; - var preconditionMatches = new List<(ObjectStatus, (int[],int[]))>[preconditionsLength]; - for (var i = 0; i < preconditionsLength; i++) - { - preconditionMatches[i] = new List<(ObjectStatus, (int[], int[]))>(); - } - - foreach (var possibleTransformToClick in possibleTransformsToClick) + List<(ObjectStatus, (int[], int[]))>[] preconditionMatches = Array.Empty>(); + if (preconditionsLength > 0) { - possibleTransformToClick.TokenizedObjectPath ??= PreconditionNormalizedPathData.TokenizeObjectPath(possibleTransformToClick.NormalizedPath); + preconditionMatches = new List<(ObjectStatus, (int[], int[]))>[preconditionsLength]; + for (var i = 0; i < preconditionsLength; i++) + { + preconditionMatches[i] = new List<(ObjectStatus, (int[], int[]))>(); + } - var breakoutCount = 0;// optimization for when we've found exact matches for all preconditions - // this should nearly always be smaller than the # of possibleTransformToClick - for (var j = 0; j < preconditionsLength; j++) + foreach (var possibleTransformToClick in possibleTransformsToClick) { - // optimization - only keep processing this precondition when we didn't have an exact match for it already - if (preconditionMatches[j].Count == 0 || preconditionMatches[j][0].Item2.Item1 != null) - { - var precondition = preConditionNormalizedPaths[j]; + possibleTransformToClick.TokenizedObjectPath ??= PreconditionNormalizedPathData.TokenizeObjectPath(possibleTransformToClick.NormalizedPath); - // prefer normalized path match, then tokenized path matching logic - if (precondition.normalizedPath == possibleTransformToClick.NormalizedPath) - { - // normalized matching logic - if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 != null) - { - // wipe out all the tokenized matches if we get an exact - preconditionMatches[j].Clear(); - } - preconditionMatches[j].Add((possibleTransformToClick, (null, null))); - break; // the for - } - else + var breakoutCount = 0; // optimization for when we've found exact matches for all preconditions + // this should nearly always be smaller than the # of possibleTransformToClick + for (var j = 0; j < preconditionsLength; j++) + { + // optimization - only keep processing this precondition when we didn't have an exact match for it already + if (preconditionMatches[j].Count == 0 || preconditionMatches[j][0].Item2.Item1 != null) { - // tokenized matching logic - if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 == null) + var precondition = preConditionNormalizedPaths[j]; + + // prefer normalized path match, then tokenized path matching logic + if (precondition.normalizedPath == possibleTransformToClick.NormalizedPath) { - // we already have exact matches.. ignore the tokenized ones + // normalized matching logic + if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 != null) + { + // wipe out all the tokenized matches if we get an exact + preconditionMatches[j].Clear(); + } + + preconditionMatches[j].Add((possibleTransformToClick, (null, null))); break; // the for } - var tokenMatches = EvaluateTokenMatches(precondition.tokenData, possibleTransformToClick.TokenizedObjectPath); - if (tokenMatches.Item1 != null) + else { - // got something of the same path length with some token matches in each part - if (preconditionMatches[j].Count > 0) + // tokenized matching logic + if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 == null) + { + // we already have exact matches.. ignore the tokenized ones + break; // the for + } + + var tokenMatches = EvaluateTokenMatches(precondition.tokenData, possibleTransformToClick.TokenizedObjectPath); + if (tokenMatches.Item1 != null) { - var isBetter = AreNewTokenMatchesBetter(preconditionMatches[j][0].Item2, tokenMatches); - // compare - if (true == isBetter) + // got something of the same path length with some token matches in each part + if (preconditionMatches[j].Count > 0) { - // better match.. clear the list - preconditionMatches[j].Clear(); - preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); + var isBetter = AreNewTokenMatchesBetter(preconditionMatches[j][0].Item2, tokenMatches); + // compare + if (true == isBetter) + { + // better match.. clear the list + preconditionMatches[j].Clear(); + preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); + } + else if (isBetter == null) + { + // equal match.. add to the list + preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); + } + // else worse match.. leave it alone } - else if (isBetter == null) + else { - // equal match.. add to the list + //set the first match preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); } - // else worse match.. leave it alone - } - else - { - //set the first match - preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); - } - break; // the for + break; // the for + } } } + else + { + ++breakoutCount; + } } - else + + if (breakoutCount == preconditionsLength) { - ++breakoutCount; + break; // the foreach - we found exact matches for everything } } - - if (breakoutCount == preconditionsLength) - { - break; // the foreach - we found exact matches for everything - } } return preconditionMatches; @@ -532,7 +562,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, overlappingObjects = new List(); // now that we have all the precondition matches mapped out, let's see if we have the leftmost object.. - if (preconditionMatches[0].Count > 0) + if (preconditionMatches.Length > 0 && preconditionMatches[0].Count > 0) { // yay.. we found something(s).. figure out which one of them is the 'best' based on number of overlapping matches @@ -540,6 +570,9 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, // we will also compute the 'smallest' click bounds for these overlaps as we go var matchResults = new List<(ObjectStatus, int, (float, float, float, float))>(preconditionMatches[0].Count); + var screenWidth = Screen.width; + var screenHeight = Screen.height; + foreach (var preconditionMatch0 in preconditionMatches[0]) { var isPreconditionMatch0Interactable = true; @@ -552,8 +585,11 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, { // ui object var selectables = theTransform.GetComponents(); - // make sure 1 is interactable - isPreconditionMatch0Interactable = selectables.Any(a => a.interactable); + if (selectables.Length > 0) + { + // make sure 1 is interactable .. otherwise leave isPreconditionMatch0Interactable == true + isPreconditionMatch0Interactable = selectables.Any(a => a.interactable); + } } } @@ -562,10 +598,11 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, // ReSharper disable once PossibleInvalidOperationException - already filtered in BuildPreconditions to only have entries with valid visible bounds var smallestBounds = preconditionMatch0.Item1.screenSpaceBounds.Value; // we tried doing this with int math.. but pixel fluctuations of objects with fractional render bounds are a thing (think shimmering/flicker along aliased edges in games) - var minX = smallestBounds.min.x; - var minY = smallestBounds.min.y; - var maxX = smallestBounds.max.x; - var maxY = smallestBounds.max.y; + // limit to the screen space.. some visible things hang off the screen and we don't want to click off the screen + var minX = Mathf.Max(smallestBounds.min.x, 0f); + var minY = Mathf.Max(smallestBounds.min.y, 0f); + var maxX = Mathf.Min(smallestBounds.max.x, screenWidth); + var maxY = Mathf.Min(smallestBounds.max.y, screenHeight); var originalMinX = minX; var originalMinY = minY; @@ -594,8 +631,11 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, { // ui object var selectables = theTransform.GetComponents(); - // make sure 1 is interactable - isInteractable = selectables.Any(a => a.interactable); + if (selectables.Length > 0) + { + // make sure 1 is interactable - if none leave isInteratable == true + isInteractable = selectables.Any(a => a.interactable); + } } } if (isInteractable) @@ -722,13 +762,13 @@ private bool DoActionForObjectAtPosition(int segmentNumber, MouseInputActionData { if (!targetObjectPath.StartsWith(objectsAtClickPosition[0].NormalizedPath)) { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{targetObjectPath}\nanother object is obstructing with path:\n{objectsAtClickPosition[0].NormalizedPath}"; + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\n\non object path:\n{targetObjectPath}\n\nanother object is obstructing with path:\n{objectsAtClickPosition[0].NormalizedPath}"; return false; } } else { - error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\non object path:\n{targetObjectPath}\nno objects at that position"; + error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\n\non object path:\n{targetObjectPath}\n\nno objects at that position"; return false; } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs index 00c2328f..82abcdfb 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs @@ -19,75 +19,63 @@ public class KeyMomentEvaluator public void Reset() { - _keyMomentNumber = 0; + _keyMomentNumber = 1; } [CanBeNull] public BotSegment EvaluateKeyMoment(long tickNumber, out long keyMomentNumber) { - var meaningfulInputs = MouseDataBuffer.Select((data, i) => (i,data)).Where(tuple => tuple.data.clickedObjectNormalizedPaths.Length > 0).ToList(); - if (meaningfulInputs.Count > 0) + // build out the list of clicks and un-clicks up to the last un-click + List<(int, MouseInputActionData)> meaningfulInputs = new(); + MouseInputActionData priorMouseData = _previousKeyMomentMouseInputState; + var mouseDataBufferCount = MouseDataBuffer.Count; + var foundUnClick = false; + for (var i = 0; !foundUnClick && i < mouseDataBufferCount; i++) { - List inputsToProcess = new(); - // only take from the buffer up to the last 'un-clicked' state - // this is 'hard'... we want to encapsulate click down/up as a single action together - // but in cases like FPS where I might 'hold' right click to ADS then repeatedly click/release the left mouse button to fire.. i don't want that ALL as one action - // each shot of the weapon would be an 'action', AND .. ads was an action.. so we break this up by any button release - - // find the index range we want - var firstInput = meaningfulInputs[0]; - - if (firstInput.data.IsButtonUnClick(_previousKeyMomentMouseInputState)) + var mouseData = MouseDataBuffer[i]; + var isUnClick = priorMouseData != null && mouseData.IsButtonUnClick(priorMouseData); + if (mouseData.clickedObjectNormalizedPaths.Length > 0 || isUnClick) { - inputsToProcess.Add(firstInput.data); - // clean out all the entries up to here - MouseDataBuffer.RemoveRange(0, firstInput.i+1); + meaningfulInputs.Add((i,mouseData)); } - else if (meaningfulInputs.Count > 1) + if (isUnClick) { - var priorData = _previousKeyMomentMouseInputState; - var foundUnClick = false; - // go through all the inputs until we find an un-click - // we speculatively update the list as we go, but if we never hit an un-click we wipe it back out before moving on - foreach (var meaningfulInput in meaningfulInputs) - { - if (meaningfulInput.i - 1 >= 0) - { - var previousInput = MouseDataBuffer[meaningfulInput.i - 1]; + foundUnClick = true; + // we found an un-click, we're done + } - // this may seem odd, but we use this API backwards so that we see if the previous mouse spot before the click had a different click state than the current one.. it 'should' ,but better to be safe - // ultimately.. we're trying to add the mouse position event before the click so that the mouse is 'in position' before clicking down to avoid any snafu's with the input system - if (previousInput.IsButtonUnClick(meaningfulInput.data)) - { - inputsToProcess.Add(previousInput); - } - } + priorMouseData = mouseData; + } - inputsToProcess.Add(meaningfulInput.data); - if (meaningfulInput.data.IsButtonUnClick(priorData)) - { - // clean out all the entries up to here - MouseDataBuffer.RemoveRange(0, meaningfulInput.i+1); - foundUnClick = true; - break; - } + if (foundUnClick) + { + try + { + // this should always be either length 1 - only an un-click, or length 2 - click/un-click pair - priorData = meaningfulInput.data; - } + // only take from the buffer up to the last 'un-clicked' state + // this is 'hard'... we want to encapsulate click down/up as a single action together + // but in cases like FPS where I might 'hold' right click to ADS then repeatedly click/release the left mouse button to fire.. i don't want that ALL as one action + // each shot of the weapon would be an 'action', AND .. ads was an action.. so we break this up by any button release + + var inputsToProcess = new List(); - if (!foundUnClick) + // add a 'positional' prior state before the click/un-click + var priorIndex = meaningfulInputs[0].Item1 - 1; + if (priorIndex >= 0) { - // never found an un-click.. don't process the list so far - inputsToProcess.Clear(); + inputsToProcess.Add(MouseDataBuffer[priorIndex]); } - } - if (inputsToProcess.Count > 0) - { - // update the last state based on the end entry in our list - _previousKeyMomentMouseInputState = inputsToProcess[^1]; + // if meaningfulInputs.Count == 1, then we just had an un-click and that's it... that would only ever happen if you had many buttons held down, and release 1 button 1 frame and another button another frame + if (meaningfulInputs.Count > 1) + { + // add the click + inputsToProcess.Add(meaningfulInputs[0].Item2); + } - ++_keyMomentNumber; + // add the un-click + inputsToProcess.Add(meaningfulInputs[^1].Item2); var botSegment = new BotSegment { @@ -112,8 +100,30 @@ public BotSegment EvaluateKeyMoment(long tickNumber, out long keyMomentNumber) }; keyMomentNumber = _keyMomentNumber; + + if (inputsToProcess.Count == 2 && inputsToProcess[^1].clickedObjectNormalizedPaths.Length < 1) + { + // we had an un-click only segment on something without any paths.. basically an un-click on nothing + // this can happen when there is a random click/un-click on nothing in the game or on something that is excluded from RG seeing it in the state + + // we should NOT record this segment + keyMomentNumber = _keyMomentNumber; + return null; + } + + + // update the last state based on the end entry in our list + _previousKeyMomentMouseInputState = meaningfulInputs[^1].Item2; + + ++_keyMomentNumber; + return botSegment; } + finally + { + // remove everything up through the un-click + MouseDataBuffer.RemoveRange(0, meaningfulInputs[^1].Item1 + 1); + } } keyMomentNumber = _keyMomentNumber; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index 3be73f54..3c6bd777 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -173,9 +173,9 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer if (ray.HasValue) { - // do a raycast all into the world and see what hits... we want to be able to sort things with colliders above those that don't have one - var raycastHits = Physics.RaycastAll(ray.Value); - worldSpaceHits = raycastHits.ToDictionary(a => a.transform.GetInstanceID(), a => a); + // do a ray-cast all into the world and see what hits... we want to be able to sort things with colliders above those that don't have one + var rayCastHits = Physics.RaycastAll(ray.Value); + worldSpaceHits = rayCastHits.ToDictionary(a => a.transform.GetInstanceID(), a => a); } foreach (var recordedGameObjectState in statefulObjects) @@ -196,8 +196,12 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer { // ui object var selectables = theTransform.GetComponents(); - // make sure 1 is interactable - isInteractable = selectables.Any(a => a.interactable); + if (selectables.Length > 0) + { + // make sure 1 is interactable + isInteractable = selectables.Any(a => a.interactable); + } + // else .. wasn't selectable, but certainly still obstructs what we're clicking on so leave isInteractable = true } } @@ -205,10 +209,10 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer { if (recordedGameObjectState.worldSpaceBounds.HasValue) { + var didHit = false; if (ray.HasValue) { // compute the zOffset at this point based on the ray - var didHit = false; if (recordedGameObjectState is TransformStatus transformStatus) { var collider = transformStatus.Transform.GetComponentInParent(); @@ -222,43 +226,31 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer if (hitDistance >= 0f) { var hitPoint = ray.Value.GetPoint(hitDistance); - // make sure that even though our parent had this collider, that the hit is still within the bounding volume of the object we're checking - // we wouldn't want a parent collider to cause ALL of its children to be included as clicked, just the one ray pass of renderers we actually hit - if (recordedGameObjectState.worldSpaceBounds.Value.Contains(hitPoint)) - { - didHit = true; - recordedGameObjectState.zOffsetForMousePoint = hitDistance; - recordedGameObjectState.worldSpaceCoordinatesForMousePoint = hitPoint; - } + didHit = true; + recordedGameObjectState.zOffsetForMousePoint = hitDistance; + recordedGameObjectState.worldSpaceCoordinatesForMousePoint = hitPoint; + result.Add(recordedGameObjectState); } } } } - if(!didHit) - { - // wasn't in front of the camera - recordedGameObjectState.zOffsetForMousePoint = -1f; - recordedGameObjectState.worldSpaceCoordinatesForMousePoint = null; - } + } - else + if(!didHit) { - // go with the zOffset for the bounding box itself instead of at this exact point... this leads to really weird cases in world space objects with other nearby objects and should be avoided - // example.. a stone you click sitting on the top back corner of a cube will be 'behind' the cube because the front plane of the cube is closer, just not at that screen point - // leaving this in here for now though as some sorting is still better than no sorting if we get here (have no camera) - recordedGameObjectState.zOffsetForMousePoint = recordedGameObjectState.screenSpaceZOffset; - recordedGameObjectState.worldSpaceCoordinatesForMousePoint = recordedGameObjectState.worldSpaceBounds.Value.center; + // wasn't in front of the camera + recordedGameObjectState.zOffsetForMousePoint = -1f; + recordedGameObjectState.worldSpaceCoordinatesForMousePoint = null; } } else if (recordedGameObjectState.screenSpaceZOffset >= 0f) { // screen space object - recordedGameObjectState.zOffsetForMousePoint = recordedGameObjectState.screenSpaceZOffset; + recordedGameObjectState.zOffsetForMousePoint = 0f; recordedGameObjectState.worldSpaceCoordinatesForMousePoint = null; + result.Add(recordedGameObjectState); } - result.Add(recordedGameObjectState); } - } } } @@ -273,7 +265,8 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer }; EventSystem.current.RaycastAll(pointerEventData, eventSystemHits); - var uiHitMapping = eventSystemHits.ToDictionary(a => (long)a.gameObject.transform.GetInstanceID(), a => a); + // store their ordering value.. RaycastAll is already sorted in the correct order for us by Unity.. don't mess with their ordering sort or you will get incorrect obstructions :/ + var uiHitMapping = eventSystemHits.Select((a,i) => (a,i)).ToDictionary(ai => (long)ai.a.gameObject.transform.GetInstanceID(), ai => ai.i); // sort with lower z-offsets first so we have UI things on top of game object things // if both elements are from the UI.. then sort by the event system ray hits @@ -282,46 +275,36 @@ public static List FindObjectsAtPosition(Vector2 position, IEnumer { if (a.worldSpaceBounds == null && b.worldSpaceBounds == null) { - RaycastResult? aHitData = null; + int aHitOrder = -1; if (a is TransformStatus at) { if (uiHitMapping.TryGetValue(at.Id, out var value)) { - aHitData = value; + aHitOrder = value; } } - RaycastResult? bHitData = null; + + int bHitOrder = -1; if (b is TransformStatus bt) { if (uiHitMapping.TryGetValue(bt.Id, out var value)) { - bHitData = value; + bHitOrder = value; } } - if (aHitData.HasValue) + if (aHitOrder >= 0) { - if (bHitData.HasValue) + if (bHitOrder >= 0) { - if (aHitData.Value.distance < bHitData.Value.distance) - { - return -1; - } - - if (aHitData.Value.distance > bHitData.Value.distance) - { - return 1; - } - - // higher depth for ui hits == closer to camera.. don't even get me started - return bHitData.Value.depth - aHitData.Value.depth; + return aHitOrder - bHitOrder; } // aDidHit.. but not b, put a in front return -1; } - if (bHitData.HasValue) + if (bHitOrder >= 0) { //bDidHit.. but not a, put b in front return 1; From eef18c3f1f9e297635b024c4217bba04cb3410a6 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Wed, 11 Dec 2024 13:54:37 -0500 Subject: [PATCH 20/31] Documentation and refactor how evaluators are added in --- .../BotSegments/ActionExplorationDriver.cs | 9 + .../BotSegmentsPlaybackController.cs | 236 +++++++++++------- .../BotActions/KeyMomentMouseActionData.cs | 2 +- .../StateRecorder/KeyMomentEvaluator.cs | 132 +++------- .../Scripts/StateRecorder/KeyMoments.meta | 3 + .../KeyMoments/IKeyMomentEvaluator.cs | 12 + .../KeyMoments/IKeyMomentEvaluator.cs.meta | 3 + .../KeyMoments/MouseKeyMomentEvaluator.cs | 116 +++++++++ .../MouseKeyMomentEvaluator.cs.meta | 3 + .../Models/MouseInputActionData.cs | 42 +--- .../StateRecorder/MouseInputActionObserver.cs | 12 +- .../Scripts/StateRecorder/ScreenRecorder.cs | 2 +- 12 files changed, 341 insertions(+), 231 deletions(-) create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments.meta create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/IKeyMomentEvaluator.cs create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/IKeyMomentEvaluator.cs.meta create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs create mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs.meta diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index d9620378..75c374dc 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -15,6 +15,10 @@ public enum ExplorationState PAUSED, EXPLORING } + + /** + * This class controls action exploration during bot runs. See BotSegmentsPlaybackController.ProcessBotSegmentAction for further information. + */ public class ActionExplorationDriver : MonoBehaviour { private const int PRIOR_ACTION_LIMIT = 5; @@ -73,6 +77,11 @@ public void StartExploring() private IBotActionData _inProgressAction = null; + /** + * Chooses the next exploratory action to perform. + * + * The current implementation chooses one of the previous N successful IKeyMomentExploration actions to try on each update pass. + */ public void PerformExploratoryAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) { error = null; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs index 1d01002d..d5f2fd11 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs @@ -60,7 +60,7 @@ public class BotSegmentsPlaybackController : MonoBehaviour private ActionExplorationDriver _explorationDriver; - private void EvaluateBotSegments() + private void ProcessBotSegments() { var now = Time.unscaledTime; @@ -100,101 +100,14 @@ private void EvaluateBotSegments() var nextBotSegmentIndex = 0; while (nextBotSegmentIndex < _nextBotSegments.Count) { + var nextBotSegment = _nextBotSegments[nextBotSegmentIndex]; + // if we're working on the first entry in the list is the only time we do actions if (nextBotSegmentIndex == 0) { - BotSegment firstActionSegment = _nextBotSegments[0]; - string logPrefix = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - "; - try - { - if (firstActionSegment.botAction?.IsCompleted == false) - { - // allow the main action to retry between every exploratory action - var didAction = firstActionSegment.ProcessAction(transformStatuses, entityStatuses, out var error); - - if (error == null) - { - // we're going to 'pause' exploring, but not reset the exploration state quite yet until this action fully finishes - _explorationDriver.PauseExploring(firstActionSegment.Replay_SegmentNumber); - if (didAction && _explorationDriver.ExplorationState == ExplorationState.STOPPED) - { - // for every non error action, reset the timer - _lastTimeLoggedKeyFrameConditions = now; - FindObjectOfType()?.SetKeyFrameWarningText(null); - } - } - - if (error != null) - { - // arranges this to build up a status message with real action + exploratory status - // TODO: (REG-2213) Update all this to use a status manager to manage reporting - - string loggedMessage; - - if (firstActionSegment.botAction?.data is IKeyMomentExploration) - { - loggedMessage = $"Error processing BotAction\n\n" + error + "\n\n\nRunning exploratory actions..."; - } - else - { - loggedMessage = $"Error processing BotAction\n\n" + error; - } - - // wait the action warning interval before starting to explore actions - if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) - { - // log this before we start exploring so we can see why it started exploring - LogPlaybackWarning(logPrefix + loggedMessage); - if (firstActionSegment.botAction?.data is IKeyMomentExploration) - { - _explorationDriver.StartExploring(); - } - } - - string explorationError = null; - if (firstActionSegment.botAction?.data is IKeyMomentExploration keyMomentExploration) - { - _explorationDriver.PerformExploratoryAction(firstActionSegment.Replay_SegmentNumber, transformStatuses, entityStatuses, out explorationError); - // we just interfered mid action.. reset this thing to try again - keyMomentExploration.KeyMomentExplorationReset(); - } - - if (explorationError != null) - { - // put this on top to minimize screen flicker - loggedMessage = $"Error processing exploratory BotAction\n\n" + explorationError +"\n\n\nPre-Exploration Error - " + loggedMessage; - LogPlaybackWarning(logPrefix + loggedMessage); - } - else if (_explorationDriver.ExplorationState != ExplorationState.STOPPED || _lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) - { - LogPlaybackWarning(logPrefix + loggedMessage); - } - } - } - - if (firstActionSegment.botAction?.IsCompleted == true && firstActionSegment.botAction?.data is IKeyMomentExploration) - { - // for every non error action, reset the timer - _lastTimeLoggedKeyFrameConditions = now; - FindObjectOfType()?.SetKeyFrameWarningText(null); - - _explorationDriver.StopExploring(firstActionSegment.Replay_SegmentNumber); - _explorationDriver.ReportPreviouslyCompletedAction(firstActionSegment.botAction.data); - } - - } - catch (Exception ex) - { - var loggedMessage = "Exception processing BotAction\n\n" + ex.Message; - LogPlaybackWarning(logPrefix + loggedMessage, ex); - // uncaught exception... stop and unload the segment - UnloadSegmentsAndReset(); - throw; - } + ProcessBotSegmentAction(nextBotSegment, transformStatuses, entityStatuses); } - var nextBotSegment = _nextBotSegments[nextBotSegmentIndex]; - var matched = nextBotSegment.Replay_Matched || nextBotSegment.endCriteria == null || nextBotSegment.endCriteria.Count == 0 || KeyFrameEvaluator.Evaluator.Matched( nextBotSegmentIndex == 0, nextBotSegment.Replay_SegmentNumber, @@ -315,13 +228,152 @@ private void EvaluateBotSegments() } } + /** + * Handles processing the action for the bot segment if it has one. + * + * During the processing of the action, if the action returns an error, the system will wait ACTION_WARNING_INTERVAL before reporting the error. + * In this error case, if the Bot action data implements IKeyMomentExploration, then it supports 'exploration'. Exploration is used to try to find a way + * past the error using the ActionExplorationDriver. The actions supporting exploration MUST be implemented such they fully support interleaving and early aborting (sort of like writing a mobile app). + * What the ActionExplorationDriver does to explore is documented in ActionExplorationDriver itself. + * + * The sequence looks something like this ... + * - load an action (action1) and call this method again + * - processAction (action1) - error + * - ... error repeats for over ACTION_WARNING_INTERVAL -> log error and display on screen + * - processAction (action1) - success + * - clear error from screen + * - (action1) is complete + * - load next action (action2 {IKeyMomentExploration}) and call this method again + * - processAction (action2 {IKeyMomentExploration}) - error + * - ... error repeats for over ACTION_WARNING_INTERVAL -> log error and display on screen + * - START exploration + * - Perform exploration action + * - Reset (action2 {IKeyMomentExploration}) -- whenever we do an exploration action, we reset the main action back to its start state to be ready to fully run again + * - exploration action - error + * - log updated error + update error display on screen + * - processAction (action2 {IKeyMomentExploration}) - success + * - PAUSE exploration + * - clear error from screen + * - processAction (action2 {IKeyMomentExploration}) - success -- the action wasn't complete yet... success != complete, success just means that 1 call pass worked cleanly + * - processAction (action2 {IKeyMomentExploration}) - error + * - ... error repeats for over ACTION_WARNING_INTERVAL -> log error and display on screen + * - RESUME exploration + * - Perform exploration action + * - Reset (action2 {IKeyMomentExploration}) -- whenever we do an exploration action, we reset the main action back to its start state to be ready to fully run again + * - exploration action - success + * - processAction (action2 {IKeyMomentExploration}) - success + * - PAUSE exploration + * - clear error from screen + * - processAction (action2 {IKeyMomentExploration}) - success + * - processAction (action2 {IKeyMomentExploration}) - success + * - (action2) is complete + * + * (...) + * - load next action and call this method again + * (...) + * + * Thus an exploration action can be invoked in between any 2 passes of the main action when an error occurs. With that concept of update to update interleaving, it should be obvious why actions implementing IKeyMomentExploration must be + * written to expect to be interrupted at any point. + */ + private void ProcessBotSegmentAction(BotSegment firstActionSegment, Dictionary transformStatuses, Dictionary entityStatuses) + { + var now = Time.unscaledTime; + string logPrefix = $"({firstActionSegment.Replay_SegmentNumber}) - Bot Segment - "; + try + { + if (firstActionSegment.botAction?.IsCompleted == false) + { + // allow the main action to retry between every exploratory action + var didAction = firstActionSegment.ProcessAction(transformStatuses, entityStatuses, out var error); + + if (error == null) + { + // we're going to 'pause' exploring, but not reset the exploration state quite yet until this action fully finishes + _explorationDriver.PauseExploring(firstActionSegment.Replay_SegmentNumber); + if (didAction && _explorationDriver.ExplorationState == ExplorationState.STOPPED) + { + // for every non error action, reset the timer + _lastTimeLoggedKeyFrameConditions = now; + FindObjectOfType()?.SetKeyFrameWarningText(null); + } + } + + if (error != null) + { + // arranges this to build up a status message with real action + exploratory status + // TODO: (REG-2213) Update all this to use a status manager to manage reporting + + string loggedMessage; + + if (firstActionSegment.botAction?.data is IKeyMomentExploration) + { + loggedMessage = $"Error processing BotAction\n\n" + error + "\n\n\nRunning exploratory actions..."; + } + else + { + loggedMessage = $"Error processing BotAction\n\n" + error; + } + + // wait the action warning interval before starting to explore actions + if (_lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) + { + // log this before we start exploring so we can see why it started exploring + LogPlaybackWarning(logPrefix + loggedMessage); + if (firstActionSegment.botAction?.data is IKeyMomentExploration) + { + _explorationDriver.StartExploring(); + } + } + + string explorationError = null; + if (firstActionSegment.botAction?.data is IKeyMomentExploration keyMomentExploration) + { + _explorationDriver.PerformExploratoryAction(firstActionSegment.Replay_SegmentNumber, transformStatuses, entityStatuses, out explorationError); + // we just interfered mid action.. reset this thing to try again + keyMomentExploration.KeyMomentExplorationReset(); + } + + if (explorationError != null) + { + // put this on top to minimize screen flicker + loggedMessage = $"Error processing exploratory BotAction\n\n" + explorationError +"\n\n\nPre-Exploration Error - " + loggedMessage; + LogPlaybackWarning(logPrefix + loggedMessage); + } + else if (_explorationDriver.ExplorationState != ExplorationState.STOPPED || _lastTimeLoggedKeyFrameConditions < now - ACTION_WARNING_INTERVAL) + { + LogPlaybackWarning(logPrefix + loggedMessage); + } + } + } + + if (firstActionSegment.botAction?.IsCompleted == true && firstActionSegment.botAction?.data is IKeyMomentExploration) + { + // for every non error action, reset the timer + _lastTimeLoggedKeyFrameConditions = now; + FindObjectOfType()?.SetKeyFrameWarningText(null); + + _explorationDriver.StopExploring(firstActionSegment.Replay_SegmentNumber); + _explorationDriver.ReportPreviouslyCompletedAction(firstActionSegment.botAction.data); + } + + } + catch (Exception ex) + { + var loggedMessage = "Exception processing BotAction\n\n" + ex.Message; + LogPlaybackWarning(logPrefix + loggedMessage, ex); + // uncaught exception... stop and unload the segment + UnloadSegmentsAndReset(); + throw; + } + } + public void LateUpdate() { if (_playState == PlayState.Playing) { if (_dataPlaybackContainer != null) { - EvaluateBotSegments(); + ProcessBotSegments(); } if (_nextBotSegments.Count == 0) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index c2450657..43452816 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -52,7 +52,7 @@ public static List TokenizeObjectPath(string path) } /** - * Data for clicking on a key moment object in the frame + * Data for clicking on a key moment object in the frame. This is used to record key moment bot segments based on mouse actions. */ [Serializable] public class KeyMomentMouseActionData : IBotActionData, IKeyMomentExploration, IStringBuilderWriteable, IKeyMomentStringBuilderWriteable diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs index 82abcdfb..591a70ef 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMomentEvaluator.cs @@ -1,9 +1,8 @@ -using System.Collections.Generic; -using System.Linq; + +using System.Collections.Generic; using JetBrains.Annotations; using RegressionGames.StateRecorder.BotSegments.Models; -using RegressionGames.StateRecorder.BotSegments.Models.BotCriteria; -using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions; +using RegressionGames.StateRecorder.KeyMoments; using RegressionGames.StateRecorder.Models; namespace RegressionGames.StateRecorder @@ -11,10 +10,6 @@ namespace RegressionGames.StateRecorder public class KeyMomentEvaluator { - public readonly List MouseDataBuffer = new(); - - private MouseInputActionData _previousKeyMomentMouseInputState = null; - private long _keyMomentNumber; public void Reset() @@ -22,112 +17,49 @@ public void Reset() _keyMomentNumber = 1; } + // Future: We may change this to allow easier registration of evaluators by scanning the classpath... we already do a pattern like this in other code in the SDK, so should be easy to add in later + // Most of this class is where future work for new recording of key moments for keyboard inputs or actions will be glued in. This class WILL change its implementation quite a bit as those pieces are added and we see what patterns work best. + private readonly IKeyMomentEvaluator[] _keyMomentEvaluators = { + new MouseKeyMomentEvaluator() + }; + [CanBeNull] public BotSegment EvaluateKeyMoment(long tickNumber, out long keyMomentNumber) { - // build out the list of clicks and un-clicks up to the last un-click - List<(int, MouseInputActionData)> meaningfulInputs = new(); - MouseInputActionData priorMouseData = _previousKeyMomentMouseInputState; - var mouseDataBufferCount = MouseDataBuffer.Count; - var foundUnClick = false; - for (var i = 0; !foundUnClick && i < mouseDataBufferCount; i++) + // Future: This currently considers that only 1 key moment action will be return per segment. Maybe this is fine, but we may need to handle returning multiple segments for a single pass at some point, + // or maybe better would be smashing these into a BotSegmentList. Since it would be ambiguous to try to replay N actions at the same time, these will have to be sequenced in some way, OR we'll need to create + // evaluator/action-class pairs that blend things as we need. (ie: Mouse/Keyboard interleaving, Mouse/GameAction interleaving, etc) TBD if this is a real thing or just a possibility being considered. + keyMomentNumber = _keyMomentNumber; + BotSegment result = null; + foreach (var keyMomentEvaluator in _keyMomentEvaluators) { - var mouseData = MouseDataBuffer[i]; - var isUnClick = priorMouseData != null && mouseData.IsButtonUnClick(priorMouseData); - if (mouseData.clickedObjectNormalizedPaths.Length > 0 || isUnClick) + var segment = keyMomentEvaluator.IsKeyMoment(tickNumber, _keyMomentNumber); + if (segment != null) { - meaningfulInputs.Add((i,mouseData)); + result = segment; } - if (isUnClick) - { - foundUnClick = true; - // we found an un-click, we're done - } - - priorMouseData = mouseData; } - if (foundUnClick) + if (result != null) { - try - { - // this should always be either length 1 - only an un-click, or length 2 - click/un-click pair - - // only take from the buffer up to the last 'un-clicked' state - // this is 'hard'... we want to encapsulate click down/up as a single action together - // but in cases like FPS where I might 'hold' right click to ADS then repeatedly click/release the left mouse button to fire.. i don't want that ALL as one action - // each shot of the weapon would be an 'action', AND .. ads was an action.. so we break this up by any button release - - var inputsToProcess = new List(); - - // add a 'positional' prior state before the click/un-click - var priorIndex = meaningfulInputs[0].Item1 - 1; - if (priorIndex >= 0) - { - inputsToProcess.Add(MouseDataBuffer[priorIndex]); - } - - // if meaningfulInputs.Count == 1, then we just had an un-click and that's it... that would only ever happen if you had many buttons held down, and release 1 button 1 frame and another button another frame - if (meaningfulInputs.Count > 1) - { - // add the click - inputsToProcess.Add(meaningfulInputs[0].Item2); - } - - // add the un-click - inputsToProcess.Add(meaningfulInputs[^1].Item2); - - var botSegment = new BotSegment - { - name = $"KeyMoment: {_keyMomentNumber}, Tick: {tickNumber} - Mouse Action Segment", - - endCriteria = new List - { - new() - { - type = KeyFrameCriteriaType.ActionComplete, - data = new ActionCompleteKeyFrameCriteriaData() - } - }, - botAction = new BotAction - { - type = BotActionType.KeyMoment_MouseAction, - data = new KeyMomentMouseActionData - { - mouseActions = inputsToProcess - } - } - }; - - keyMomentNumber = _keyMomentNumber; - - if (inputsToProcess.Count == 2 && inputsToProcess[^1].clickedObjectNormalizedPaths.Length < 1) - { - // we had an un-click only segment on something without any paths.. basically an un-click on nothing - // this can happen when there is a random click/un-click on nothing in the game or on something that is excluded from RG seeing it in the state - - // we should NOT record this segment - keyMomentNumber = _keyMomentNumber; - return null; - } - - - // update the last state based on the end entry in our list - _previousKeyMomentMouseInputState = meaningfulInputs[^1].Item2; + keyMomentNumber = _keyMomentNumber; + // increment after reporting in the result + ++_keyMomentNumber; + } - ++_keyMomentNumber; + return result; + } - return botSegment; - } - finally + public void UpdateMouseInputData(List mouseInputData) + { + foreach (var keyMomentEvaluator in _keyMomentEvaluators) + { + if (keyMomentEvaluator is MouseKeyMomentEvaluator mouseKeyMomentEvaluator) { - // remove everything up through the un-click - MouseDataBuffer.RemoveRange(0, meaningfulInputs[^1].Item1 + 1); + mouseKeyMomentEvaluator.MouseDataBuffer.AddRange(mouseInputData); + break; } } - - keyMomentNumber = _keyMomentNumber; - return null; } } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments.meta new file mode 100644 index 00000000..32e8594c --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5845e515810f42bb8c83fa6681b65cdf +timeCreated: 1733941859 \ No newline at end of file diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/IKeyMomentEvaluator.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/IKeyMomentEvaluator.cs new file mode 100644 index 00000000..c289e996 --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/IKeyMomentEvaluator.cs @@ -0,0 +1,12 @@ +using RegressionGames.StateRecorder.BotSegments.Models; + +namespace RegressionGames.StateRecorder.KeyMoments +{ + /** + * Implemented by classes that provide key moment evaluations. + */ + public interface IKeyMomentEvaluator + { + BotSegment IsKeyMoment(long tickNumber, long keyMomentNumber); + } +} diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/IKeyMomentEvaluator.cs.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/IKeyMomentEvaluator.cs.meta new file mode 100644 index 00000000..7bb680e0 --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/IKeyMomentEvaluator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 29efbcca8d134d6391efac92c2302d90 +timeCreated: 1733941877 \ No newline at end of file diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs new file mode 100644 index 00000000..590a1183 --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; +using RegressionGames.StateRecorder.BotSegments.Models; +using RegressionGames.StateRecorder.BotSegments.Models.BotCriteria; +using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions; +using RegressionGames.StateRecorder.Models; + +namespace RegressionGames.StateRecorder.KeyMoments +{ + public class MouseKeyMomentEvaluator : IKeyMomentEvaluator + { + public readonly List MouseDataBuffer = new(); + + private MouseInputActionData _previousKeyMomentMouseInputState = null; + + public BotSegment IsKeyMoment(long tickNumber, long keyMomentNumber) + { + // build out the list of clicks and un-clicks up to the last un-click + List<(int, MouseInputActionData)> meaningfulInputs = new(); + MouseInputActionData priorMouseData = _previousKeyMomentMouseInputState; + var mouseDataBufferCount = MouseDataBuffer.Count; + var foundUnClick = false; + for (var i = 0; !foundUnClick && i < mouseDataBufferCount; i++) + { + var mouseData = MouseDataBuffer[i]; + var isUnClick = priorMouseData != null && mouseData.IsButtonUnClick(priorMouseData); + if (mouseData.clickedObjectNormalizedPaths.Length > 0 || isUnClick) + { + meaningfulInputs.Add((i,mouseData)); + } + if (isUnClick) + { + foundUnClick = true; + // we found an un-click, we're done + } + + priorMouseData = mouseData; + } + + if (foundUnClick) + { + try + { + // this should always be either length 1 - only an un-click, or length 2 - click/un-click pair + + // only take from the buffer up to the last 'un-clicked' state + // this is 'hard'... we want to encapsulate click down/up as a single action together + // but in cases like FPS where I might 'hold' right click to ADS then repeatedly click/release the left mouse button to fire.. i don't want that ALL as one action + // each shot of the weapon would be an 'action', AND .. ads was an action.. so we break this up by any button release + + var inputsToProcess = new List(); + + // add a 'positional' prior state before the click/un-click + var priorIndex = meaningfulInputs[0].Item1 - 1; + if (priorIndex >= 0) + { + inputsToProcess.Add(MouseDataBuffer[priorIndex]); + } + + // if meaningfulInputs.Count == 1, then we just had an un-click and that's it... that would only ever happen if you had many buttons held down, and release 1 button 1 frame and another button another frame + if (meaningfulInputs.Count > 1) + { + // add the click + inputsToProcess.Add(meaningfulInputs[0].Item2); + } + + // add the un-click + inputsToProcess.Add(meaningfulInputs[^1].Item2); + + var botSegment = new BotSegment + { + name = $"KeyMoment: {keyMomentNumber}, Tick: {tickNumber} - Mouse Action Segment", + + endCriteria = new List + { + new() + { + type = KeyFrameCriteriaType.ActionComplete, + data = new ActionCompleteKeyFrameCriteriaData() + } + }, + botAction = new BotAction + { + type = BotActionType.KeyMoment_MouseAction, + data = new KeyMomentMouseActionData + { + mouseActions = inputsToProcess + } + } + }; + + if (inputsToProcess.Count == 2 && inputsToProcess[^1].clickedObjectNormalizedPaths.Length < 1) + { + // we had an un-click only segment on something without any paths.. basically an un-click on nothing + // this can happen when there is a random click/un-click on nothing in the game or on something that is excluded from RG seeing it in the state + + // we should NOT record this segment + return null; + } + + + // update the last state based on the end entry in our list + _previousKeyMomentMouseInputState = meaningfulInputs[^1].Item2; + + return botSegment; + } + finally + { + // remove everything up through the un-click + MouseDataBuffer.RemoveRange(0, meaningfulInputs[^1].Item1 + 1); + } + } + + return null; + } + } +} diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs.meta new file mode 100644 index 00000000..fad800ae --- /dev/null +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a61d54dfa69142e0b3197db5e3dbcad1 +timeCreated: 1733942026 \ No newline at end of file diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs index 79c21d81..63c14d44 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/Models/MouseInputActionData.cs @@ -97,12 +97,6 @@ public void WriteToStringBuilder(StringBuilder stringBuilder) IntJsonConverter.WriteToStringBuilder(stringBuilder, apiVersion); stringBuilder.Append(",\"startTime\":"); DoubleJsonConverter.WriteToStringBuilder(stringBuilder, startTime); - stringBuilder.Append(",\"screenSize\":"); - Vector2IntJsonConverter.WriteToStringBuilder(stringBuilder, screenSize); - stringBuilder.Append(",\"position\":"); - Vector2IntJsonConverter.WriteToStringBuilder(stringBuilder, position); - stringBuilder.Append(",\"worldPosition\":"); - Vector3JsonConverter.WriteToStringBuilderNullable(stringBuilder, worldPosition); stringBuilder.Append(",\"leftButton\":"); BooleanJsonConverter.WriteToStringBuilder(stringBuilder,leftButton); stringBuilder.Append(",\"middleButton\":"); @@ -115,6 +109,12 @@ public void WriteToStringBuilder(StringBuilder stringBuilder) BooleanJsonConverter.WriteToStringBuilder(stringBuilder,backButton); stringBuilder.Append(",\"scroll\":"); Vector2JsonConverter.WriteToStringBuilder(stringBuilder, scroll); + stringBuilder.Append(",\"screenSize\":"); + Vector2IntJsonConverter.WriteToStringBuilder(stringBuilder, screenSize); + stringBuilder.Append(",\"position\":"); + Vector2IntJsonConverter.WriteToStringBuilder(stringBuilder, position); + stringBuilder.Append(",\"worldPosition\":"); + Vector3JsonConverter.WriteToStringBuilderNullable(stringBuilder, worldPosition); stringBuilder.Append(",\"clickedObjectNormalizedPaths\":["); var clickedObjectPathsLength = clickedObjectNormalizedPaths.Length; for (var i = 0; i < clickedObjectPathsLength; i++) @@ -137,33 +137,9 @@ public string ToJsonString() public void WriteKeyMomentToStringBuilder(StringBuilder stringBuilder) { - stringBuilder.Append("{\"apiVersion\":"); - IntJsonConverter.WriteToStringBuilder(stringBuilder, apiVersion); - stringBuilder.Append(",\"startTime\":"); - DoubleJsonConverter.WriteToStringBuilder(stringBuilder, startTime); - stringBuilder.Append(",\"leftButton\":"); - BooleanJsonConverter.WriteToStringBuilder(stringBuilder,leftButton); - stringBuilder.Append(",\"middleButton\":"); - BooleanJsonConverter.WriteToStringBuilder(stringBuilder,middleButton); - stringBuilder.Append(",\"rightButton\":"); - BooleanJsonConverter.WriteToStringBuilder(stringBuilder,rightButton); - stringBuilder.Append(",\"forwardButton\":"); - BooleanJsonConverter.WriteToStringBuilder(stringBuilder,forwardButton); - stringBuilder.Append(",\"backButton\":"); - BooleanJsonConverter.WriteToStringBuilder(stringBuilder,backButton); - stringBuilder.Append(",\"scroll\":"); - Vector2JsonConverter.WriteToStringBuilder(stringBuilder, scroll); - stringBuilder.Append(",\"clickedObjectNormalizedPaths\":["); - var clickedObjectPathsLength = clickedObjectNormalizedPaths.Length; - for (var i = 0; i < clickedObjectPathsLength; i++) - { - StringJsonConverter.WriteToStringBuilder(stringBuilder, clickedObjectNormalizedPaths[i]); - if (i + 1 < clickedObjectPathsLength) - { - stringBuilder.Append(","); - } - } - stringBuilder.Append("]}"); + // in the first implementations of this, we excluded the position information, but later added it back in so that we could pick the 'best' match to the original for things + // like character selection on boss room by picking the 'closest to original' match when multiple otherwise equal possibilities exist + WriteToStringBuilder(stringBuilder); } public string ToKeyMomentJsonString() diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index 3c6bd777..21fa7826 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -32,13 +32,17 @@ public void ObserveMouse(IEnumerable statefulObjects) _clickedObjectNormalizedPaths.Clear(); - // the objects found are already in order, but we do need to see if there was a worldPosition clicked - foreach (var clickedTransformStatus in clickedOnObjects) + // the objects found are already in order, but we do need to see if the topmost thing clicked had a worldPosition + if (clickedOnObjects.Count > 0) { - if (clickedTransformStatus.worldSpaceBounds != null && !newMouseState.worldPosition.HasValue) + if (clickedOnObjects[0].worldSpaceBounds != null && !newMouseState.worldPosition.HasValue) { - newMouseState.worldPosition = clickedTransformStatus.worldSpaceCoordinatesForMousePoint; + newMouseState.worldPosition = clickedOnObjects[0].worldSpaceCoordinatesForMousePoint; } + } + + foreach (var clickedTransformStatus in clickedOnObjects) + { _clickedObjectNormalizedPaths.Add(clickedTransformStatus.NormalizedPath); } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs index 87cfca7a..c302321b 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs @@ -864,7 +864,7 @@ private IEnumerator RecordFrame(bool endRecording = false, bool endRecordingFrom var mouseInputData = _mouseObserver.FlushInputDataBuffer(endRecordingFromToolbarButton, minimizeOutput: minimizeRecordingMouseMovements); _segmentMouseDataBuffer.AddRange(mouseInputData); - _keyMomentEvaluator.MouseDataBuffer.AddRange(mouseInputData); + _keyMomentEvaluator.UpdateMouseInputData(mouseInputData); // Compute key moment criteria / action var keyMomentBotSegment = _keyMomentEvaluator.EvaluateKeyMoment(_tickNumber, out var keyMomentNumber); From f7ce27152d7ff5981bfbeece94504f907460c104 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Wed, 11 Dec 2024 14:28:08 -0500 Subject: [PATCH 21/31] Positional preference on replay of clicks --- .../BotActions/KeyMomentMouseActionData.cs | 165 ++++++++++++------ 1 file changed, 107 insertions(+), 58 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 43452816..c61e8d97 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -125,7 +125,7 @@ public void StartAction(int segmentNumber, Dictionary curren // should tokenize to something like [HeroesObjects],[Unit,Hero,Gunner],[Spine,Mecanim,GameObject,unit,hero,gunner,RenderTexture] // thus any of the following examples... preference given to the one with the highest tokens matched.. note that tokens will ONLY match when IN ORDER // MATCH[1,3,7] (perfect) - "HeroesObjects/Unit_Hero_Gunner/Spine Mecanim GameObject (unit_hero_gunner) RenderTexture" - // -- but what if there are 2 or more 'perfect' matches.. then we need to narrow down based on the # of other path elements that overlap each of these + // -- but what if there are 2 or more 'perfect' matches.. then we need to narrow down based on the # of other path elements that overlap each of these... AND.. if those all match, then we go by the closest to the original click position in our later evaluations // MATCH[1,3,7] (perfect) - "HeroesObjects/Unit_Hero_Gunner/Spine Mecanim GameObject (unit_hero_gunner) RenderTexture" // MATCH[1,3,7] (perfect) - "HeroesObjects/Unit_Hero_Gunner/Spine Mecanim GameObject (unit_hero_gunner) RenderTexture" // -- these next two are 'equal' matches.. chooses first one encountered @@ -389,77 +389,63 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, { possibleTransformToClick.TokenizedObjectPath ??= PreconditionNormalizedPathData.TokenizeObjectPath(possibleTransformToClick.NormalizedPath); - var breakoutCount = 0; // optimization for when we've found exact matches for all preconditions // this should nearly always be smaller than the # of possibleTransformToClick for (var j = 0; j < preconditionsLength; j++) { - // optimization - only keep processing this precondition when we didn't have an exact match for it already - if (preconditionMatches[j].Count == 0 || preconditionMatches[j][0].Item2.Item1 != null) - { - var precondition = preConditionNormalizedPaths[j]; + var precondition = preConditionNormalizedPaths[j]; - // prefer normalized path match, then tokenized path matching logic - if (precondition.normalizedPath == possibleTransformToClick.NormalizedPath) + // prefer normalized path match, then tokenized path matching logic + if (precondition.normalizedPath == possibleTransformToClick.NormalizedPath) + { + // normalized matching logic + if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 != null) { - // normalized matching logic - if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 != null) - { - // wipe out all the tokenized matches if we get an exact - preconditionMatches[j].Clear(); - } + // wipe out all the tokenized matches if we get an exact + preconditionMatches[j].Clear(); + } - preconditionMatches[j].Add((possibleTransformToClick, (null, null))); + preconditionMatches[j].Add((possibleTransformToClick, (null, null))); + break; // the for + } + else + { + // tokenized matching logic + if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 == null) + { + // we already have exact matches.. ignore the tokenized ones break; // the for } - else - { - // tokenized matching logic - if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 == null) - { - // we already have exact matches.. ignore the tokenized ones - break; // the for - } - var tokenMatches = EvaluateTokenMatches(precondition.tokenData, possibleTransformToClick.TokenizedObjectPath); - if (tokenMatches.Item1 != null) + var tokenMatches = EvaluateTokenMatches(precondition.tokenData, possibleTransformToClick.TokenizedObjectPath); + if (tokenMatches.Item1 != null) + { + // got something of the same path length with some token matches in each part + if (preconditionMatches[j].Count > 0) { - // got something of the same path length with some token matches in each part - if (preconditionMatches[j].Count > 0) + var isBetter = AreNewTokenMatchesBetter(preconditionMatches[j][0].Item2, tokenMatches); + // compare + if (true == isBetter) { - var isBetter = AreNewTokenMatchesBetter(preconditionMatches[j][0].Item2, tokenMatches); - // compare - if (true == isBetter) - { - // better match.. clear the list - preconditionMatches[j].Clear(); - preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); - } - else if (isBetter == null) - { - // equal match.. add to the list - preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); - } - // else worse match.. leave it alone + // better match.. clear the list + preconditionMatches[j].Clear(); + preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); } - else + else if (isBetter == null) { - //set the first match + // equal match.. add to the list preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); } - - break; // the for + // else worse match.. leave it alone + } + else + { + //set the first match + preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); } + + break; // the for } } - else - { - ++breakoutCount; - } - } - - if (breakoutCount == preconditionsLength) - { - break; // the foreach - we found exact matches for everything } } } @@ -711,6 +697,11 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, } } + var widthScale = screenWidth / mouseAction.screenSize.x; + var heightScale = screenHeight / mouseAction.screenSize.y; + + var normalizedMouseActionSSPosition = new Vector2(mouseAction.position.x * widthScale, mouseAction.position.y * heightScale); + matchResults.Sort((a, b) => { if (a.Item2 < b.Item2) @@ -725,16 +716,74 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, return -1; } - // else sort by smallest bounds + // else if they were still equal sort by nearest distance to the original click + + // consider if world space first + if (mouseAction.worldPosition.HasValue) + { + if (a.Item1.worldSpaceBounds.HasValue) + { + if (b.Item1.worldSpaceBounds.HasValue) + { + // compare the distances to the original click point + var aClosestPoint = a.Item1.worldSpaceBounds.Value.ClosestPoint(mouseAction.worldPosition.Value); + var bClosestPoint = b.Item1.worldSpaceBounds.Value.ClosestPoint(mouseAction.worldPosition.Value); + + var aDistance = Vector3.Distance(aClosestPoint, mouseAction.worldPosition.Value); + var bDistance = Vector3.Distance(bClosestPoint, mouseAction.worldPosition.Value); + + if (aDistance < bDistance) + { + return -1; + } + + if (aDistance > bDistance) + { + return 1; + } + // else - unlikely to be exactly the same.. but let it go anyway + } + else + { + return -1; // a had world bounds.. more important + } + } + else if (b.Item1.worldSpaceBounds.HasValue) + { + return 1; // b had world bounds.. more important + } + } + + // otherwise consider screen space bounds + // bounds around z=0 (z size 0.5f) ... considering the concise bounds computed from overlaps + var aSSBounds = new Bounds(new Vector3((a.Item3.Item3 - a.Item3.Item1) / 2 + a.Item3.Item1, (a.Item3.Item4 - a.Item3.Item2) / 2 + a.Item3.Item2, 0f), new Vector3(a.Item3.Item3 - a.Item3.Item1, a.Item3.Item4 - a.Item3.Item2, 0.5f)); + var bSSBounds = new Bounds(new Vector3((b.Item3.Item3 - b.Item3.Item1) / 2 + b.Item3.Item1, (b.Item3.Item4 - b.Item3.Item2) / 2 + b.Item3.Item2, 0f), new Vector3(b.Item3.Item3 - b.Item3.Item1, b.Item3.Item4 - b.Item3.Item2, 0.5f)); + + var aSSClosestPoint = aSSBounds.ClosestPoint(normalizedMouseActionSSPosition); + var bSSClosestPoint = bSSBounds.ClosestPoint(normalizedMouseActionSSPosition); + + var aSSDistance = Vector3.Distance(aSSClosestPoint, normalizedMouseActionSSPosition); + var bSSDistance = Vector3.Distance(bSSClosestPoint, normalizedMouseActionSSPosition); + + if (aSSDistance < bSSDistance) + { + return -1; + } + + if (aSSDistance > bSSDistance) + { + return 1; + } + // else - unlikely to be exactly the same.. but let it go anyway + + // else if still somehow equal sort by smallest bounds area var aArea = (a.Item3.Item3 - a.Item3.Item1) * (a.Item3.Item4 - a.Item3.Item2); var bArea = (b.Item3.Item3 - b.Item3.Item1) * (b.Item3.Item4 - b.Item3.Item2); - if (aArea < bArea) { return -1; } - - // floating point math.. don't really care about equality in the zillionth of a percent chance that happens here + // floating point multiplication math for area... don't really care about equality in the zillionth of a percent chance that happens here return 1; }); From 1bd4c5998bf44663a87f4402c760a57dc60ebe74 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Wed, 11 Dec 2024 14:46:38 -0500 Subject: [PATCH 22/31] fix exploration hang timing bug --- .../BotSegments/BotSegmentsPlaybackController.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs index d5f2fd11..446613d2 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs @@ -314,15 +314,18 @@ private void ProcessBotSegmentAction(BotSegment firstActionSegment, Dictionary Date: Wed, 11 Dec 2024 15:47:18 -0500 Subject: [PATCH 23/31] Fix botsegment mouse recording --- .../StateRecorder/MouseInputActionObserver.cs | 18 ++------------ .../Scripts/StateRecorder/ScreenRecorder.cs | 24 ++++++++++--------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index 21fa7826..72ae6e4f 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -117,29 +117,15 @@ public void ClearBuffer() /** * Flush the latest captured mouse movements * Ignore the last click.. useful for recordings to avoid capturing the click of the stop recording button - * Flag to leave out everything except the key movements around clicks. */ - public List FlushInputDataBuffer(bool ignoreLastClick, bool minimizeOutput) + public List FlushInputDataBuffer(bool ignoreLastClick) { List result = new(); - MouseInputActionData lastAction = null; while (_mouseInputActions.TryPeek(out var action)) { _mouseInputActions.TryDequeue(out _); + result.Add(action); - // when minimizing output, only capture the inputs 1 before 1 after and during mouse clicks or holds... the normalized paths is populated for both clicks and un-clicks so is useful for determining the un-click data - - if (lastAction != null && (!minimizeOutput || action.IsButtonClicked || action.clickedObjectNormalizedPaths.Length > 0 || lastAction.IsButtonClicked || lastAction.clickedObjectNormalizedPaths.Length > 0)) - { - result.Add(lastAction); - } - - lastAction = action; - } - - if (lastAction != null) - { - result.Add(lastAction); } if (result.Count == 0 && _priorMouseState != null) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs index c302321b..3df009af 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs @@ -69,9 +69,6 @@ public class ScreenRecorder : MonoBehaviour [Tooltip("Minimize the amount of criteria in bot segment recordings. This limits the endCriteria in recorded bot segments to only include objects with count deltas and objects that were under click locations.")] public bool minimizeRecordingCriteria = true; - [Tooltip("RG-INTERNAL/EXPERIMENTAL: Minimize the amount of mouse movement data capture in bot segment recordings. This currently breaks replay on any game where camera movement is locked to the mouse, like First Person Shooters.")] - public bool minimizeRecordingMouseMovements = false; - private double _lastCvFrameTime = -1; private int _frameCountSinceLastTick; @@ -862,7 +859,7 @@ private IEnumerator RecordFrame(bool endRecording = false, bool endRecordingFrom // tell if the new frame is a key frame or the first frame (always a key frame) GetKeyFrameType(_tickNumber == 0, hasDeltas, pixelHashChanged, endRecording); - var mouseInputData = _mouseObserver.FlushInputDataBuffer(endRecordingFromToolbarButton, minimizeOutput: minimizeRecordingMouseMovements); + var mouseInputData = _mouseObserver.FlushInputDataBuffer(endRecordingFromToolbarButton); _segmentMouseDataBuffer.AddRange(mouseInputData); _keyMomentEvaluator.UpdateMouseInputData(mouseInputData); @@ -915,22 +912,23 @@ private IEnumerator RecordFrame(bool endRecording = false, bool endRecordingFrom } // also include any clicked on objects - if (mouseInputData.FirstOrDefault(md => md.clickedObjectNormalizedPaths.Contains(data.path)) != null) + if (_segmentMouseDataBuffer.FirstOrDefault(md => md.clickedObjectNormalizedPaths.Contains(data.path)) != null) { a.transient = false; return a; } } } + return null; - }).Where(a=>a!=null).ToList(); + }).Where(a => a != null).ToList(); } // we often get events in the buffer with input times fractions of a ms after the current frame time for this update, but actually related to causing this update // update the frame time to be latest of 'now' or the last device event in it // otherwise replay gets messed up trying to read the inputs by time var mostRecentKeyboardTime = keyboardInputData == null || keyboardInputData.Count == 0 ? 0.0 : keyboardInputData.Max(a => !a.endTime.HasValue ? a.startTime.Value : Math.Max(a.startTime.Value, a.endTime.Value)); - var mostRecentMouseTime = mouseInputData == null || mouseInputData.Count == 0 ? 0.0 : mouseInputData.Max(a => a.startTime); + var mostRecentMouseTime = _segmentMouseDataBuffer == null || _segmentMouseDataBuffer.Count == 0 ? 0.0 : _segmentMouseDataBuffer.Max(a => a.startTime); var mostRecentDeviceEventTime = Math.Max(mostRecentKeyboardTime, mostRecentMouseTime); var frameTime = Math.Max(now, mostRecentDeviceEventTime); @@ -950,7 +948,7 @@ private IEnumerator RecordFrame(bool endRecording = false, bool endRecordingFrom var inputData = new InputData() { keyboard = keyboardInputData, - mouse = mouseInputData + mouse = _segmentMouseDataBuffer }; if (pixelHashChanged) @@ -973,9 +971,9 @@ private IEnumerator RecordFrame(bool endRecording = false, bool endRecordingFrom if (inputTime < 0) { // first frame, get the mouse input time for the first frame if it exists - if (mouseInputData.Count > 0) + if (_segmentMouseDataBuffer.Count > 0) { - inputTime = mouseInputData[0].startTime; + inputTime = _segmentMouseDataBuffer[0].startTime; } } @@ -1082,7 +1080,7 @@ private IEnumerator RecordFrame(bool endRecording = false, bool endRecordingFrom { if (RGDebug.IsDebugEnabled) { - RGDebug.LogDebug("Tick " + _tickNumber + " had " + keyboardInputData?.Count + " keyboard inputs , " + mouseInputData?.Count + " mouse inputs - KeyFrame: [" + string.Join(',', frameState.keyFrame) + "]"); + RGDebug.LogDebug("Tick " + _tickNumber + " had " + keyboardInputData?.Count + " keyboard inputs , " + _segmentMouseDataBuffer?.Count + " mouse inputs - KeyFrame: [" + string.Join(',', frameState.keyFrame) + "]"); } } @@ -1103,6 +1101,10 @@ private IEnumerator RecordFrame(bool endRecording = false, bool endRecordingFrom { RGDebug.LogException(e, $"Exception capturing state for tick # {_tickNumber}"); } + finally + { + _segmentMouseDataBuffer.Clear(); + } var didQueue = 0; From b3d6ffd651405da473869bc3b4dc2a2b1ade997a Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Wed, 11 Dec 2024 16:05:47 -0500 Subject: [PATCH 24/31] Update MouseInputActionObserver.cs --- .../Scripts/StateRecorder/MouseInputActionObserver.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs index 72ae6e4f..cac8ffba 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseInputActionObserver.cs @@ -147,6 +147,15 @@ public List FlushInputDataBuffer(bool ignoreLastClick) return result; } + /** + * This finds all objects at the given position that a mouse click would possibly register on and returns them sorted correctly based on how that click would be handled. + * + * 1. UI objects based on their order from EventSystem.current.RaycastAll + * If a UI object includes components that are `Selectable`, then it will only be returned if one of those has `interactable` == true. This means disabled buttons/etc won't be included. + * + * 2. World Space objects with colliders hit based on the distance from Camera.main to their collider hit. + * This properly considers colliders on parent objects in the hierarchy being hit as it is common for a parent to have the collider and child objects to just be renderers. + */ public static List FindObjectsAtPosition(Vector2 position, IEnumerable statefulObjects) { // make sure screen space position Z is around 0 From e74ffb42c53bf5a0eeb6cdbdc46c94d17c6b8707 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Wed, 11 Dec 2024 21:47:43 -0500 Subject: [PATCH 25/31] [REG-2228] Fix world space object click collider misses --- .../BotActions/KeyMomentMouseActionData.cs | 46 +++- .../StateRecorder/TransformObjectFinder.cs | 208 ++++++++++-------- 2 files changed, 162 insertions(+), 92 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index c61e8d97..1c58e757 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -566,11 +566,11 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, // visible (already true by the time we get here)/active-enabled(we already know that from a canvas perspective this is visible, but need to check UI component info) if (preconditionMatch0.Item1 is TransformStatus preconditionMatch0TransformStatus) { - var theTransform = preconditionMatch0TransformStatus.Transform; - if (theTransform is RectTransform) + var preconditionMatch0Transform = preconditionMatch0TransformStatus.Transform; + if (preconditionMatch0Transform is RectTransform) { // ui object - var selectables = theTransform.GetComponents(); + var selectables = preconditionMatch0Transform.GetComponents(); if (selectables.Length > 0) { // make sure 1 is interactable .. otherwise leave isPreconditionMatch0Interactable == true @@ -583,6 +583,25 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, { // ReSharper disable once PossibleInvalidOperationException - already filtered in BuildPreconditions to only have entries with valid visible bounds var smallestBounds = preconditionMatch0.Item1.screenSpaceBounds.Value; + + // for world space object, narrow the bounds to its collider + if (preconditionMatch0.Item1.worldSpaceBounds != null && preconditionMatch0.Item1 is TransformStatus preconditionMatch0Ts) + { + var preconditionMatch0Transform = preconditionMatch0Ts.Transform; + + // check for a collider + var collider = preconditionMatch0Transform.GetComponentInParent(); + if (collider != null) + { + // limit the bounds starting with the collider bounds... the renderer we captured could be bigger than the collider, but the click will only work on the collider + var ssBounds = TransformObjectFinder.ConvertWorldSpaceBoundsToScreenSpace(collider.bounds); + if (ssBounds.HasValue) + { + smallestBounds = ssBounds.Value; + } + } + } + // we tried doing this with int math.. but pixel fluctuations of objects with fractional render bounds are a thing (think shimmering/flicker along aliased edges in games) // limit to the screen space.. some visible things hang off the screen and we don't want to click off the screen var minX = Mathf.Max(smallestBounds.min.x, 0f); @@ -629,6 +648,24 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, var pmIDidOverlap = false; // ReSharper disable once PossibleInvalidOperationException - already filtered in BuildPreconditions to only have entries with valid visible bounds var newBounds = preconditionMatchI.Item1.screenSpaceBounds.Value; + + if (preconditionMatchI.Item1.worldSpaceBounds != null && preconditionMatchI.Item1 is TransformStatus preconditionMatchITs) + { + var preconditionMatchITransform = preconditionMatchITs.Transform; + + // check for a collider + var collider = preconditionMatchITransform.GetComponentInParent(); + if (collider != null) + { + // limit the bounds starting with the collider bounds... the renderer we captured could be bigger than the collider, but the click will only work on the collider + var ssBounds = TransformObjectFinder.ConvertWorldSpaceBoundsToScreenSpace(collider.bounds); + if (ssBounds.HasValue) + { + newBounds = ssBounds.Value; + } + } + } + // adjust in for the min to ensure we don't miss a click var newMinX = newBounds.min.x; var newMinY = newBounds.min.y; @@ -809,7 +846,8 @@ private bool DoActionForObjectAtPosition(int segmentNumber, MouseInputActionData // see if our object is 'first' or obstructed if (objectsAtClickPosition.Count > 0) { - if (!targetObjectPath.StartsWith(objectsAtClickPosition[0].NormalizedPath)) + // handle the case where what we need to click is actually a collider on a parent object + if (!(targetObjectPath.StartsWith(objectsAtClickPosition[0].NormalizedPath) || objectsAtClickPosition[0].NormalizedPath.StartsWith(targetObjectPath))) { error = $"Unable to perform Key Moment Mouse Action at position:\n({(int)myClickPosition.x}, {(int)myClickPosition.y})\n\non object path:\n{targetObjectPath}\n\nanother object is obstructing with path:\n{objectsAtClickPosition[0].NormalizedPath}"; return false; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs index 7aa25796..a6dba346 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/TransformObjectFinder.cs @@ -237,6 +237,109 @@ public override (Dictionary, Dictionary) return (_priorObjects, _newObjects); } + public static (float,float,float,float,float,float)? ConvertWorldSpaceBoundsToScreenSpace(float minWorldX, float maxWorldX, float minWorldY, float maxWorldY, float minWorldZ, float maxWorldZ) + { + // convert world space to screen space + WorldCorners[0].x = minWorldX; + WorldCorners[0].y = minWorldY; + WorldCorners[0].z = minWorldZ; + + WorldCorners[1].x = maxWorldX; + WorldCorners[1].y = minWorldY; + WorldCorners[1].z = minWorldZ; + + WorldCorners[2].x = maxWorldX; + WorldCorners[2].y = maxWorldY; + WorldCorners[2].z = minWorldZ; + + WorldCorners[3].x = minWorldX; + WorldCorners[3].y = maxWorldY; + WorldCorners[3].z = minWorldZ; + + WorldCorners[4].x = minWorldX; + WorldCorners[4].y = minWorldY; + WorldCorners[4].z = maxWorldZ; + + WorldCorners[5].x = maxWorldX; + WorldCorners[5].y = minWorldY; + WorldCorners[5].z = maxWorldZ; + + WorldCorners[6].x = maxWorldX; + WorldCorners[6].y = maxWorldY; + WorldCorners[6].z = maxWorldZ; + + WorldCorners[7].x = minWorldX; + WorldCorners[7].y = maxWorldY; + WorldCorners[7].z = maxWorldZ; + + var minX = float.MaxValue; + var maxX = float.MinValue; + + var minY = float.MaxValue; + var maxY = float.MinValue; + + var minZ = float.MaxValue; + var maxZ = float.MinValue; + + var worldCornersLength = WorldCorners.Length; + var mainCamera = Camera.main; + if (mainCamera != null) + { + for (var i = 0; i < worldCornersLength; i++) + { + var screenSpaceObjectCorner = mainCamera.WorldToScreenPoint(WorldCorners[i]); + var x = screenSpaceObjectCorner.x; + if (x < minX) + { + minX = x; + } + + if (x > maxX) + { + maxX = x; + } + + var y = screenSpaceObjectCorner.y; + if (y < minY) + { + minY = y; + } + + if (y > maxY) + { + maxY = y; + } + + var z = screenSpaceObjectCorner.z; + if (z < minZ) + { + minZ = z; + } + + if (z > maxZ) + { + maxZ = z; + } + } + + return (minX, maxX, minY, maxY, minZ, maxZ); + } + + return null; + } + + public static Bounds? ConvertWorldSpaceBoundsToScreenSpace(Bounds worldSpaceBounds) + { + var values = ConvertWorldSpaceBoundsToScreenSpace(worldSpaceBounds.min.x, worldSpaceBounds.max.x, worldSpaceBounds.min.y, worldSpaceBounds.max.y, worldSpaceBounds.min.z, worldSpaceBounds.max.z); + if (values.HasValue) + { + var extents = new Vector3((values.Value.Item2 - values.Value.Item1) / 2, (values.Value.Item4 - values.Value.Item3) / 2, (values.Value.Item6 - values.Value.Item5) / 2); + return new Bounds(new Vector3(values.Value.Item1 + extents.x, values.Value.Item3 + extents.y, values.Value.Item5 + extents.z), extents ); + } + + return null; + } + // ReSharper disable once MemberCanBePrivate.Global - Keep this method public, while not called from this package module, it is called from some of our extension packages public static (Bounds?, float, Bounds?) SelectBoundsForTransform(Camera mainCamera, int screenWidth, int screenHeight, Transform theTransform) { @@ -475,113 +578,42 @@ public static (Bounds?, float, Bounds?) SelectBoundsForTransform(Camera mainCame if (onCamera) { - // convert world space to screen space - WorldCorners[0].x = minWorldX; - WorldCorners[0].y = minWorldY; - WorldCorners[0].z = minWorldZ; - - WorldCorners[1].x = maxWorldX; - WorldCorners[1].y = minWorldY; - WorldCorners[1].z = minWorldZ; - - WorldCorners[2].x = maxWorldX; - WorldCorners[2].y = maxWorldY; - WorldCorners[2].z = minWorldZ; - - WorldCorners[3].x = minWorldX; - WorldCorners[3].y = maxWorldY; - WorldCorners[3].z = minWorldZ; - - WorldCorners[4].x = minWorldX; - WorldCorners[4].y = minWorldY; - WorldCorners[4].z = maxWorldZ; - - WorldCorners[5].x = maxWorldX; - WorldCorners[5].y = minWorldY; - WorldCorners[5].z = maxWorldZ; - - WorldCorners[6].x = maxWorldX; - WorldCorners[6].y = maxWorldY; - WorldCorners[6].z = maxWorldZ; - - WorldCorners[7].x = minWorldX; - WorldCorners[7].y = maxWorldY; - WorldCorners[7].z = maxWorldZ; - - var minX = float.MaxValue; - var maxX = float.MinValue; - - var minY = float.MaxValue; - var maxY = float.MinValue; - - var minZ = float.MaxValue; - var maxZ = float.MinValue; - - var worldCornersLength = WorldCorners.Length; - for (var i = 0; i < worldCornersLength; i++) + var boundsValues = ConvertWorldSpaceBoundsToScreenSpace(minWorldX, maxWorldX, minWorldY, maxWorldY, minWorldZ, maxWorldZ); + if (!boundsValues.HasValue) { - var screenSpaceObjectCorner = mainCamera.WorldToScreenPoint(WorldCorners[i]); - var x = screenSpaceObjectCorner.x; - if (x < minX) - { - minX = x; - } - - if (x > maxX) - { - maxX = x; - } - - var y = screenSpaceObjectCorner.y; - if (y < minY) - { - minY = y; - } - - if (y > maxY) - { - maxY = y; - } - - var z = screenSpaceObjectCorner.z; - if (z < minZ) - { - minZ = z; - } - - if (z > maxZ) - { - maxZ = z; - } + onCamera = false; } - var xLowerLimit = 0; - var xUpperLimit = screenWidth; - var yLowerLimit = 0; - var yUpperLimit = screenHeight; - if (!(minX <= xUpperLimit && maxX >= xLowerLimit && minY <= yUpperLimit && maxY >= yLowerLimit)) + if (onCamera) { - // not in camera.. - onCamera = false; + var xLowerLimit = 0; + var xUpperLimit = screenWidth; + var yLowerLimit = 0; + var yUpperLimit = screenHeight; + if (!(boundsValues.Value.Item1 <= xUpperLimit && boundsValues.Value.Item2 >= xLowerLimit && boundsValues.Value.Item3 <= yUpperLimit && boundsValues.Value.Item4 >= yLowerLimit)) + { + // not in camera.. + onCamera = false; + } } if (onCamera) { // make sure the screen space bounds has a non-zero Z size around 0 // we track the true z offset separately for ease of mouse selection on replay - var size = new Vector3((maxX - minX), (maxY - minY), 0.05f); - var center = new Vector3(minX + size.x / 2, minY + size.y / 2, 0); + var size = new Vector3((boundsValues.Value.Item2 - boundsValues.Value.Item1), (boundsValues.Value.Item4 - boundsValues.Value.Item3), 0.05f); + var center = new Vector3(boundsValues.Value.Item1 + size.x / 2, boundsValues.Value.Item3 + size.y / 2, 0); var worldSize = new Vector3((maxWorldX - minWorldX), (maxWorldY - minWorldY), (maxWorldZ - minWorldZ)); var worldCenter = new Vector3(minWorldX + worldSize.x / 2, minWorldY + worldSize.y / 2, minWorldZ + worldSize.z / 2); - var zOffset = Math.Min(minZ, maxZ); + var zOffset = Math.Min(boundsValues.Value.Item5, boundsValues.Value.Item6); // if the zOffset is negative, our camera is inside the bounding volume for this object, choose the farthest z instead.. this happens for particle effects like fx_ImpSpawner in bossroom // we still need to track down why this thing has HUGE screenspace bounds compared to what is shown in the editor if (zOffset < 0.0f) { - zOffset = Math.Max(minZ, maxZ); + zOffset = Math.Max(boundsValues.Value.Item5, boundsValues.Value.Item6); } // don't let world space objects be <= 0.0f as they would appear on top of true ui overlay objects in our processing if (zOffset <= 0f) From ceabca0606c2c22d628c3542070081c2ecb6e734 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Thu, 12 Dec 2024 14:24:58 -0500 Subject: [PATCH 26/31] Prefer clicking object nearest the original click --- .../Fonts/Resources/TMP Settings.asset | 4 +- .../BotActions/KeyMomentMouseActionData.cs | 52 ++++++------------- 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Fonts/Resources/TMP Settings.asset b/src/gg.regression.unity.bots/Runtime/Fonts/Resources/TMP Settings.asset index e79bb692..f2fec3cb 100644 --- a/src/gg.regression.unity.bots/Runtime/Fonts/Resources/TMP Settings.asset +++ b/src/gg.regression.unity.bots/Runtime/Fonts/Resources/TMP Settings.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4432a0ecb71e9f5cede524054fd31e73bef8f899138ab6f6b1240de936be582a -size 4416 +oid sha256:52f55e974c1989bfc3ca68c96b9cd6ab0fbdd7d676c1026d54604883983ef20b +size 1702 diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 1c58e757..9e6e5c08 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -220,10 +220,10 @@ public bool ProcessAction(int segmentNumber, Dictionary curr // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not if (_lastClickPosition.HasValue) { - if (_lastClickPosition.Value.x >= bestUnClickMatchResult.Value.Item3.Item1 - && _lastClickPosition.Value.x <= bestUnClickMatchResult.Value.Item3.Item3 - && _lastClickPosition.Value.y >= bestUnClickMatchResult.Value.Item3.Item2 - && _lastClickPosition.Value.y <= bestUnClickMatchResult.Value.Item3.Item4) + if (_lastClickPosition.Value.x >= bestUnClickMatchResult.Value.Item2.Item1 + && _lastClickPosition.Value.x <= bestUnClickMatchResult.Value.Item2.Item3 + && _lastClickPosition.Value.y >= bestUnClickMatchResult.Value.Item2.Item2 + && _lastClickPosition.Value.y <= bestUnClickMatchResult.Value.Item2.Item4) { RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on bounds overlaps to original path: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the @@ -353,7 +353,7 @@ public bool ProcessAction(int segmentNumber, Dictionary curr return false; } - private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float,float)) bestMatchResult) + private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float)) bestMatchResult) { // we started with clicking the center.. but this was limiting //clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); @@ -363,7 +363,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, // on the next attempt, it picks a new position to try thus giving us a better chance of passing // TODO (REG-2223): Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? // This would allow us to know that we clicked about X far into this floor tile and replicate that positioning regardless of the actual worldspace positioning in the replay... - Vector2 result = new Vector2(Random.Range(bestMatchResult.Item3.Item1, bestMatchResult.Item3.Item3), Random.Range(bestMatchResult.Item3.Item2, bestMatchResult.Item3.Item4)); + Vector2 result = new Vector2(Random.Range(bestMatchResult.Item2.Item1, bestMatchResult.Item2.Item3), Random.Range(bestMatchResult.Item2.Item2, bestMatchResult.Item2.Item4)); return result; } @@ -543,7 +543,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, return result; } - private (ObjectStatus, int, (float,float,float,float))? FindBestMatch(MouseInputActionData mouseAction, List<(ObjectStatus, (int[], int[]))>[] preconditionMatches, out List overlappingObjects) + private (ObjectStatus, (float,float,float,float))? FindBestMatch(MouseInputActionData mouseAction, List<(ObjectStatus, (int[], int[]))>[] preconditionMatches, out List overlappingObjects) { overlappingObjects = new List(); @@ -552,9 +552,9 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, { // yay.. we found something(s).. figure out which one of them is the 'best' based on number of overlapping matches - // for each of the left most preconditionMatches[0] .. go through each of other precondition matches and count how many overlap.. can only count 0 or 1 per each index - // we will also compute the 'smallest' click bounds for these overlaps as we go - var matchResults = new List<(ObjectStatus, int, (float, float, float, float))>(preconditionMatches[0].Count); + // for each of the left most preconditionMatches[0] .. go through each of other precondition matches + // we will compute the 'smallest' click bounds for these overlaps as we go + var matchResults = new List<(ObjectStatus, (float, float, float, float))>(preconditionMatches[0].Count); var screenWidth = Screen.width; var screenHeight = Screen.height; @@ -616,14 +616,11 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, RGDebug.LogDebug($"Starting with bounds: ({minX}, {minY}),({maxX}, {maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} , target object: {preconditionMatch0.Item1.NormalizedPath}"); // now let's narrow down the screen space bounds more precisely based on all our preconditions - // count the number of indexes where we had an overlap while doing it - var overlapCount = 0; for (var i = 1; i < preconditionMatches.Length; i++) { var preconditionMatchesI = preconditionMatches[i]; if (preconditionMatchesI.Count > 0) { - var didOverlap = false; foreach (var preconditionMatchI in preconditionMatchesI) { var isInteractable = true; @@ -716,21 +713,17 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, if (pmIDidOverlap) { - didOverlap = true; overlappingObjects.Add(preconditionMatchI.Item1); RGDebug.LogDebug($"Tightened bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} - overlap with object path [{i}]: {preconditionMatchI.Item1.NormalizedPath}"); } } } - if (didOverlap) - { - ++overlapCount; - } + } } - matchResults.Add((preconditionMatch0.Item1, overlapCount, (minX, minY, maxX, maxY))); + matchResults.Add((preconditionMatch0.Item1, (minX, minY, maxX, maxY))); } } @@ -741,19 +734,8 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, matchResults.Sort((a, b) => { - if (a.Item2 < b.Item2) - { - // sort lower match counts to the end - return 1; - } - - if (a.Item2 > b.Item2) - { - // sort highest match counts to the front - return -1; - } - // else if they were still equal sort by nearest distance to the original click + // sort by nearest distance to the original click // consider if world space first if (mouseAction.worldPosition.HasValue) @@ -793,8 +775,8 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, // otherwise consider screen space bounds // bounds around z=0 (z size 0.5f) ... considering the concise bounds computed from overlaps - var aSSBounds = new Bounds(new Vector3((a.Item3.Item3 - a.Item3.Item1) / 2 + a.Item3.Item1, (a.Item3.Item4 - a.Item3.Item2) / 2 + a.Item3.Item2, 0f), new Vector3(a.Item3.Item3 - a.Item3.Item1, a.Item3.Item4 - a.Item3.Item2, 0.5f)); - var bSSBounds = new Bounds(new Vector3((b.Item3.Item3 - b.Item3.Item1) / 2 + b.Item3.Item1, (b.Item3.Item4 - b.Item3.Item2) / 2 + b.Item3.Item2, 0f), new Vector3(b.Item3.Item3 - b.Item3.Item1, b.Item3.Item4 - b.Item3.Item2, 0.5f)); + var aSSBounds = new Bounds(new Vector3((a.Item2.Item3 - a.Item2.Item1) / 2 + a.Item2.Item1, (a.Item2.Item4 - a.Item2.Item2) / 2 + a.Item2.Item2, 0f), new Vector3(a.Item2.Item3 - a.Item2.Item1, a.Item2.Item4 - a.Item2.Item2, 0.5f)); + var bSSBounds = new Bounds(new Vector3((b.Item2.Item3 - b.Item2.Item1) / 2 + b.Item2.Item1, (b.Item2.Item4 - b.Item2.Item2) / 2 + b.Item2.Item2, 0f), new Vector3(b.Item2.Item3 - b.Item2.Item1, b.Item2.Item4 - b.Item2.Item2, 0.5f)); var aSSClosestPoint = aSSBounds.ClosestPoint(normalizedMouseActionSSPosition); var bSSClosestPoint = bSSBounds.ClosestPoint(normalizedMouseActionSSPosition); @@ -814,8 +796,8 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, int, (float,float,float, // else - unlikely to be exactly the same.. but let it go anyway // else if still somehow equal sort by smallest bounds area - var aArea = (a.Item3.Item3 - a.Item3.Item1) * (a.Item3.Item4 - a.Item3.Item2); - var bArea = (b.Item3.Item3 - b.Item3.Item1) * (b.Item3.Item4 - b.Item3.Item2); + var aArea = (a.Item2.Item3 - a.Item2.Item1) * (a.Item2.Item4 - a.Item2.Item2); + var bArea = (b.Item2.Item3 - b.Item2.Item1) * (b.Item2.Item4 - b.Item2.Item2); if (aArea < bArea) { return -1; From e108b53d6f2f1ee86e9de8cbdc127f7a3b6c2f96 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Thu, 12 Dec 2024 14:58:36 -0500 Subject: [PATCH 27/31] Prefer hitting the exact unclick position of the click on object match to avoid causing unity to treat the input as a drag --- .../BotActions/KeyMomentMouseActionData.cs | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 9e6e5c08..b135a4ef 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -74,7 +74,6 @@ public class KeyMomentMouseActionData : IBotActionData, IKeyMomentExploration, I // this is important as we don't want the next segment to run on this same frame until the result of the mouse click action has processed in the game engine private bool _isDoneWaitOneFrame = false; - // These are all used to track the un-click work across multiple Update calls private double _unClickTime = 0d; private Vector2? _lastClickPosition = null; @@ -215,34 +214,37 @@ public bool ProcessAction(int segmentNumber, Dictionary curr clickPosition = GetClickPositionForMatch(bestUnClickMatchResult.Value); // if our best match path isn't the same as the click path.. do some extra evaluation - if (bestObjectStatus.NormalizedPath != mouseActions[1].clickedObjectNormalizedPaths[0]) + // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not + if (_lastClickPosition.HasValue) { - // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not - if (_lastClickPosition.HasValue) + if (_lastClickPosition.Value.x >= bestUnClickMatchResult.Value.Item2.Item1 + && _lastClickPosition.Value.x <= bestUnClickMatchResult.Value.Item2.Item3 + && _lastClickPosition.Value.y >= bestUnClickMatchResult.Value.Item2.Item2 + && _lastClickPosition.Value.y <= bestUnClickMatchResult.Value.Item2.Item4) { - if (_lastClickPosition.Value.x >= bestUnClickMatchResult.Value.Item2.Item1 - && _lastClickPosition.Value.x <= bestUnClickMatchResult.Value.Item2.Item3 - && _lastClickPosition.Value.y >= bestUnClickMatchResult.Value.Item2.Item2 - && _lastClickPosition.Value.y <= bestUnClickMatchResult.Value.Item2.Item4) + RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on bounds overlaps to original path: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); + // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the + // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame + clickPosition = _lastClickPosition.Value; + if (bestObjectStatus.NormalizedPath != mouseActions[1].clickedObjectNormalizedPaths[0]) { - RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on bounds overlaps to original path: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); - // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the - // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame - clickPosition = _lastClickPosition.Value; bestObjectStatus = null; } - else if (_previousOverlappingObjects.Count > 0) - { - // this can handle things where the un-click was recorded 'after' the element dis-appeared, but it is still there in the replay until the un-click happens... this is just a matter of how ui events and when we can observe during recording works + } + else if (_previousOverlappingObjects.Count > 0) + { + // this can handle things where the un-click was recorded 'after' the element dis-appeared, but it is still there in the replay until the un-click happens... this is just a matter of how ui events and when we can observe during recording works - // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) - // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button - // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen - // isn't important.. we want to un-click exactly where we clicked - if (_previousOverlappingObjects.Any(a => a == bestUnClickMatchResult.Value.Item1)) + // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) + // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button + // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen + // isn't important.. we want to un-click exactly where we clicked + if (_previousOverlappingObjects.Any(a => a == bestUnClickMatchResult.Value.Item1)) + { + RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on object path existing at time of click: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); + clickPosition = _lastClickPosition.Value; + if (bestObjectStatus.NormalizedPath != mouseActions[1].clickedObjectNormalizedPaths[0]) { - RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on object path existing at time of click: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); - clickPosition = _lastClickPosition.Value; bestObjectStatus = null; } } From 33e8a87fb2a85eafbc245382e87140031bcb971c Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Thu, 12 Dec 2024 21:38:21 -0500 Subject: [PATCH 28/31] Make sure click events are each sent on a separate frame --- .../BotSegments/ActionExplorationDriver.cs | 5 +- .../BotActions/KeyMomentMouseActionData.cs | 258 +++++++++--------- 2 files changed, 140 insertions(+), 123 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index 75c374dc..0d862f64 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -186,7 +186,10 @@ public void PauseExploring(int segmentNumber) RGDebug.LogInfo("ActionExplorationDriver - Paused Exploratory Actions"); } - ExplorationState = ExplorationState.PAUSED; + if (ExplorationState != ExplorationState.STOPPED) + { + ExplorationState = ExplorationState.PAUSED; + } } public void StopExploring(int segmentNumber) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index b135a4ef..68a77e0d 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -164,6 +164,90 @@ public void StartAction(int segmentNumber, Dictionary curren } } + private bool HandleUnClickAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) + { + // find the best object and un-click on it if possible + var unClickPaths = _preconditionNormalizedPaths[2]; + var unClickPreconditionMatches = BuildPreconditions(segmentNumber, currentTransforms, currentEntities, unClickPaths); + + var validateObjectMatches = true; + Vector2 clickPosition; + if (unClickPreconditionMatches.Length > 0) + { + var bestUnClickMatchResult = FindBestMatch(segmentNumber, _unClickAction, unClickPreconditionMatches, out _); + + if (bestUnClickMatchResult.HasValue) + { + + clickPosition = GetClickPositionForMatch(bestUnClickMatchResult.Value); + + // if our best match path isn't the same as the click path.. do some extra evaluation + // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not + if (_lastClickPosition.HasValue) + { + if (_lastClickPosition.Value.x >= bestUnClickMatchResult.Value.Item2.Item1 + && _lastClickPosition.Value.x <= bestUnClickMatchResult.Value.Item2.Item3 + && _lastClickPosition.Value.y >= bestUnClickMatchResult.Value.Item2.Item2 + && _lastClickPosition.Value.y <= bestUnClickMatchResult.Value.Item2.Item4) + { + RGDebug.LogDebug($"({segmentNumber}) Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on bounds overlaps to original path: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); + // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the + // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame + clickPosition = _lastClickPosition.Value; + validateObjectMatches = false; + } + else if (_previousOverlappingObjects.Count > 0) + { + // this can handle things where the un-click was recorded 'after' the element dis-appeared, but it is still there in the replay until the un-click happens... this is just a matter of how ui events and when we can observe during recording works + + // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) + // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button + // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen + // isn't important.. we want to un-click exactly where we clicked + if (_previousOverlappingObjects.Any(a => a == bestUnClickMatchResult.Value.Item1)) + { + RGDebug.LogDebug($"({segmentNumber}) Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on object path existing at time of click: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); + clickPosition = _lastClickPosition.Value; + validateObjectMatches = false; + } + } + } + } + else + { + // didn't find it.. this is where 'exploration' is going to start happening based on our result + error = $"No valid mouse un-click action object found for path:\n{unClickPaths[0].normalizedPath}"; + return false; + } + } + else + { + // there were no object paths on the un-click. assume it is at the same position as the click + if (_lastClickPosition.HasValue) + { + clickPosition = _lastClickPosition.Value; + } + else + { + error = $"No valid mouse un-click position... This is a code bug in Regression Games and should NOT happen"; + return false; + } + } + + var targetObjectPath = _unClickAction.clickedObjectNormalizedPaths.Length > 0 ? _unClickAction.clickedObjectNormalizedPaths[0] : null; + if (!DoActionForObjectAtPosition(segmentNumber, validateObjectMatches, _unClickAction, clickPosition, targetObjectPath, currentTransforms, out error)) + { + return false; + } + + error = null; + return true; + + } + + /** + * Handles the mouse inputs to each send their event on a different update loop pass, otherwise the UI event system fails to process correctly as it can't handle 2 mouse input events on the same update + */ public bool ProcessAction(int segmentNumber, Dictionary currentTransforms, Dictionary currentEntities, out string error) { if (_isDoneWaitOneFrame) @@ -197,94 +281,14 @@ public bool ProcessAction(int segmentNumber, Dictionary curr // wait until the right time, then do the un-click mouse action if (Time.unscaledTimeAsDouble >= _unClickTime) { - // find the best object and un-click on it if possible - var unClickPaths = _preconditionNormalizedPaths[2]; - var unClickPreconditionMatches = BuildPreconditions(segmentNumber, currentTransforms, currentEntities, unClickPaths); - - ObjectStatus bestObjectStatus = null; - Vector2 clickPosition; - if (unClickPreconditionMatches.Length > 0) + var worked = HandleUnClickAction(segmentNumber, currentTransforms, currentEntities, out error); + if (worked) { - var bestUnClickMatchResult = FindBestMatch(_unClickAction, unClickPreconditionMatches, out _); - - if (bestUnClickMatchResult.HasValue) - { - bestObjectStatus = bestUnClickMatchResult.Value.Item1; - - clickPosition = GetClickPositionForMatch(bestUnClickMatchResult.Value); - - // if our best match path isn't the same as the click path.. do some extra evaluation - // check if this is an un-click of a previous click.. if so we have some special cases to check to make sure we want to move the click position or not - if (_lastClickPosition.HasValue) - { - if (_lastClickPosition.Value.x >= bestUnClickMatchResult.Value.Item2.Item1 - && _lastClickPosition.Value.x <= bestUnClickMatchResult.Value.Item2.Item3 - && _lastClickPosition.Value.y >= bestUnClickMatchResult.Value.Item2.Item2 - && _lastClickPosition.Value.y <= bestUnClickMatchResult.Value.Item2.Item4) - { - RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on bounds overlaps to original path: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); - // if the click position was within the bounds of the un-click object, just use that same one... this is critical for cases where the - // clicked button is no longer present in the normalizedPaths list for the un-click... which happens based on how observation of the un-click occurs on a future frame - clickPosition = _lastClickPosition.Value; - if (bestObjectStatus.NormalizedPath != mouseActions[1].clickedObjectNormalizedPaths[0]) - { - bestObjectStatus = null; - } - } - else if (_previousOverlappingObjects.Count > 0) - { - // this can handle things where the un-click was recorded 'after' the element dis-appeared, but it is still there in the replay until the un-click happens... this is just a matter of how ui events and when we can observe during recording works - - // an even more special case... even though the bounds don't align.. the resolution could have changed (this happens a lot for bossroom menus when you resize and the 3d game objects scale differently than the UI) - // but on un-click.. the ui element isn't in the path list anymore so it tries to un-click on the door or some other background game object instead and misses the button - // so if we had this same object listed in the conditions for the prior click calculation.. then the original click already considered this and the scaling factor of the screen - // isn't important.. we want to un-click exactly where we clicked - if (_previousOverlappingObjects.Any(a => a == bestUnClickMatchResult.Value.Item1)) - { - RGDebug.LogDebug($"Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on object path existing at time of click: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); - clickPosition = _lastClickPosition.Value; - if (bestObjectStatus.NormalizedPath != mouseActions[1].clickedObjectNormalizedPaths[0]) - { - bestObjectStatus = null; - } - } - } - } - } - else - { - // didn't find it.. this is where 'exploration' is going to start happening based on our result - error = $"No valid mouse un-click action object found for path:\n{unClickPaths[0].normalizedPath}"; - return false; - } - } - else - { - // there were no object paths on the un-click. assume it is at the same position as the click - if (_lastClickPosition.HasValue) - { - clickPosition = _lastClickPosition.Value; - } - else - { - error = $"No valid mouse un-click position... This is a code bug in Regression Games and should NOT happen"; - return false; - } - } - - // only send the bestObjectStatus for filtering when NOT un-clicking at the original position - var targetObjectPath = _unClickAction.clickedObjectNormalizedPaths.Length > 0 ? _unClickAction.clickedObjectNormalizedPaths[0] : null; - if (!DoActionForObjectAtPosition(segmentNumber, _unClickAction, bestObjectStatus, clickPosition, targetObjectPath, currentTransforms, out error)) - { - return false; + _unClickAction = null; + _isDoneWaitOneFrame = true; } - _unClickAction = null; - _isDoneWaitOneFrame = true; - - error = null; - return true; - + return worked; } else { @@ -293,31 +297,49 @@ public bool ProcessAction(int segmentNumber, Dictionary curr return false; } } + else if (_lastClickPosition.HasValue) + { + // handling [1] index.. the click/un-click + if (!DoActionForObjectAtPosition(segmentNumber, true, mouseActions[1], _lastClickPosition.Value, mouseActions[1].clickedObjectNormalizedPaths[0], currentTransforms, out error)) + { + return false; + } + + if (mouseActions.Count > 2) + { + _unClickAction = mouseActions[2]; + // save off the time the unClick should happen here so mouse button holds work + var clickGapDelta = _unClickAction.startTime - mouseActions[1].startTime; + _unClickTime = mouseActions[1].startTime == 0d ? 0d : Time.unscaledTimeAsDouble + clickGapDelta; + } + else + { + // this was just a 2 action segment.. we're done + _unClickAction = null; + _unClickTime = 0d; + _isDoneWaitOneFrame = true; + } + + error = null; + return true; + } else { // handling [0] , [1] indexes.. the positioning and the click[length=3] or un-click action[length=2] // these have the same position.. so we can compute it just once // we have to be aware that you can get a segment with only a position and an un-click (length 2) .. so we have to be careful there + // we use the [1] index to find the paths we need to get the correct position - var movementAction = mouseActions[0]; - var clickAction = mouseActions[1]; var clickPaths = _preconditionNormalizedPaths[1]; var preconditionMatches = BuildPreconditions(segmentNumber, currentTransforms, currentEntities, clickPaths); - var bestMatchResult = FindBestMatch(clickAction, preconditionMatches, out var overlappingObjects); + var bestMatchResult = FindBestMatch(segmentNumber, mouseActions[1], preconditionMatches, out var overlappingObjects); if (bestMatchResult.HasValue) { - var bestObjectStatus = bestMatchResult.Value.Item1; var clickPosition = GetClickPositionForMatch(bestMatchResult.Value); - - if (!DoActionForObjectAtPosition(segmentNumber, movementAction, bestObjectStatus, clickPosition,clickAction.clickedObjectNormalizedPaths[0], currentTransforms, out error)) - { - return false; - } - - if (!DoActionForObjectAtPosition(segmentNumber, clickAction, bestObjectStatus, clickPosition,clickAction.clickedObjectNormalizedPaths[0], currentTransforms, out error)) + if (!DoActionForObjectAtPosition(segmentNumber, true, mouseActions[0], clickPosition, mouseActions[1].clickedObjectNormalizedPaths[0], currentTransforms, out error)) { return false; } @@ -326,20 +348,6 @@ public bool ProcessAction(int segmentNumber, Dictionary curr // on the 'click'.. save the position _lastClickPosition = clickPosition; - if (mouseActions.Count > 2) - { - _unClickAction = mouseActions[2]; - // save off the time the unClick should happen here so mouse button holds work - _unClickTime = clickAction.startTime == 0d ? 0d : Time.unscaledTimeAsDouble + _unClickAction.startTime - clickAction.startTime; - } - else - { - // this was just a position and un-click segment.. we're done - _unClickAction = null; - _unClickTime = 0d; - _isDoneWaitOneFrame = true; - } - error = null; return true; } @@ -359,13 +367,17 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float { // we started with clicking the center.. but this was limiting //clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); - // instead... make the click-position a random position within the bounds + // instead... make the click-position a random position within the bounds.. // 1. for more variability in testing // 2. to get around cases where we were say clicking on a floor tile, but there is something on that floor tile and we wanted to click on the open space of the floor tile // on the next attempt, it picks a new position to try thus giving us a better chance of passing // TODO (REG-2223): Future: Can we capture the relativistic offset click position where we hit a world space object so that we can try to re-click on that same offset given its new world position ??? // This would allow us to know that we clicked about X far into this floor tile and replicate that positioning regardless of the actual worldspace positioning in the replay... - Vector2 result = new Vector2(Random.Range(bestMatchResult.Item2.Item1, bestMatchResult.Item2.Item3), Random.Range(bestMatchResult.Item2.Item2, bestMatchResult.Item2.Item4)); + // Vector2 result = new Vector2(Random.Range(bestMatchResult.Item2.Item1, bestMatchResult.Item2.Item3), Random.Range(bestMatchResult.Item2.Item2, bestMatchResult.Item2.Item4)); + //we pick in the central 80% of the height/width of the object to avoid edge misses which were happening on UI buttons in initial testing + var rangeWidth10Percent = (bestMatchResult.Item2.Item3 - bestMatchResult.Item2.Item1) / 10; + var rangeHeight10Percent = (bestMatchResult.Item2.Item4 - bestMatchResult.Item2.Item2) / 10; + Vector2 result = new Vector2(Random.Range(bestMatchResult.Item2.Item1+rangeWidth10Percent, bestMatchResult.Item2.Item3-rangeWidth10Percent), Random.Range(bestMatchResult.Item2.Item2+rangeHeight10Percent, bestMatchResult.Item2.Item4-rangeHeight10Percent)); return result; } @@ -545,7 +557,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float return result; } - private (ObjectStatus, (float,float,float,float))? FindBestMatch(MouseInputActionData mouseAction, List<(ObjectStatus, (int[], int[]))>[] preconditionMatches, out List overlappingObjects) + private (ObjectStatus, (float,float,float,float))? FindBestMatch(int segmentNumber, MouseInputActionData mouseAction, List<(ObjectStatus, (int[], int[]))>[] preconditionMatches, out List overlappingObjects) { overlappingObjects = new List(); @@ -616,7 +628,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float var originalMaxX = maxX; var originalMaxY = maxY; - RGDebug.LogDebug($"Starting with bounds: ({minX}, {minY}),({maxX}, {maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} , target object: {preconditionMatch0.Item1.NormalizedPath}"); + RGDebug.LogDebug($"({segmentNumber}) Starting with bounds: ({minX}, {minY}),({maxX}, {maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} , target object: {preconditionMatch0.Item1.NormalizedPath}"); // now let's narrow down the screen space bounds more precisely based on all our preconditions for (var i = 1; i < preconditionMatches.Length; i++) { @@ -716,7 +728,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float if (pmIDidOverlap) { overlappingObjects.Add(preconditionMatchI.Item1); - RGDebug.LogDebug($"Tightened bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} - overlap with object path [{i}]: {preconditionMatchI.Item1.NormalizedPath}"); + RGDebug.LogDebug($"({segmentNumber}) Tightened bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} - overlap with object path [{i}]: {preconditionMatchI.Item1.NormalizedPath}"); } } } @@ -818,14 +830,15 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float return null; } - private bool DoActionForObjectAtPosition(int segmentNumber, MouseInputActionData mouseAction, ObjectStatus bestObjectStatus, Vector2 myClickPosition, string targetObjectPath, Dictionary currentTransforms, out string error) + private bool DoActionForObjectAtPosition(int segmentNumber, bool validateObjectMatches, MouseInputActionData mouseAction, Vector2 myClickPosition, string targetObjectPath, Dictionary currentTransforms, out string error) { - if (bestObjectStatus is TransformStatus) + + // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over + // our intended click item (iow.. it's there/ready, but obstructed) ... + // make sure our object is at the front of the list and thus at the closest Z depth at that point.. FindObjectsAtPosition handles z depth/etc sorting for us + // or that the item at the front of the list is on the same path tree as us (we might have clicked on the text part of the button instead of the button, but both still click the button) + if (validateObjectMatches) { - // cross check that the top level element we want to click is actually on top at the click position.. .this is to handle things like scene transitions with loading screens, or temporary popups on the screen over - // our intended click item (iow.. it's there/ready, but obstructed) ... - // make sure our object is at the front of the list and thus at the closest Z depth at that point.. FindObjectsAtPosition handles z depth/etc sorting for us - // or that the item at the front of the list is on the same path tree as us (we might have clicked on the text part of the button instead of the button, but both still click the button) var objectsAtClickPosition = MouseInputActionObserver.FindObjectsAtPosition(myClickPosition, currentTransforms.Values); // see if our object is 'first' or obstructed if (objectsAtClickPosition.Count > 0) @@ -844,7 +857,8 @@ private bool DoActionForObjectAtPosition(int segmentNumber, MouseInputActionData } } - RGDebug.LogInfo($"KeyMoment - Mouse Action at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {targetObjectPath}"); + + RGDebug.LogInfo($"({segmentNumber}) KeyMoment - Mouse Action at position: ({(int)myClickPosition.x}, {(int)myClickPosition.y}) on object path: {targetObjectPath}"); // perform the mouse action at the center of our new smallest bounds MouseEventSender.SendRawPositionMouseEvent(segmentNumber, myClickPosition, mouseAction.leftButton, mouseAction.middleButton, mouseAction.rightButton, mouseAction.forwardButton, mouseAction.backButton, mouseAction.scroll); From 403f9d02e90ae43d6c8cb7c2ec8d897e07488264 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Fri, 13 Dec 2024 08:49:05 -0500 Subject: [PATCH 29/31] Update KeyMomentMouseActionData.cs --- .../KeyMoments/BotActions/KeyMomentMouseActionData.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs index 68a77e0d..63c86f24 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs @@ -210,6 +210,12 @@ private bool HandleUnClickAction(int segmentNumber, Dictionary Date: Fri, 13 Dec 2024 10:52:07 -0500 Subject: [PATCH 30/31] Fixup package names/locations --- .../StateRecorder/BotSegments/ActionExplorationDriver.cs | 2 +- .../StateRecorder/BotSegments/BotSegmentsPlaybackController.cs | 2 +- .../BotSegments/JsonConverters/BotActionJsonConverter.cs | 2 +- .../BotSegments/Models/{ => BotActions}/KeyMoments.meta | 0 .../{ => BotActions}/KeyMoments/IKeyMomentExploration.cs | 2 +- .../{ => BotActions}/KeyMoments/IKeyMomentExploration.cs.meta | 0 .../KeyMoments}/KeyMomentMouseActionData.cs | 3 ++- .../KeyMoments}/KeyMomentMouseActionData.cs.meta | 0 .../BotSegments/Models/KeyMoments/BotActions.meta | 3 --- .../StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs | 2 +- .../Runtime/Scripts/StateRecorder/ScreenRecorder.cs | 2 -- 11 files changed, 7 insertions(+), 11 deletions(-) rename src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/{ => BotActions}/KeyMoments.meta (100%) rename src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/{ => BotActions}/KeyMoments/IKeyMomentExploration.cs (54%) rename src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/{ => BotActions}/KeyMoments/IKeyMomentExploration.cs.meta (100%) rename src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/{KeyMoments/BotActions => BotActions/KeyMoments}/KeyMomentMouseActionData.cs (99%) rename src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/{KeyMoments/BotActions => BotActions/KeyMoments}/KeyMomentMouseActionData.cs.meta (100%) delete mode 100644 src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions.meta diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs index 0d862f64..d913983f 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/ActionExplorationDriver.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using RegressionGames; using RegressionGames.StateRecorder.BotSegments.Models; -using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions; +using RegressionGames.StateRecorder.BotSegments.Models.BotActions.KeyMoments; using RegressionGames.StateRecorder.Models; using UnityEngine; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs index 446613d2..98835275 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/BotSegmentsPlaybackController.cs @@ -3,7 +3,7 @@ using System.IO; using Newtonsoft.Json; using RegressionGames.StateRecorder.BotSegments.Models; -using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments; +using RegressionGames.StateRecorder.BotSegments.Models.BotActions.KeyMoments; using RegressionGames.StateRecorder.Models; using StateRecorder.BotSegments; using StateRecorder.BotSegments.Models; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/JsonConverters/BotActionJsonConverter.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/JsonConverters/BotActionJsonConverter.cs index 8033f111..95070e27 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/JsonConverters/BotActionJsonConverter.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/JsonConverters/BotActionJsonConverter.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json.Linq; using RegressionGames.StateRecorder.BotSegments.Models; using RegressionGames.StateRecorder.BotSegments.Models.BotActions; -using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions; +using RegressionGames.StateRecorder.BotSegments.Models.BotActions.KeyMoments; namespace RegressionGames.StateRecorder.BotSegments.JsonConverters diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments.meta similarity index 100% rename from src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments.meta rename to src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments.meta diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/IKeyMomentExploration.cs similarity index 54% rename from src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs rename to src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/IKeyMomentExploration.cs index 1fdcc90f..4ccc6528 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/IKeyMomentExploration.cs @@ -1,4 +1,4 @@ -namespace RegressionGames.StateRecorder.BotSegments.Models.KeyMoments +namespace RegressionGames.StateRecorder.BotSegments.Models.BotActions.KeyMoments { public interface IKeyMomentExploration { diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/IKeyMomentExploration.cs.meta similarity index 100% rename from src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/IKeyMomentExploration.cs.meta rename to src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/IKeyMomentExploration.cs.meta diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/KeyMomentMouseActionData.cs similarity index 99% rename from src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs rename to src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/KeyMomentMouseActionData.cs index 63c86f24..97bf1e13 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/KeyMomentMouseActionData.cs @@ -10,7 +10,7 @@ // ReSharper disable InconsistentNaming -namespace RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions +namespace RegressionGames.StateRecorder.BotSegments.Models.BotActions.KeyMoments { [Serializable] public class PreconditionNormalizedPathData @@ -642,6 +642,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float var preconditionMatchesI = preconditionMatches[i]; if (preconditionMatchesI.Count > 0) { + // TODO: Currently this narrows the bounds.. but can cause you to actually get further from the original click position... i think we need to consider the 'most' overlapping match with the original for similar objects before choosing to narrow foreach (var preconditionMatchI in preconditionMatchesI) { var isInteractable = true; diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/KeyMomentMouseActionData.cs.meta similarity index 100% rename from src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions/KeyMomentMouseActionData.cs.meta rename to src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/KeyMomentMouseActionData.cs.meta diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions.meta b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions.meta deleted file mode 100644 index ddbcdd7b..00000000 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/KeyMoments/BotActions.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 539d8f195d62486fbf21dcb1c57af9be -timeCreated: 1733235018 \ No newline at end of file diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs index 590a1183..5dc801d3 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/KeyMoments/MouseKeyMomentEvaluator.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using RegressionGames.StateRecorder.BotSegments.Models; using RegressionGames.StateRecorder.BotSegments.Models.BotCriteria; -using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions; +using RegressionGames.StateRecorder.BotSegments.Models.BotActions.KeyMoments; using RegressionGames.StateRecorder.Models; namespace RegressionGames.StateRecorder.KeyMoments diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs index 3df009af..24e6e843 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/ScreenRecorder.cs @@ -9,14 +9,12 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; -using Newtonsoft.Json; using RegressionGames.ActionManager; using RegressionGames.CodeCoverage; using RegressionGames.RemoteOrchestration; using RegressionGames.StateRecorder.BotSegments.Models; using RegressionGames.StateRecorder.BotSegments.Models.BotActions; using RegressionGames.StateRecorder.BotSegments.Models.BotCriteria; -using RegressionGames.StateRecorder.BotSegments.Models.KeyMoments.BotActions; using RegressionGames.StateRecorder.Models; using StateRecorder.BotSegments; #if UNITY_EDITOR From cd521daecc13d5808ce9b6d9837c52794612db51 Mon Sep 17 00:00:00 2001 From: RG-nAmKcAz Date: Fri, 13 Dec 2024 12:42:30 -0500 Subject: [PATCH 31/31] Corrects logic for getting precondition path matches + fixes overlap considerations in shrinking bounds --- .../BotActions/InputPlaybackActionData.cs | 2 + .../KeyMoments/KeyMomentMouseActionData.cs | 150 ++++++++++-------- .../Scripts/StateRecorder/MouseEventSender.cs | 2 + 3 files changed, 92 insertions(+), 62 deletions(-) diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/InputPlaybackActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/InputPlaybackActionData.cs index ba8d4089..9a625aef 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/InputPlaybackActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/InputPlaybackActionData.cs @@ -153,6 +153,8 @@ public bool ProcessAction(int segmentNumber, Dictionary curr { // send event result = true; + + //TODO (REG-2237) : Replace with this finding the object and sending the raw position mouse event MouseEventSender.SendMouseEvent(segmentNumber, replayMouseInputEntry, null, null, currentTransforms, currentEntities); replayMouseInputEntry.Replay_IsDone = true; } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/KeyMomentMouseActionData.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/KeyMomentMouseActionData.cs index 97bf1e13..6601e235 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/KeyMomentMouseActionData.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/BotSegments/Models/BotActions/KeyMoments/KeyMomentMouseActionData.cs @@ -77,7 +77,7 @@ public class KeyMomentMouseActionData : IBotActionData, IKeyMomentExploration, I // These are all used to track the un-click work across multiple Update calls private double _unClickTime = 0d; private Vector2? _lastClickPosition = null; - private List _previousOverlappingObjects = new(); + private HashSet _previousOverlappingObjects = new(); private MouseInputActionData _unClickAction = null; public bool IsCompleted() @@ -174,7 +174,7 @@ private bool HandleUnClickAction(int segmentNumber, Dictionary 0) { - var bestUnClickMatchResult = FindBestMatch(segmentNumber, _unClickAction, unClickPreconditionMatches, out _); + var bestUnClickMatchResult = FindBestMatch(segmentNumber, _unClickAction, unClickPreconditionMatches); if (bestUnClickMatchResult.HasValue) { @@ -206,7 +206,7 @@ private bool HandleUnClickAction(int segmentNumber, Dictionary a == bestUnClickMatchResult.Value.Item1)) { - RGDebug.LogDebug($"({segmentNumber}) Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on object path existing at time of click: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); + RGDebug.LogDebug($"({segmentNumber}) Leaving mouse un-click action at previous action position: ({(int)_lastClickPosition.Value.x}, {(int)_lastClickPosition.Value.y}) based on object target being at the point of the original click: {bestUnClickMatchResult.Value.Item1.NormalizedPath}"); clickPosition = _lastClickPosition.Value; validateObjectMatches = false; } @@ -340,7 +340,7 @@ public bool ProcessAction(int segmentNumber, Dictionary curr var clickPaths = _preconditionNormalizedPaths[1]; var preconditionMatches = BuildPreconditions(segmentNumber, currentTransforms, currentEntities, clickPaths); - var bestMatchResult = FindBestMatch(segmentNumber, mouseActions[1], preconditionMatches, out var overlappingObjects); + var bestMatchResult = FindBestMatch(segmentNumber, mouseActions[1], preconditionMatches); if (bestMatchResult.HasValue) { @@ -351,7 +351,7 @@ public bool ProcessAction(int segmentNumber, Dictionary curr return false; } - _previousOverlappingObjects = overlappingObjects; + _previousOverlappingObjects = bestMatchResult.Value.Item3; // on the 'click'.. save the position _lastClickPosition = clickPosition; @@ -370,7 +370,7 @@ public bool ProcessAction(int segmentNumber, Dictionary curr return false; } - private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float)) bestMatchResult) + private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float), HashSet) bestMatchResult) { // we started with clicking the center.. but this was limiting //clickPosition = new Vector2(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); @@ -410,7 +410,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float { possibleTransformToClick.TokenizedObjectPath ??= PreconditionNormalizedPathData.TokenizeObjectPath(possibleTransformToClick.NormalizedPath); - // this should nearly always be smaller than the # of possibleTransformToClick + // this should nearly always be smaller than the # of possibleTransformToClick as far as optimizing the nested loops goes for (var j = 0; j < preconditionsLength; j++) { var precondition = preConditionNormalizedPaths[j]; @@ -426,15 +426,15 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float } preconditionMatches[j].Add((possibleTransformToClick, (null, null))); - break; // the for + break; // the for - we got an exact match.. stop processing this index } else { // tokenized matching logic if (preconditionMatches[j].Count > 0 && preconditionMatches[j][0].Item2.Item1 == null) { - // we already have exact matches.. ignore the tokenized ones - break; // the for + // we already have exact matches.. ignore the tokenized ones for this index + continue; // the for - this still might be an exact match for another index } var tokenMatches = EvaluateTokenMatches(precondition.tokenData, possibleTransformToClick.TokenizedObjectPath); @@ -464,7 +464,7 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float preconditionMatches[j].Add((possibleTransformToClick, tokenMatches)); } - break; // the for + continue; // the for - might still be an exact match for another index } } } @@ -564,24 +564,27 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float return result; } - private (ObjectStatus, (float,float,float,float))? FindBestMatch(int segmentNumber, MouseInputActionData mouseAction, List<(ObjectStatus, (int[], int[]))>[] preconditionMatches, out List overlappingObjects) + /** + * Finds the best object on which to click given the precondition matches for the path names + the mouse location. + * Returns a tuple of (objectStatus, (minX,minY,maxY,maxY) bounds of that object, HashSet of objects found to be overlapping that object) + */ + private (ObjectStatus, (float,float,float,float), HashSet)? FindBestMatch(int segmentNumber, MouseInputActionData mouseAction, List<(ObjectStatus, (int[], int[]))>[] preconditionMatches) { - overlappingObjects = new List(); - // now that we have all the precondition matches mapped out, let's see if we have the leftmost object.. if (preconditionMatches.Length > 0 && preconditionMatches[0].Count > 0) { // yay.. we found something(s).. figure out which one of them is the 'best' based on number of overlapping matches - // for each of the left most preconditionMatches[0] .. go through each of other precondition matches - // we will compute the 'smallest' click bounds for these overlaps as we go - var matchResults = new List<(ObjectStatus, (float, float, float, float))>(preconditionMatches[0].Count); + // for each of the objects matching the left most preconditionMatches[0] .. go through each of other precondition matches in the list and evaluate which of them have an overlap + // use all of those information to find the best match based on proximity to the original click + bounds area + var matchResults = new List<(ObjectStatus, (float, float, float, float), HashSet)>(preconditionMatches[0].Count); var screenWidth = Screen.width; var screenHeight = Screen.height; foreach (var preconditionMatch0 in preconditionMatches[0]) { + var myOverlappingObjects = new HashSet(); var isPreconditionMatch0Interactable = true; // now that we have a candidate match ... first let's make sure it's ready to be clicked // visible (already true by the time we get here)/active-enabled(we already know that from a canvas perspective this is visible, but need to check UI component info) @@ -642,7 +645,14 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float var preconditionMatchesI = preconditionMatches[i]; if (preconditionMatchesI.Count > 0) { - // TODO: Currently this narrows the bounds.. but can cause you to actually get further from the original click position... i think we need to consider the 'most' overlapping match with the original for similar objects before choosing to narrow + // consider the 'most' overlapping match with the original before processing bounds narrowing + // this allows us to prefer shrinking the bounds based on the most overlapping objects with the current object... this keeps us picking the best objects to narrow with in cases where game UIs + // have large degree of canvas or bounds overlaps + + // list of ObjectStatus, overlap-area, (minX,minY,maxX,maxY) + var sortedOverlappingPreconditionMatchesI = new List<(ObjectStatus, float, (float,float,float,float))>(); + + // sort the objects by those that overlap the most foreach (var preconditionMatchI in preconditionMatchesI) { var isInteractable = true; @@ -657,17 +667,18 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float var selectables = theTransform.GetComponents(); if (selectables.Length > 0) { - // make sure 1 is interactable - if none leave isInteratable == true + // make sure 1 is interactable - if none leave isInteractable == true isInteractable = selectables.Any(a => a.interactable); } } } + if (isInteractable) { - var pmIDidOverlap = false; // ReSharper disable once PossibleInvalidOperationException - already filtered in BuildPreconditions to only have entries with valid visible bounds var newBounds = preconditionMatchI.Item1.screenSpaceBounds.Value; + // for world space object, narrow the bounds to its collider if (preconditionMatchI.Item1.worldSpaceBounds != null && preconditionMatchI.Item1 is TransformStatus preconditionMatchITs) { var preconditionMatchITransform = preconditionMatchITs.Transform; @@ -685,67 +696,82 @@ private Vector2 GetClickPositionForMatch((ObjectStatus, (float,float,float,float } } - // adjust in for the min to ensure we don't miss a click - var newMinX = newBounds.min.x; - var newMinY = newBounds.min.y; - // adjust in for the max to ensure we don't miss a click - var newMaxX = newBounds.max.x; - var newMaxY = newBounds.max.y; + var areaMinX = Mathf.Max(newBounds.min.x, originalMinX); + var areaMaxX = Mathf.Min(newBounds.max.x, originalMaxX); + var areaMinY = Mathf.Max(newBounds.min.y, originalMinY); + var areaMaxY = Mathf.Min(newBounds.max.y, originalMaxY); - if (newMinX >= minX && newMinX <= maxX) + var areaOfOverlap = (areaMaxX - areaMinX) * (areaMaxY - areaMinY); + if (areaOfOverlap > 0) { - minX = newMinX; + sortedOverlappingPreconditionMatchesI.Add((preconditionMatchI.Item1, areaOfOverlap, (areaMinX, areaMinY, areaMaxX, areaMaxY))); } - - if (newMinY >= minY && newMinY <= maxY) + else { - minY = newMinY; + RGDebug.LogVerbose($"Excluding object with negative area of overlap"); } + } + } - if (newMaxX >= minX && newMaxX <= maxX) - { - maxX = newMaxX; - } + sortedOverlappingPreconditionMatchesI.Sort((a, b) => + { + // sort biggest overlap numbers to the front + if (a.Item2 < b.Item2) + { + return 1; + } - if (newMaxY >= minY && newMaxY <= maxY) - { - maxY = newMaxY; - } + // we're comparing multiplied floats.. don't really need to care about the zillionth of a percent chance they are equal based on what we're doing with the data + return -1; + }); + // then process those results narrowing bounds until we miss, then stop + foreach (var sortedOverlappingPreconditionMatch in sortedOverlappingPreconditionMatchesI) + { + // need to add all the overlaps + myOverlappingObjects.Add(sortedOverlappingPreconditionMatch.Item1); - if (newMinX >= originalMinX && newMinX <= originalMaxX) - { - pmIDidOverlap = true; - } + var didShrinkBounds = false; + var newMinX = sortedOverlappingPreconditionMatch.Item3.Item1; + var newMaxX = sortedOverlappingPreconditionMatch.Item3.Item3; - if (newMinY >= originalMinY && newMinY <= originalMaxY) - { - pmIDidOverlap = true; - } + var newMinY = sortedOverlappingPreconditionMatch.Item3.Item2; + var newMaxY = sortedOverlappingPreconditionMatch.Item3.Item4; - if (newMaxX >= originalMinX && newMaxX <= originalMaxX) - { - pmIDidOverlap = true; - } + if (newMinX > minX && newMinX < maxX) + { + didShrinkBounds = true; + minX = newMinX; + } - if (newMaxY >= originalMinY && newMaxY <= originalMaxY) - { - pmIDidOverlap = true; - } + if (newMinY > minY && newMinY < maxY) + { + didShrinkBounds = true; + minY = newMinY; + } - if (pmIDidOverlap) - { - overlappingObjects.Add(preconditionMatchI.Item1); - RGDebug.LogDebug($"({segmentNumber}) Tightened bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} - overlap with object path [{i}]: {preconditionMatchI.Item1.NormalizedPath}"); - } + if (newMaxX > minX && newMaxX < maxX) + { + didShrinkBounds = true; + maxX = newMaxX; + } + + if (newMaxY > minY && newMaxY < maxY) + { + didShrinkBounds = true; + maxY = newMaxY; } - } + if (didShrinkBounds) + { + RGDebug.LogDebug($"({segmentNumber}) Tightened bounds: ({(int)minX}, {(int)minY}),({(int)maxX}, {(int)maxY}) for object path: {mouseAction.clickedObjectNormalizedPaths[0]} - overlap with object path [{i}]: {sortedOverlappingPreconditionMatch.Item1.NormalizedPath}"); + } + } } } - matchResults.Add((preconditionMatch0.Item1, (minX, minY, maxX, maxY))); + matchResults.Add((preconditionMatch0.Item1, (minX, minY, maxX, maxY), myOverlappingObjects)); } } diff --git a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseEventSender.cs b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseEventSender.cs index c0f2e12f..28f4ed17 100644 --- a/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseEventSender.cs +++ b/src/gg.regression.unity.bots/Runtime/Scripts/StateRecorder/MouseEventSender.cs @@ -343,6 +343,7 @@ public static void SendRawPositionMouseEvent(int replaySegment, Vector2 normaliz } + //TODO (REG-2237): Update/remove this based on KeyMomentMouseActionData Algorithm public static void SendMouseEvent(int replaySegment, MouseInputActionData mouseInput, Dictionary priorTransforms, Dictionary priorEntities, Dictionary transforms, Dictionary entities) { var clickObjectResult = FindBestClickObject(Camera.main, mouseInput, priorTransforms, priorEntities, transforms, entities); @@ -427,6 +428,7 @@ private static void SendMouseEventLegacy(Vector2 position, Vector2 delta, Vector // Finds the best object to adjust our click position to for a given mouse input // Uses the exact path for UI clicks, but the normalized path for world space clicks // Returns (the object, whether it was world space, the suggested mouse position) + //TODO (REG-2237): Remove/Replace this with the newer algorithm from KeyMomentMouseActionData private static (ObjectStatus, bool, Vector2, IEnumerable) FindBestClickObject(Camera mainCamera, MouseInputActionData mouseInput, Dictionary priorTransforms, Dictionary priorEntities, Dictionary transforms, Dictionary entities) {