diff --git a/Paths/Assets/Scenes/Default.unity b/Paths/Assets/Scenes/Default.unity index 90f6a17..5f9ad67 100644 --- a/Paths/Assets/Scenes/Default.unity +++ b/Paths/Assets/Scenes/Default.unity @@ -123,6 +123,410 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &17751484 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 17751485} + - component: {fileID: 17751487} + - component: {fileID: 17751486} + m_Layer: 5 + m_Name: Text (TMP) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &17751485 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 17751484} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 229754413} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &17751486 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 17751484} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Button + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4281479730 + m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 24 + m_fontSizeBase: 24 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 1 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &17751487 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 17751484} + m_CullTransparentMesh: 0 +--- !u!1 &229754412 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 229754413} + - component: {fileID: 229754416} + - component: {fileID: 229754415} + - component: {fileID: 229754414} + m_Layer: 5 + m_Name: Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &229754413 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 229754412} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 17751485} + m_Father: {fileID: 1155758495} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 70.714, y: -12.815} + m_SizeDelta: {x: 141.42737, y: 25.629944} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &229754414 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 229754412} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 229754415} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &229754415 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 229754412} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &229754416 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 229754412} + m_CullTransparentMesh: 0 +--- !u!1 &344474894 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 344474895} + - component: {fileID: 344474897} + - component: {fileID: 344474896} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &344474895 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 344474894} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.65, y: 0.65, z: 1} + m_Children: [] + m_Father: {fileID: 831361552} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 35, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &344474896 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 344474894} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &344474897 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 344474894} + m_CullTransparentMesh: 0 +--- !u!1 &422608066 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 422608067} + - component: {fileID: 422608069} + - component: {fileID: 422608068} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &422608067 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 422608066} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1127471362} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.25} + m_AnchorMax: {x: 1, y: 0.75} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &422608068 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 422608066} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.26415092, g: 0.26415092, b: 0.26415092, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &422608069 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 422608066} + m_CullTransparentMesh: 0 --- !u!1 &519420028 GameObject: m_ObjectHideFlags: 0 @@ -206,6 +610,81 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &542076403 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 542076404} + - component: {fileID: 542076406} + - component: {fileID: 542076405} + m_Layer: 5 + m_Name: Fill + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &542076404 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 542076403} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2071673332} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 10, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &542076405 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 542076403} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.16981131, g: 0.16981131, b: 0.16981131, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &542076406 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 542076403} + m_CullTransparentMesh: 0 --- !u!850595691 &628914115 LightingSettings: m_ObjectHideFlags: 0 @@ -267,7 +746,7 @@ LightingSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 ---- !u!1 &1373243071 +--- !u!1 &831361551 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -275,47 +754,340 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1373243072} - - component: {fileID: 1373243073} - m_Layer: 0 - m_Name: Game Manager + - component: {fileID: 831361552} + m_Layer: 5 + m_Name: Handle Slide Area m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &1373243072 -Transform: +--- !u!224 &831361552 +RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1373243071} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_GameObject: {fileID: 831361551} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 0} - m_RootOrder: 3 + m_Children: + - {fileID: 344474895} + m_Father: {fileID: 1127471362} + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &1373243073 + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &938674168 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 938674171} + - component: {fileID: 938674170} + - component: {fileID: 938674169} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &938674169 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1373243071} + m_GameObject: {fileID: 938674168} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: ca142250b9964eb1adf66ac7c5cc3e3e, type: 3} + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} m_Name: m_EditorClassIdentifier: - mainCamera: {fileID: 519420031} - gridObject: {fileID: 2092623184} + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &938674170 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 938674168} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &938674171 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 938674168} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1127471361 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1127471362} + - component: {fileID: 1127471363} + m_Layer: 5 + m_Name: ProgressSlider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1127471362 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1127471361} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 422608067} + - {fileID: 2071673332} + - {fileID: 831361552} + m_Father: {fileID: 1155758495} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: -0} + m_AnchoredPosition: {x: 0.2999878, y: 11} + m_SizeDelta: {x: -22.869019, y: 35} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1127471363 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1127471361} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1502f97daf7446a48b977b5b7fa485ad, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 0.5660378, g: 0.5660378, b: 0.5660378, a: 1} + m_HighlightedColor: {r: 0.7735849, g: 0.7735849, b: 0.7735849, a: 1} + m_PressedColor: {r: 0.3301887, g: 0.3301887, b: 0.3301887, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 344474896} + m_FillRect: {fileID: 542076404} + m_HandleRect: {fileID: 344474895} + m_Direction: 0 + m_MinValue: 0 + m_MaxValue: 1 + m_WholeNumbers: 0 + m_Value: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &1155758491 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1155758495} + - component: {fileID: 1155758494} + - component: {fileID: 1155758493} + - component: {fileID: 1155758492} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1155758492 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1155758491} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &1155758493 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1155758491} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!223 &1155758494 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1155758491} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 25 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &1155758495 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1155758491} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 1127471362} + - {fileID: 1998222258} + - {fileID: 229754413} + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &1378668893 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1378668895} + - component: {fileID: 1378668894} + m_Layer: 0 + m_Name: UIController + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1378668894 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1378668893} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fd9c76568dd84a92b068dd1e622bd101, type: 3} + m_Name: + m_EditorClassIdentifier: + progressSlider: {fileID: 1127471363} gridController: {fileID: 2092623185} + mainCamera: {fileID: 519420031} debugText: {fileID: 1436240699} + gridObject: {fileID: 2092623184} + clampIncrement: 1.25 speed: 2500 - clampIncrement: 2 +--- !u!4 &1378668895 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1378668893} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1.0482129, y: 0.49563652, z: -32.86509} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1436240698 GameObject: m_ObjectHideFlags: 0 @@ -478,7 +1250,7 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 4 + 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} @@ -577,6 +1349,117 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1998222257 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1998222258} + - component: {fileID: 1998222260} + - component: {fileID: 1998222259} + m_Layer: 5 + m_Name: Panel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1998222258 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1998222257} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1155758495} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: -297.2, y: 123.3} + m_SizeDelta: {x: -683.3771, y: -359.0529} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1998222259 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1998222257} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.392} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1998222260 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1998222257} + m_CullTransparentMesh: 0 +--- !u!1 &2071673331 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2071673332} + m_Layer: 5 + m_Name: Fill Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2071673332 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2071673331} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 542076404} + m_Father: {fileID: 1127471362} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.25} + m_AnchorMax: {x: 1, y: 0.75} + m_AnchoredPosition: {x: -5, y: 0} + m_SizeDelta: {x: -20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &2092623184 GameObject: m_ObjectHideFlags: 0 @@ -609,8 +1492,8 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: gridMaterial: {fileID: 2100000, guid: 780d53bea4df7b0418e9c8ed8afd6410, type: 2} - width: 10 - height: 10 + width: 60 + height: 34 --- !u!23 &2092623186 MeshRenderer: m_ObjectHideFlags: 0 diff --git a/Paths/Assets/Scripts/Algorithms/AStar.cs b/Paths/Assets/Scripts/Algorithms/AStar.cs index 1c395ad..7f56aa7 100644 --- a/Paths/Assets/Scripts/Algorithms/AStar.cs +++ b/Paths/Assets/Scripts/Algorithms/AStar.cs @@ -3,18 +3,18 @@ using UnityEngine; namespace Algorithms { public class AStar : IPathfinding { - private NodeGrid _nodeGrid; + public NodeGrid NodeGrid { get; } private Stack _path; private List _openList; private List _closedList; - public ChangeController ChangeController { get; private set; } + public ChangeController ChangeController { get; } public Vector2Int Start { get; private set; } public Vector2Int End { get; private set; } public AStar(NodeGrid nodeGrid) { - this._nodeGrid = nodeGrid; + NodeGrid = nodeGrid; ChangeController = new ChangeController(nodeGrid.RenderNodeTypes()); } @@ -25,8 +25,8 @@ namespace Algorithms { ChangeController.AddChange(new Change(start.x, start.y, GridNodeType.Start, GridNodeType.Empty)); ChangeController.AddChange(new Change(end.x, end.y, GridNodeType.End, GridNodeType.Empty)); - Node startNode = _nodeGrid.Grid[start.x, start.y]; - Node endNode = _nodeGrid.Grid[end.x, end.y]; + Node startNode = NodeGrid.Grid[start.x, start.y]; + Node endNode = NodeGrid.Grid[end.x, end.y]; _path = new Stack(); _openList = new List(); @@ -39,19 +39,23 @@ namespace Algorithms { _openList.Add(startNode); while (_openList.Count != 0) { + // take the first node out (lowest F score) current = _openList[0]; _openList.RemoveAt(0); + // add it to closed list & mark current.State = NodeState.Closed; ChangeController.AddChange(new Change( current.Position.x, current.Position.y, GridNodeType.Expanded, GridNodeType.Seen)); _closedList.Add(current); + // exit if this is the end node if (current.Position == endNode.Position) break; - Node[] adjacentNodes = this._nodeGrid.GetAdjacentNodesArray(current); + // look at all adjacent nodes and add them to the open list if possible + Node[] adjacentNodes = this.NodeGrid.GetAdjacentNodesArray(current); for (int i = 0; i < adjacentNodes.Length; i++) { Node node = adjacentNodes[i]; if (node != null && node.State == NodeState.None && node.Walkable) { @@ -60,6 +64,7 @@ namespace Algorithms { node.DistanceToTarget = NodeGrid.Manhattan(node, endNode); node.Cost = node.Weight + node.Parent.Cost; + // mark as open node.State = NodeState.Open; ChangeController.AddChange(new Change(node.Position.x, node.Position.y, GridNodeType.Seen, GridNodeType.Empty)); @@ -77,19 +82,46 @@ namespace Algorithms { return null; } - // Fix start position being overriden - ChangeController.RemovePositions(start, 1); + // if all good, return path Node temp = _closedList[_closedList.IndexOf(current)]; if (temp == null) return null; do { _path.Push(temp); - ChangeController.AddChange(new Change(temp.Position.x, temp.Position.y, GridNodeType.Path, GridNodeType.Expanded)); + ChangeController.AddChange(new Change(temp.Position.x, temp.Position.y, GridNodeType.Path, + GridNodeType.Expanded)); temp = temp.Parent; } while (temp != null && !temp.Equals(startNode)); + // Fix start and end position being overriden + // TODO: Look into using a proper fix for this instead of a 'patch'. + ChangeController.RemovePositions(start, 1); + ChangeController.RemovePositions(end, 3); + + return _path; } + + /// + /// Attempts to clean the NodeGrid of all edits made to heuristic values and such, fast. + /// This is done by clearing the open and closed list, and for each node, resetting them (clearing heuristic + /// values and setting state back to it's default). + /// + public void Cleanup() { + while (_openList.Count > 0) { + Node node = _openList[0]; + _openList.RemoveAt(0); + + node.Reset(); + } + + while (_closedList.Count > 0) { + Node node = _closedList[0]; + _closedList.RemoveAt(0); + + node.Reset(); + } + } } } \ No newline at end of file diff --git a/Paths/Assets/Scripts/Algorithms/Node.cs b/Paths/Assets/Scripts/Algorithms/Node.cs index faab057..ae79d90 100644 --- a/Paths/Assets/Scripts/Algorithms/Node.cs +++ b/Paths/Assets/Scripts/Algorithms/Node.cs @@ -37,6 +37,16 @@ namespace Algorithms { Walkable = walkable; } + /// + /// Resets this Node back to it's assumed default values. + /// + public void Reset() { + Parent = null; + DistanceToTarget = null; + Cost = 1; + State = NodeState.None; + } + public override bool Equals(object obj) { return obj is Node node && Position.Equals(node.Position); } diff --git a/Paths/Assets/Scripts/Algorithms/NodeGrid.cs b/Paths/Assets/Scripts/Algorithms/NodeGrid.cs index fdb059d..76f0c0c 100644 --- a/Paths/Assets/Scripts/Algorithms/NodeGrid.cs +++ b/Paths/Assets/Scripts/Algorithms/NodeGrid.cs @@ -37,8 +37,15 @@ namespace Algorithms { Width = this.Grid.GetLength(1); } + /// + /// Returns adjacent Node objects in each of the cardinal directions. + /// Only valid nodes will be included. Invalid positions will return 0 nodes. + /// + /// The node from which adjacents will be found. + /// A length 4 or less list containing nodes. + /// public List GetAdjacentNodesList(Node node) { - List temp = new List(); + List temp = new List(4); int col = node.Position.x; int row = node.Position.y; @@ -51,11 +58,29 @@ namespace Algorithms { return temp; } + /// + /// Returns True if a Vector2Int position is within the grid's boundaries. + /// + /// A Vector2Int coordinate position. + /// True if valid and real (a node with the same position exists). + public bool IsValid(Vector2Int position) { + return position.x >= 0 && position.y >= 0 && position.x < Width && position.y < Height; + } + + /// + /// Returns a 1D Array describing the valid nearby nodes. + /// Invalid nodes will be represented by null. + /// Currently only returns nodes in cardinal directions (no diagonals). + /// Only valid positions within the grid are returned. + /// + /// A valid position on the grid. + /// A 4 length array containing valid nodes in each of the cardinal directions. + /// public Node[] GetAdjacentNodesArray(Node node) { int col = node.Position.x; int row = node.Position.y; - return new [] { + return new[] { row + 1 < Height ? Grid[col, row + 1] : null, row - 1 >= 0 ? Grid[col, row - 1] : null, col - 1 >= 0 ? Grid[col - 1, row] : null, @@ -139,7 +164,7 @@ namespace Algorithms { } } - public GridNodeType[,] RenderNodeTypes() { + public GridNodeType[,] RenderNodeTypes(Vector2Int? start = null, Vector2Int? end = null) { GridNodeType[,] nodeTypeGrid = new GridNodeType[Grid.GetLength(0), Grid.GetLength(1)]; for (int x = 0; x < Grid.GetLength(0); x++) { @@ -148,6 +173,13 @@ namespace Algorithms { } } + // Start / End node addition + if (start.HasValue) + nodeTypeGrid[start.Value.x, start.Value.y] = GridNodeType.Start; + + if (end.HasValue) + nodeTypeGrid[end.Value.x, end.Value.y] = GridNodeType.End; + return nodeTypeGrid; } } diff --git a/Paths/Assets/Scripts/Change.cs b/Paths/Assets/Scripts/Change.cs index 1cf7546..d8d5ba7 100644 --- a/Paths/Assets/Scripts/Change.cs +++ b/Paths/Assets/Scripts/Change.cs @@ -1,4 +1,8 @@ -public readonly struct Change { +/// +/// A Change struct represents a Change in the grid's rendering. +/// This struct remembers the original and new GridNodeType, the time it was recorded and of course the relevant position. +/// +public readonly struct Change { public readonly int X; public readonly int Y; public readonly GridNodeType New; diff --git a/Paths/Assets/Scripts/ChangeController.cs b/Paths/Assets/Scripts/ChangeController.cs index 1f084b9..2824524 100644 --- a/Paths/Assets/Scripts/ChangeController.cs +++ b/Paths/Assets/Scripts/ChangeController.cs @@ -9,16 +9,16 @@ public class ChangeController { private readonly GridNodeType[,] _initial; public GridNodeType[,] Current { get; private set; } public bool[,] DirtyFlags { get; private set; } - public int Index { get; private set; } + public int CurrentChangeIndex { get; private set; } private readonly List _changes; public int Count => _changes.Count; - public Change CurrentChange => _changes[Index]; - public double CurrentRuntime => _changes[Index].Time - _changes[0].Time; + public Change CurrentChange => _changes[CurrentChangeIndex]; + public double CurrentRuntime => _changes[CurrentChangeIndex].Time - _changes[0].Time; public ChangeController(GridNodeType[,] initial) { _initial = initial; Current = initial; - Index = -1; + CurrentChangeIndex = -1; _changes = new List(); DirtyFlags = new bool[initial.GetLength(0), initial.GetLength(1)]; SetDirty(); @@ -64,7 +64,7 @@ public class ChangeController { /// - /// Move the ChangeController's current index forward by index. + /// Move the ChangeController's current index forward by a certain number of times. /// Positive values only, wil only result in forward movement (if any). /// /// The number of times to move forward. @@ -73,14 +73,14 @@ public class ChangeController { throw new ArgumentOutOfRangeException(nameof(n)); for (int i = 0; i < n; i++) { - Change cur = _changes[++Index]; + Change cur = _changes[++CurrentChangeIndex]; Current[cur.X, cur.Y] = cur.New; SetDirty(new Vector2Int(cur.X, cur.Y)); } } /// - /// Move the ChangeController's current index backward by index. + /// Move the ChangeController's current index backward by a certain number of times. /// Positive values only, will only result in backward movement (if any). /// /// The number of times to move backward. @@ -88,11 +88,11 @@ public class ChangeController { if (n < 0) throw new ArgumentOutOfRangeException(nameof(n)); - if (n > 5 && Index - n == 0) + if (n > 5 && CurrentChangeIndex - n == 0) Reset(); // resetting by copying values instead of mutating might be easier. else { for (int i = 0; i < n; i++) { - Change cur = _changes[--Index]; + Change cur = _changes[CurrentChangeIndex--]; // post decrement as we apply the current Change's old, not the previous Current[cur.X, cur.Y] = cur.Old; SetDirty(new Vector2Int(cur.X, cur.Y)); } @@ -124,7 +124,7 @@ public class ChangeController { $"Cannot move to change index {index}. Only indexes from 0 to {_changes.Count - 1} are valid."); // diff & move to - int diff = index - Index; + int diff = index - CurrentChangeIndex; if (diff != 0) // prefer resetting if index is 0 and it needs to move at least some. if (index == 0 && diff > 5) diff --git a/Paths/Assets/Scripts/CustomSlider.cs b/Paths/Assets/Scripts/CustomSlider.cs new file mode 100644 index 0000000..5ecac5f --- /dev/null +++ b/Paths/Assets/Scripts/CustomSlider.cs @@ -0,0 +1,23 @@ +using UnityEngine.EventSystems; +using UnityEngine.UI; + +/// +/// A simple custom slider implementation that adds a single boolean field; IsPressed. +/// +public class CustomSlider: Slider { + /// + /// Whether or not the Slider is currently being pressed on by the Left Click mouse button. + /// + public new bool IsPressed { get; private set; } + + public override void OnPointerDown(PointerEventData eventData) + { + base.OnPointerDown(eventData); + IsPressed = true; + } + + public override void OnPointerUp(PointerEventData eventData) { + base.OnPointerUp(eventData); + IsPressed = false; + } +} \ No newline at end of file diff --git a/Paths/Assets/Scripts/CustomSlider.cs.meta b/Paths/Assets/Scripts/CustomSlider.cs.meta new file mode 100644 index 0000000..6df6158 --- /dev/null +++ b/Paths/Assets/Scripts/CustomSlider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1502f97daf7446a48b977b5b7fa485ad +timeCreated: 1606327381 \ No newline at end of file diff --git a/Paths/Assets/Scripts/GridController.cs b/Paths/Assets/Scripts/GridController.cs index 60558e3..d9a0035 100644 --- a/Paths/Assets/Scripts/GridController.cs +++ b/Paths/Assets/Scripts/GridController.cs @@ -25,9 +25,11 @@ public class GridController : MonoBehaviour { private static readonly int Values = Shader.PropertyToID("_values"); private static readonly int GridWidth = Shader.PropertyToID("_GridWidth"); private static readonly int GridHeight = Shader.PropertyToID("_GridHeight"); + public Vector2Int Size => new Vector2Int(width, height); private void Start() { _values = new int[width * height]; + // TODO: Decide at some point how to improve how the ComputerBuffer's size is allocated. _buffer = new ComputeBuffer((int) Mathf.Pow(2048, 2), 4); // Update all Shader properties @@ -130,4 +132,32 @@ public class GridController : MonoBehaviour { public int GetIndex(int x, int y) { return width * y + x; } + + /// + /// Translate a world position to the approximate position on the Grid. + /// May not return valid grid coordinates (outside the range). + /// + /// a Vector3 World Position; Z coordinates are inconsequential. + /// A Vector2Int representing a grid position from the bottom left. + public Vector2Int GetGridPosition(Vector3 worldPosition) { + Vector3 localScale = transform.localScale; + Vector2 gridPosition = (worldPosition + (localScale / 2f)) / new Vector2(localScale.x, localScale.y); + return Size - new Vector2Int( + (int) (gridPosition.x * width), + (int) (gridPosition.y * height)) - Vector2Int.one; + } + + /// + /// Translates a position on the grid into a real World Position. + /// + /// The XY position on the grid + /// A Vector3 centered on the grid square in the World + public Vector3 GetWorldPosition(Vector2Int gridPosition) { + Transform ttransform = transform; + Vector3 localScale = ttransform.localScale; + Vector2 topRight = ttransform.position + (localScale / 2f); + var singleSquare = new Vector2(localScale.x / width, localScale.y / height); + Vector2 worldPosition = topRight - (singleSquare * (gridPosition + Vector2Int.one)) + (singleSquare / 2f); + return worldPosition; + } } \ No newline at end of file diff --git a/Paths/Assets/Scripts/IPathfinding.cs b/Paths/Assets/Scripts/IPathfinding.cs index 6707545..84e93c7 100644 --- a/Paths/Assets/Scripts/IPathfinding.cs +++ b/Paths/Assets/Scripts/IPathfinding.cs @@ -13,8 +13,9 @@ public interface IPathfinding { /// The position trying to be found via pathfinding /// A List of NodeGridGrid objects representing the timeline of Pathfinding Stack FindPath(Vector2Int start, Vector2Int end); - + NodeGrid NodeGrid { get; } Vector2Int Start { get; } Vector2Int End { get; } ChangeController ChangeController { get; } + void Cleanup(); } \ No newline at end of file diff --git a/Paths/Assets/Scripts/Manager.cs b/Paths/Assets/Scripts/Manager.cs deleted file mode 100644 index 81ea5b8..0000000 --- a/Paths/Assets/Scripts/Manager.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using Algorithms; -using LevelGeneration; -using TMPro; -using UnityEngine; -using UnityEngine.Analytics; - -/// -/// The primary controller of the entire application, managing state, events and sending commands -/// -public class Manager : MonoBehaviour { - private IPathfinding _algorithm; - private ChangeController _state; - private int _curIndex; - private Stack _path; - private float _runtime; - - public Camera mainCamera; - public GameObject gridObject; - public GridController gridController; - public TextMeshPro debugText; - public float speed; - public float clampIncrement; - - private int CurrentIndex { - get => (int) _runtime; - set => _runtime = value; - } - - public void Start() { - GeneratePath(); - Resize(); - } - - // public void OnDrawGizmos() { - // float size = (float) (10.0 / gridController.size); - // Gizmos.DrawWireCube(transform.position, new Vector3(size, size, size)); - // } - - /// - /// Returns the current time multiplier, based on the latest change in the path. - /// - /// A positive non-zero float representing how fast the current frame should be processed. - private float CurrentMultiplier() { - if (_state.Index == -1) - return 1; - - switch (_state.CurrentChange.New) { - case GridNodeType.Path: - return 1/5f; - case GridNodeType.Empty: - break; - case GridNodeType.Wall: - break; - case GridNodeType.Start: - break; - case GridNodeType.End: - break; - case GridNodeType.Seen: - break; - case GridNodeType.Expanded: - break; - default: - throw new ArgumentOutOfRangeException(); - } - return 1; - } - - public void Update() { - var increment = Time.deltaTime * speed * CurrentMultiplier(); - if (clampIncrement > 0) - increment = Mathf.Clamp(increment, 0, _state.Count * Time.deltaTime / clampIncrement); - _runtime += increment; - - if (CurrentIndex < _state.Count) - LoadNextState(); - else { - GeneratePath(); - CurrentIndex = 0; - } - } - - private void GeneratePath() { - var nodeGrid = new NodeGrid(gridController.width, gridController.height); - - Vector2Int start = nodeGrid.RandomPosition(); - // Vector2Int start = new Vector2Int(30, 30); - Vector2Int end = nodeGrid.RandomPosition(); - - nodeGrid.ApplyGenerator(new RandomPlacement(0.3f, true, true)); - - nodeGrid.GetNode(start).Walkable = true; - nodeGrid.GetNode(end).Walkable = true; - - _algorithm = new AStar(nodeGrid); - _path = _algorithm.FindPath(start, end); - _state = _algorithm.ChangeController; - } - - private void LoadNextState() { - _state.MoveTo(CurrentIndex); - gridController.LoadDirtyGridState(_state.Current, _state.DirtyFlags); - - string pathCount = _path != null ? $"{_path.Count}" : "N/A"; - debugText.text = $"{_state.CurrentRuntime * 1000.0:F1}ms\n" + - $"{this.CurrentIndex:000} / {_state.Count:000}\n" + - $"Path: {pathCount} tiles"; - } - - /// - /// Scales the GridController GameObject to fit within the Camera - /// - private void Resize() { - float ratioImage = (float) gridController.width / gridController.height; - float ratioScreen = mainCamera.aspect; - - var orthographicSize = mainCamera.orthographicSize; - var image = new Vector2(gridController.width, gridController.height); - var screen = new Vector2(2 * orthographicSize * mainCamera.aspect, orthographicSize * 2); - - gridObject.transform.localScale = ratioScreen > ratioImage - ? new Vector3(image.x * screen.y / image.y, screen.y, 0.001f) - : new Vector3(screen.x, image.y * screen.x / image.x, 0.001f); - } -} \ No newline at end of file diff --git a/Paths/Assets/Scripts/Manager.cs.meta b/Paths/Assets/Scripts/Manager.cs.meta deleted file mode 100644 index ccde7f7..0000000 --- a/Paths/Assets/Scripts/Manager.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ca142250b9964eb1adf66ac7c5cc3e3e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 300 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Paths/Assets/Scripts/UIController.cs b/Paths/Assets/Scripts/UIController.cs new file mode 100644 index 0000000..837aec1 --- /dev/null +++ b/Paths/Assets/Scripts/UIController.cs @@ -0,0 +1,313 @@ +using System; +using System.Collections.Generic; +using Algorithms; +using TMPro; +using UnityEditor; +using UnityEngine; + + +/// +/// Denotes what the user clicked and is now modifying. +/// Add/Remove is for wall addition/removal, Start and End is for dragging the Start and End nodes around. +/// +public enum ClickType { + Add, + Remove, + Start, + End, + ProgressBar +} + +/// +/// Denotes the precise state of the animation as the user clicks, stops or starts it. +/// Stopped is a full stop, allowing editing and manipulation. The path will not be shown or generated. +/// Started is fully engaged when the user is not currently editing or manipulating. +/// Reloading occurs when the user manipulates the grid while the program is +/// +public enum AnimationState { + Stopped, + Paused, + Started, + Reloading +} + +/// +/// A expansive class that controls all UI interactions including grid modifications, slider movement, tool usage etc. +/// All UI elements are referenced and controlled here. +/// +public class UIController : MonoBehaviour { + // UI & important App references + public CustomSlider progressSlider; + public GridController gridController; + public Camera mainCamera; + public TextMeshPro debugText; + public GameObject gridObject; + + // Animation State, Click Management + private Vector2Int _lastClickLocation; + private ClickType _modify; + private AnimationState _animationState; + private AnimationState _previousAnimationState; + + private bool EditShouldReload => + _animationState == AnimationState.Started; + + // Grid State & Pathfinding + private NodeGrid _grid; + private Vector2Int _start; + private Vector2Int _end; + private IPathfinding _algorithm; + private Stack _path; + private ChangeController _state; + + // Animation speed & indexing + public float clampIncrement; + private float _runtime; + public int CurrentIndex => (int) Math.Ceiling(_runtime); + public float speed; + + private void Start() { + _grid = new NodeGrid(gridController.width, gridController.height); + _animationState = AnimationState.Stopped; + _previousAnimationState = _animationState; + _start = _grid.RandomPosition(); + _end = _grid.RandomPosition(); + _runtime = 0; + + Resize(); + progressSlider.onValueChanged.AddListener((value) => MoveToSlider(value)); + } + + private void Update() { + if (Input.GetMouseButton(0)) { + Vector3 worldMouse = mainCamera.ScreenToWorldPoint(Input.mousePosition); + Vector2Int position = gridController.GetGridPosition(worldMouse); + + // Initial click, remember what they clicked + if (Input.GetMouseButtonDown(0)) { + if (progressSlider.IsPressed) + _modify = ClickType.ProgressBar; + else if (position == _start) + _modify = ClickType.Start; + else if (position == _end) + _modify = ClickType.End; + else { + Node node = _grid.GetNode(position); + _modify = node.Walkable ? ClickType.Add : ClickType.Remove; + node.Walkable = !node.Walkable; + if (_animationState == AnimationState.Paused) + _animationState = AnimationState.Stopped; + else if (_animationState == AnimationState.Started) + _animationState = AnimationState.Reloading; + } + + _lastClickLocation = position; + } + else { + // If still holding down the button & the latest movement is over a new grid + if (_lastClickLocation != position) { + _lastClickLocation = position; + if (_grid.IsValid(position)) { + Node node = _grid.GetNode(position); + switch (_modify) { + // regular clicking toggles walls + // Note: Wall toggling instantly reloads, but only real start/end node movement reloads. + case ClickType.Add: + node.Walkable = false; + if (EditShouldReload) + _animationState = AnimationState.Reloading; + break; + case ClickType.Remove: + node.Walkable = true; + if (EditShouldReload) + _animationState = AnimationState.Reloading; + break; + case ClickType.Start: + if (node.Walkable) { + _start = position; + if (EditShouldReload) + _animationState = AnimationState.Reloading; + } + + break; + case ClickType.End: + if (node.Walkable) { + _end = position; + if (EditShouldReload) + _animationState = AnimationState.Reloading; + } + + break; + case ClickType.ProgressBar: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + } + } + + // Handle user start/stopping + if (Input.GetKeyDown(KeyCode.Space)) { + switch (_animationState) { + case AnimationState.Stopped: + _animationState = AnimationState.Reloading; + break; + case AnimationState.Paused: + _animationState = AnimationState.Started; + break; + case AnimationState.Started: + // Restart if already on final frame, else simply pause + if (CurrentIndex >= _state.Count) + _runtime = 0; + else + _animationState = AnimationState.Paused; + break; + case AnimationState.Reloading: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + switch (_animationState) { + case AnimationState.Reloading: + // Reloading seizes while the mouse button is depressed + if (!Input.GetMouseButton(0)) { + GeneratePath(); + LoadNextState(); + _animationState = AnimationState.Started; + } + else + gridController.LoadGridState(_grid.RenderNodeTypes(_start, _end)); + + progressSlider.SetValueWithoutNotify(_runtime / _state.Count); + break; + case AnimationState.Started: + // Calculate how much to move forward + if (!progressSlider.IsPressed) { + var increment = Time.deltaTime * speed * CurrentMultiplier(); + if (clampIncrement > 0) + increment = Mathf.Clamp(increment, 0, _state.Count * Time.deltaTime / clampIncrement); + _runtime += increment; + } + + progressSlider.SetValueWithoutNotify(_runtime / _state.Count); + + if (_runtime < _state.Count) + LoadNextState(); + break; + case AnimationState.Stopped: + // Render editable grid when fully stopped + gridController.LoadGridState(_grid.RenderNodeTypes(_start, _end)); + break; + case AnimationState.Paused: + if (_runtime < _state.Count) + LoadNextState(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + if (_animationState != _previousAnimationState) { + Debug.Log($"Animation State {_previousAnimationState} -> {_animationState}"); + _previousAnimationState = _animationState; + } + } + + private void GeneratePath() { + _algorithm?.Cleanup(); // cleanup algorithm's edits to node grid + + _runtime = 0f; + _algorithm = new AStar(_grid); + _path = _algorithm.FindPath(_start, _end); + _state = _algorithm.ChangeController; + } + + private void LoadNextState() { + // Move to the new calculated index + _state.MoveTo(Mathf.Clamp(CurrentIndex, 1, _state.Count - 1)); // use Math.max to ensure both start/end nodes are always rendered + gridController.LoadDirtyGridState(_state.Current, _state.DirtyFlags); + + string pathCount = _path != null ? $"{_path.Count}" : "N/A"; + debugText.text = $"{_state.CurrentRuntime * 1000.0:F1}ms\n" + + $"{CurrentIndex:000} / {_state.Count:000}\n" + + $"Path: {pathCount} tiles"; + } + + /// + /// Returns the current time multiplier, based on the latest change in the path. + /// + /// A positive non-zero float representing how fast the current frame should be processed. + private float CurrentMultiplier() { + if (_state.CurrentChangeIndex == -1) + return 1; + + switch (_state.CurrentChange.New) { + case GridNodeType.Path: + return 1 / 5f; + case GridNodeType.Empty: + break; + case GridNodeType.Wall: + break; + case GridNodeType.Start: + break; + case GridNodeType.End: + break; + case GridNodeType.Seen: + break; + case GridNodeType.Expanded: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + return 1; + } + + public void OnDrawGizmos() { + if (!Application.isPlaying) return; + + Vector3 mouse = mainCamera.ScreenToWorldPoint(Input.mousePosition); + Vector3 localScale = gridObject.transform.localScale; + Vector2Int gridPosition = gridController.GetGridPosition(mouse); + + var style = new GUIStyle(); + style.normal.textColor = Color.blue; + Gizmos.color = Color.blue; + + Gizmos.DrawWireCube(gridController.GetWorldPosition(gridPosition), localScale / (Vector2) gridController.Size); + Handles.Label(mouse, String.Format("{0}{1}", + gridPosition, + _algorithm != null && _algorithm.NodeGrid.IsValid(gridPosition) + ? $"\n{_state.Current[gridPosition.x, gridPosition.y]}" + : "" + ), style); + } + + /// + /// Update the animation progress to the slider's (new) position. + /// + /// The new position on the slider. + private void MoveToSlider(float @new) { + if (_state != null) + _runtime = @new * _state.Count; + } + + /// + /// Scales the GridController GameObject to fit within the Camera + /// + public void Resize() { + float ratioImage = (float) gridController.width / gridController.height; + float ratioScreen = mainCamera.aspect; + + var orthographicSize = mainCamera.orthographicSize; + var image = new Vector2(gridController.width, gridController.height); + var screen = new Vector2(2 * orthographicSize * mainCamera.aspect, orthographicSize * 2); + + gridObject.transform.localScale = ratioScreen > ratioImage + ? new Vector3(image.x * screen.y / image.y, screen.y, 0.001f) + : new Vector3(screen.x, image.y * screen.x / image.x, 0.001f); + } +} \ No newline at end of file diff --git a/Paths/Assets/Scripts/UIController.cs.meta b/Paths/Assets/Scripts/UIController.cs.meta new file mode 100644 index 0000000..1c1c16b --- /dev/null +++ b/Paths/Assets/Scripts/UIController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fd9c76568dd84a92b068dd1e622bd101 +timeCreated: 1606316179 \ No newline at end of file diff --git a/Paths/Assets/Shaders/GridShader.shader b/Paths/Assets/Shaders/GridShader.shader index 9f97a04..fe4a044 100644 --- a/Paths/Assets/Shaders/GridShader.shader +++ b/Paths/Assets/Shaders/GridShader.shader @@ -41,7 +41,7 @@ Shader "PDT Shaders/TestGrid" static const float4 _gridColors[7] = { float4(255 / 255.0, 255 / 255.0, 255 / 255.0, 1.0), // Empty - float4(0 / 255.0, 0 / 255.0, 0 / 255.0, 1.0), // Wall + float4(5 / 255.0, 5 / 255.0, 5 / 255.0, 1.0), // Wall float4(0 / 255.0, 255 / 255.0, 0 / 255.0, 1.0), // Start float4(255 / 255.0, 0 / 255.0, 0 / 255.0, 1.0), // End float4(252 / 255.0, 236 / 255.0, 3 / 255.0, 1.0), // Seen