diff --git a/Assets/Scenes/Pathfinding.unity b/Assets/Scenes/Pathfinding.unity index a73e332..781290d 100644 --- a/Assets/Scenes/Pathfinding.unity +++ b/Assets/Scenes/Pathfinding.unity @@ -974,6 +974,173 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_ShowMaskGraphic: 0 +--- !u!1 &111507929 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 111507930} + - component: {fileID: 111507932} + - component: {fileID: 111507931} + m_Layer: 5 + m_Name: Fill + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &111507930 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 111507929} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1980971369} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 10, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &111507931 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 111507929} + 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 &111507932 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 111507929} + m_CullTransparentMesh: 1 +--- !u!1 &115484931 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 115484932} + - component: {fileID: 115484933} + m_Layer: 5 + m_Name: Slider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &115484932 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 115484931} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 915989142} + - {fileID: 1980971369} + - {fileID: 351573955} + m_Father: {fileID: 873489265} + 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} + m_AnchoredPosition: {x: 661, y: -494} + m_SizeDelta: {x: 160, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &115484933 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 115484931} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 67db9e8f0e2ae9c40bc1e2b64352a6b4, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + 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: 813565619} + m_FillRect: {fileID: 111507930} + m_HandleRect: {fileID: 813565618} + m_Direction: 0 + m_MinValue: 0 + m_MaxValue: 1 + m_WholeNumbers: 0 + m_Value: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] --- !u!1 &115565369 GameObject: m_ObjectHideFlags: 0 @@ -2212,6 +2379,96 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 332997443} m_CullTransparentMesh: 1 +--- !u!1 &351573954 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 351573955} + 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!224 &351573955 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 351573954} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 813565618} + m_Father: {fileID: 115484932} + 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: 0, y: 0} + m_SizeDelta: {x: -20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &371969815 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 371969817} + - component: {fileID: 371969816} + m_Layer: 0 + m_Name: Test + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!114 &371969816 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 371969815} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ed03b279fa9f3194f8caed356ea838dc, type: 3} + m_Name: + m_EditorClassIdentifier: + gridMap: {fileID: 1193653632} + npc: {fileID: 497641072} + startTestButton: {fileID: 1287641230} + statusText: {fileID: 1778780372} + progressBar: {fileID: 115484933} + testsPerCombination: 3 + delayBetweenTests: 0.5 + saveResultsToFile: 1 + resultsFileName: pathfinding_tests.csv +--- !u!4 &371969817 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 371969815} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1324.0585, y: 547.7678, z: -5.158931} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &375331086 GameObject: m_ObjectHideFlags: 0 @@ -4605,6 +4862,82 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 809150985} m_CullTransparentMesh: 1 +--- !u!1 &813565617 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 813565618} + - component: {fileID: 813565620} + - component: {fileID: 813565619} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &813565618 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 813565617} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 351573955} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &813565619 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 813565617} + 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 &813565620 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 813565617} + m_CullTransparentMesh: 1 --- !u!1 &814740740 GameObject: m_ObjectHideFlags: 0 @@ -4829,9 +5162,11 @@ MonoBehaviour: loadButton: {fileID: 332997445} exitButton: {fileID: 1403363528} performWarmup: 1 - showWarmupMessage: 0 + showWarmupMessage: 1 mazeSizeDropdown: {fileID: 770082012} mazeDensityDropdown: {fileID: 1448650261} + testerPanelPrefab: {fileID: 0} + testerPanelParent: {fileID: 0} --- !u!4 &840407876 Transform: m_ObjectHideFlags: 0 @@ -5023,6 +5358,45 @@ MonoBehaviour: m_isRichTextEditingAllowed: 0 m_LineLimit: 0 m_InputValidator: {fileID: 0} +--- !u!1 &873489264 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 873489265} + m_Layer: 5 + m_Name: Test + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &873489265 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 873489264} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1287641229} + - {fileID: 1778780371} + - {fileID: 115484932} + m_Father: {fileID: 1261321007} + 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} + m_AnchoredPosition: {x: -150, y: 155} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &877190939 GameObject: m_ObjectHideFlags: 0 @@ -5158,6 +5532,82 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 877190939} m_CullTransparentMesh: 1 +--- !u!1 &915989141 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 915989142} + - component: {fileID: 915989144} + - component: {fileID: 915989143} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &915989142 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 915989141} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 115484932} + 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: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &915989143 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 915989141} + 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: 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 &915989144 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 915989141} + m_CullTransparentMesh: 1 --- !u!1 &924975606 GameObject: m_ObjectHideFlags: 0 @@ -7627,6 +8077,7 @@ RectTransform: - {fileID: 442352525} - {fileID: 1963564225} - {fileID: 1649005401} + - {fileID: 873489265} m_Father: {fileID: 0} m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -7711,6 +8162,128 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1269161862} m_CullTransparentMesh: 1 +--- !u!1 &1287641228 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1287641229} + - component: {fileID: 1287641232} + - component: {fileID: 1287641231} + - component: {fileID: 1287641230} + m_Layer: 5 + m_Name: Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1287641229 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1287641228} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1956300463} + m_Father: {fileID: 873489265} + 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} + m_AnchoredPosition: {x: 166, y: -494} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1287641230 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1287641228} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + 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: 1287641231} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &1287641231 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1287641228} + 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 &1287641232 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1287641228} + m_CullTransparentMesh: 1 --- !u!1 &1383327469 GameObject: m_ObjectHideFlags: 0 @@ -11124,6 +11697,141 @@ MonoBehaviour: m_EditorClassIdentifier: m_Padding: {x: -8, y: -5, z: -8, w: -5} m_Softness: {x: 0, y: 0} +--- !u!1 &1778780370 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1778780371} + - component: {fileID: 1778780373} + - component: {fileID: 1778780372} + m_Layer: 5 + m_Name: Status + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1778780371 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1778780370} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 873489265} + 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} + m_AnchoredPosition: {x: 665.35645, y: -406.85294} + m_SizeDelta: {x: 196.7319, y: 125.3129} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1778780372 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1778780370} + 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: New Text + 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: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, 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: 25 + m_fontSizeBase: 36 + m_fontWeight: 400 + m_enableAutoSizing: 1 + m_fontSizeMin: 18 + m_fontSizeMax: 25 + 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: 0 + 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 &1778780373 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1778780370} + m_CullTransparentMesh: 1 --- !u!1 &1799536494 GameObject: m_ObjectHideFlags: 0 @@ -12326,6 +13034,141 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1939810452} m_CullTransparentMesh: 1 +--- !u!1 &1956300462 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1956300463} + - component: {fileID: 1956300465} + - component: {fileID: 1956300464} + 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 &1956300463 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1956300462} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1287641229} + 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: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1956300464 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1956300462} + 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: Start + 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: 0 + 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 &1956300465 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1956300462} + m_CullTransparentMesh: 1 --- !u!1 &1960286546 GameObject: m_ObjectHideFlags: 0 @@ -12447,6 +13290,43 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1963564224} m_CullTransparentMesh: 1 +--- !u!1 &1980971368 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1980971369} + 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 &1980971369 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1980971368} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 111507930} + m_Father: {fileID: 115484932} + 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 &1982768388 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/GridMap.cs b/Assets/Scripts/GridMap.cs index ad1c6f2..39325d8 100644 --- a/Assets/Scripts/GridMap.cs +++ b/Assets/Scripts/GridMap.cs @@ -239,6 +239,15 @@ public class GridMap : MonoBehaviour /// public void RayCastAndSetDestination() { + // Don't allow changing destination during pathfinding, visualization, or movement + if (npc != null && (npc.pathFinder?.Status == PathFinding.PathFinderStatus.RUNNING || + npc.IsVisualizingPath || + npc.IsMoving)) + { + // Pathfinding, visualization or movement is in progress - ignore position change + return; + } + // Konversi posisi mouse ke koordinat dunia Vector2 rayPos = new Vector2( Camera.main.ScreenToWorldPoint(Input.mousePosition).x, @@ -291,6 +300,15 @@ public class GridMap : MonoBehaviour /// public void RayCastAndSetNPCPosition() { + // Don't allow changing NPC position during pathfinding, visualization, or movement + if (npc != null && (npc.pathFinder?.Status == PathFinding.PathFinderStatus.RUNNING || + npc.IsVisualizingPath || + npc.IsMoving)) + { + // Pathfinding, visualization or movement is in progress - ignore position change + return; + } + // Konversi posisi mouse ke koordinat dunia Vector2 rayPos = new Vector2( Camera.main.ScreenToWorldPoint(Input.mousePosition).x, @@ -318,6 +336,11 @@ public class GridMap : MonoBehaviour /// void Update() { + // Check if pathfinding, visualization, or movement is active + bool isPathfindingActive = npc != null && (npc.pathFinder?.Status == PathFinding.PathFinderStatus.RUNNING || + npc.IsVisualizingPath || + npc.IsMoving); + // Handle camera panning with middle mouse button if (Input.GetMouseButton(2)) // Middle mouse button { @@ -336,6 +359,13 @@ public class GridMap : MonoBehaviour if (currentMousePosition != lastMousePosition) { + // If pathfinding is active, only allow camera movement, no interaction with grid + if (isPathfindingActive) + { + lastMousePosition = currentMousePosition; + return; + } + // Menggambar dinding dengan Shift+Left Click if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) { @@ -357,7 +387,10 @@ public class GridMap : MonoBehaviour // Menetapkan tujuan baru saat tombol kanan mouse ditekan if (Input.GetMouseButtonDown(1)) { - RayCastAndSetDestination(); + if (!isPathfindingActive) + { + RayCastAndSetDestination(); + } } // Menyesuaikan ukuran kamera dengan scroll wheel @@ -698,8 +731,24 @@ public class GridMap : MonoBehaviour /// /// Resizes the grid to the specified dimensions /// - public void ResizeGrid(int newSizeX, int newSizeY) + public bool ResizeGrid(int newSizeX, int newSizeY) { + // Enforce grid size limits + const int MAX_GRID_SIZE = 200; + const int MIN_GRID_SIZE = 2; + + if (newSizeX > MAX_GRID_SIZE || newSizeY > MAX_GRID_SIZE) + { + Debug.LogWarning($"Attempted to resize grid beyond maximum size of {MAX_GRID_SIZE}x{MAX_GRID_SIZE}. Operation cancelled."); + return false; + } + + if (newSizeX < MIN_GRID_SIZE || newSizeY < MIN_GRID_SIZE) + { + Debug.LogWarning($"Attempted to resize grid below minimum size of {MIN_GRID_SIZE}x{MIN_GRID_SIZE}. Operation cancelled."); + return false; + } + // Clean up existing grid if (gridNodeViews != null) { @@ -749,6 +798,8 @@ public class GridMap : MonoBehaviour { npc.SetStartNode(gridNodeViews[0, 0].Node); } + + return true; } /// @@ -771,7 +822,53 @@ public class GridMap : MonoBehaviour /// Kepadatan dinding dalam persen (0-100), mempengaruhi rasio jalur terhadap ruang terbuka public void GenerateRandomMaze(float density = 35f) { - // Use the recursive backtracking maze generation + // Handle special cases for 0% and 100% density + if (density <= 0f) + { + // Make all cells walkable (no walls) + for (int x = 0; x < numX; x++) + { + for (int y = 0; y < numY; y++) + { + GridNode node = GetGridNode(x, y); + if (node != null) + { + node.IsWalkable = true; + GridNodeView gnv = GetGridNodeView(x, y); + if (gnv != null) + { + gnv.SetInnerColor(COLOR_WALKABLE); + } + } + } + } + return; + } + else if (density >= 100f) + { + // Make all cells non-walkable (all walls) + for (int x = 0; x < numX; x++) + { + for (int y = 0; y < numY; y++) + { + GridNode node = GetGridNode(x, y); + if (node != null) + { + node.IsWalkable = false; + GridNodeView gnv = GetGridNodeView(x, y); + if (gnv != null) + { + gnv.SetInnerColor(COLOR_NONWALKABLE); + } + } + } + } + + // No path creation - truly 100% blocked + return; + } + + // Use the recursive backtracking maze generation for normal density values GenerateRecursiveBacktrackingMaze(density); } diff --git a/Assets/Scripts/NPC.cs b/Assets/Scripts/NPC.cs index e607e9b..1b06128 100644 --- a/Assets/Scripts/NPC.cs +++ b/Assets/Scripts/NPC.cs @@ -21,17 +21,26 @@ public struct PathfindingMetrics public float totalGCost; // Total biaya G untuk jalur (jarak sebenarnya) public float totalHCost; // Total biaya H untuk jalur (heuristik) public float totalFCost; // Total biaya F untuk jalur (G + H) - } +/// +/// NPC adalah komponen utama yang mengelola pergerakan NPC dalam sistem pathfinding. +/// Kelas ini bertanggung jawab untuk membuat, menampilkan, dan mengelola jalur untuk NPC. +/// public class NPC : MonoBehaviour { public float speed = 2.0f; public Queue wayPoints = new Queue(); - + // Event that fires when pathfinding is complete with performance metrics public event Action OnPathfindingComplete; + // Last measured memory usage (for accessing from outside) + public long LastMeasuredMemoryUsage { get; private set; } = 0; + + /// + /// Enumerasi yang merepresentasikan berbagai algoritma pathfinding yang tersedia. + /// public enum PathFinderType { ASTAR, @@ -44,13 +53,26 @@ public class NPC : MonoBehaviour [SerializeField] public PathFinderType pathFinderType = PathFinderType.ASTAR; - PathFinder pathFinder = null; + public PathFinder pathFinder = null; public GridMap Map { get; set; } // List to store all steps for visualization playback private List visualizationSteps = new List(); private bool isVisualizingPath = false; + private bool isMoving = false; + + // Public accessor for visualization state + public bool IsVisualizingPath => isVisualizingPath; + + // Public accessor for movement state + public bool IsMoving => isMoving; + + // Event that fires when visualization is complete + public event Action OnVisualizationComplete; + + // Event that fires when movement is complete + public event Action OnMovementComplete; // Properties to control visualization [SerializeField] @@ -110,11 +132,27 @@ public class NPC : MonoBehaviour { while (wayPoints.Count > 0) { + // Set the moving flag when starting to move + if (!isMoving && wayPoints.Count > 0) + { + isMoving = true; + UnityEngine.Debug.Log("NPC movement started"); + } + yield return StartCoroutine( Coroutine_MoveToPoint( wayPoints.Dequeue(), speed)); } + + // If we were moving but now have no more waypoints, signal movement completion + if (isMoving && wayPoints.Count == 0) + { + isMoving = false; + UnityEngine.Debug.Log("NPC movement complete, invoking OnMovementComplete event"); + OnMovementComplete?.Invoke(); + } + yield return null; } } @@ -236,8 +274,10 @@ public class NPC : MonoBehaviour { yield return StartCoroutine(MeasurePerformance(silentMode)); - // Start visualization after calculation is complete - if (pathFinder.Status == PathFinderStatus.SUCCESS && showVisualization && !silentMode) + // Start visualization after calculation is complete regardless of success or failure + // This allows visualization of the explored area even when no path is found + if (showVisualization && !silentMode && + (pathFinder.Status == PathFinderStatus.SUCCESS || pathFinder.Status == PathFinderStatus.FAILURE)) { yield return StartCoroutine(VisualizePathfinding()); } @@ -254,8 +294,8 @@ public class NPC : MonoBehaviour // Pre-allocate visualizationSteps with estimated capacity to avoid reallocations visualizationSteps = new List(4); - GC.Collect(); - GC.WaitForPendingFinalizers(); // Tunggu semua finalizers selesai + //GC.Collect(2, GCCollectionMode.Forced, true, true); + //GC.WaitForPendingFinalizers(); // Tunggu semua finalizers selesai // ===== MEMORY MEASUREMENT START: Ukur memory sebelum algoritma ===== long memoryBefore = System.GC.GetTotalMemory(false); @@ -284,13 +324,9 @@ public class NPC : MonoBehaviour long memoryAfter = System.GC.GetTotalMemory(false); long memoryUsed = memoryAfter - memoryBefore; - // float miliseconds = algorithmTimer.ElapsedMilliseconds; - - //UnityEngine.Debug.Log("$algorithmTimer.ElapsedTicks: " + algorithmTimer.ElapsedTicks); - //UnityEngine.Debug.Log("$Stopwatch.Frequency: " + Stopwatch.Frequency); - //float seconds = (float)algorithmTimer.ElapsedTicks / Stopwatch.Frequency; - //UnityEngine.Debug.Log("$seconds: " + seconds); - + // Store the memory usage for external access + LastMeasuredMemoryUsage = memoryUsed > 0 ? memoryUsed : 1024; + float milliseconds = (algorithmTimer.ElapsedTicks * 1000.0f) / Stopwatch.Frequency; // Calculate path length once and reuse @@ -324,11 +360,9 @@ public class NPC : MonoBehaviour totalFCost = totalFCost, }; + // *** IMPORTANT FIX: Always invoke the event, regardless of silent mode *** // Report metrics before visualization - if (!silentMode) - { - OnPathfindingComplete?.Invoke(metrics); - } + OnPathfindingComplete?.Invoke(metrics); // Path visualization and handling HandlePathFindingResult(silentMode, pathLength); @@ -418,7 +452,6 @@ public class NPC : MonoBehaviour /// private void HandlePathFindingResult(bool silentMode, int pathLength) { - if (pathFinder.Status == PathFinderStatus.SUCCESS) { OnSuccessPathFinding(); @@ -449,6 +482,11 @@ public class NPC : MonoBehaviour else if (pathFinder.Status == PathFinderStatus.FAILURE) { OnFailurePathFinding(); + + // For failure case, we don't add any final path visualization steps + // The exploration steps (open/closed lists) are already added during the search + // and will be visualized to show what nodes were explored before failure + UnityEngine.Debug.Log($"Pathfinding failed - visualization will show {visualizationSteps.Count} exploration steps"); } } @@ -554,8 +592,9 @@ public class NPC : MonoBehaviour if (!showVisualization) yield break; + UnityEngine.Debug.Log("Path visualization starting"); isVisualizingPath = true; - + // First, ensure grid is reset Map.ResetGridNodeColours(); @@ -563,6 +602,14 @@ public class NPC : MonoBehaviour int stepCount = visualizationSteps.Count; int batchSize = Mathf.Min(visualizationBatch, stepCount); // set higher value for faster visualization + // Detect if pathfinding failed - we'll need to know this when processing steps + bool pathfindingFailed = pathFinder.Status == PathFinderStatus.FAILURE; + + if (pathfindingFailed) + { + UnityEngine.Debug.Log($"Visualizing failed pathfinding attempt with {stepCount} steps"); + } + for (int i = 0; i < stepCount; i += batchSize) { int end = Mathf.Min(i + batchSize, stepCount); @@ -587,8 +634,8 @@ public class NPC : MonoBehaviour break; case PathfindingVisualizationStep.StepType.FinalPath: gnv.SetInnerColor(Map.COLOR_PATH); - // Also add the waypoint when we process the path - if (step.type == PathfindingVisualizationStep.StepType.FinalPath) + // Only add waypoints for successful pathfinding + if (!pathfindingFailed) { GridNode pathNode = Map.GetGridNode(step.position.x, step.position.y); AddWayPoint(pathNode); @@ -603,6 +650,9 @@ public class NPC : MonoBehaviour } isVisualizingPath = false; + UnityEngine.Debug.Log("Path visualization complete, invoking OnVisualizationComplete event"); + // Notify any listeners that visualization is complete + OnVisualizationComplete?.Invoke(); } /// diff --git a/Assets/Scripts/PathFinder.cs b/Assets/Scripts/PathFinder.cs index 9dbdf22..be2bd33 100644 --- a/Assets/Scripts/PathFinder.cs +++ b/Assets/Scripts/PathFinder.cs @@ -1360,7 +1360,8 @@ namespace PathFinding #region Breath-First Search Algorithm /// /// Implementasi algoritma Breadth-First Search (BFS) - /// Algoritma ini menjelajahi semua node pada jarak yang sama dari titik awal sebelum bergerak ke node yang lebih jauh + /// Algoritma ini menjelajahi semua node pada jarak yang sama dari + /// titik awal sebelum bergerak ke node yang lebih jauh /// /// Tipe data nilai yang disimpan dalam node public class BFSPathFinder : PathFinder diff --git a/Assets/Scripts/PathfindingTestAnalyzer.cs b/Assets/Scripts/PathfindingTestAnalyzer.cs new file mode 100644 index 0000000..b19a3fa --- /dev/null +++ b/Assets/Scripts/PathfindingTestAnalyzer.cs @@ -0,0 +1,419 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +/// +/// Utility class to analyze pathfinding test results and display statistics +/// +public class PathfindingTestAnalyzer : MonoBehaviour +{ + [Header("UI References")] + public TMP_Text resultsText; + public TMP_Dropdown algorithmFilterDropdown; + public TMP_Dropdown statisticTypeDropdown; + public RectTransform graphContainer; + public GameObject barPrefab; + + [Header("File Settings")] + public string resultsFileName = "pathfinding_tests.csv"; + + // Data structures + private List allResults = new List(); + private Dictionary algorithmColors = new Dictionary() + { + { "ASTAR", Color.blue }, + { "DIJKSTRA", Color.green }, + { "GREEDY", Color.red }, + { "BACKTRACKING", Color.yellow }, + { "BFS", Color.magenta } + }; + + // Struct to hold a single test result + private struct TestResult + { + public string algorithm; + public int gridSizeX; + public int gridSizeY; + public float density; + public bool diagonalMovement; + public float timeTaken; + public int pathLength; + public int nodesExplored; + public long memoryUsed; + public bool pathFound; + public int testIndex; + } + + void Start() + { + SetupDropdowns(); + LoadResults(); + + // Set default visualization + if (allResults.Count > 0) + { + GenerateStatistics(); + } + } + + private void SetupDropdowns() + { + // Set up algorithm filter dropdown + algorithmFilterDropdown.ClearOptions(); + algorithmFilterDropdown.AddOptions(new List { + "All Algorithms", + "A*", + "Dijkstra", + "Greedy BFS", + "Backtracking", + "BFS" + }); + algorithmFilterDropdown.onValueChanged.AddListener(OnFilterChanged); + + // Set up statistic type dropdown + statisticTypeDropdown.ClearOptions(); + statisticTypeDropdown.AddOptions(new List { + "Execution Time", + "Path Length", + "Nodes Explored", + "Memory Used", + "Success Rate" + }); + statisticTypeDropdown.onValueChanged.AddListener(OnFilterChanged); + } + + private void LoadResults() + { + string directory = Path.Combine(Application.persistentDataPath, "TestResults"); + string filePath = Path.Combine(directory, resultsFileName); + + if (!File.Exists(filePath)) + { + Debug.LogError($"Results file not found: {filePath}"); + if (resultsText != null) + { + resultsText.text = "No test results found. Run tests first."; + } + return; + } + + // Read all lines and skip header + string[] lines = File.ReadAllLines(filePath); + if (lines.Length <= 1) + { + Debug.LogWarning("Results file is empty or only contains a header"); + return; + } + + // Clear previous results + allResults.Clear(); + + // Skip header row and parse data rows + for (int i = 1; i < lines.Length; i++) + { + string line = lines[i]; + if (string.IsNullOrWhiteSpace(line)) + continue; + + string[] values = line.Split(','); + if (values.Length < 10) + { + Debug.LogWarning($"Invalid data in line {i}: {line}"); + continue; + } + + TestResult result = new TestResult + { + algorithm = values[0], + gridSizeX = int.Parse(values[1]), + gridSizeY = int.Parse(values[2]), + density = float.Parse(values[3]), + diagonalMovement = bool.Parse(values[4]), + timeTaken = float.Parse(values[5]), + pathLength = int.Parse(values[6]), + nodesExplored = int.Parse(values[7]), + memoryUsed = long.Parse(values[8]), + pathFound = bool.Parse(values[9]), + testIndex = int.Parse(values[10]) + }; + + allResults.Add(result); + } + + Debug.Log($"Loaded {allResults.Count} test results"); + } + + public void OnFilterChanged(int value) + { + GenerateStatistics(); + } + + private void GenerateStatistics() + { + if (allResults.Count == 0) + return; + + // Clear previous graph + foreach (Transform child in graphContainer) + { + Destroy(child.gameObject); + } + + // Get selected algorithm filter + string algorithmFilter = "All"; + switch (algorithmFilterDropdown.value) + { + case 0: algorithmFilter = "All"; break; + case 1: algorithmFilter = "ASTAR"; break; + case 2: algorithmFilter = "DIJKSTRA"; break; + case 3: algorithmFilter = "GREEDY"; break; + case 4: algorithmFilter = "BACKTRACKING"; break; + case 5: algorithmFilter = "BFS"; break; + } + + // Filter results by algorithm if not "All" + List filteredResults = allResults; + if (algorithmFilter != "All") + { + filteredResults = allResults.Where(r => r.algorithm == algorithmFilter).ToList(); + } + + if (filteredResults.Count == 0) + { + resultsText.text = "No data for selected filters."; + return; + } + + // Generate statistics based on selected metric + switch (statisticTypeDropdown.value) + { + case 0: // Execution Time + GenerateTimeStatistics(filteredResults); + break; + case 1: // Path Length + GeneratePathLengthStatistics(filteredResults); + break; + case 2: // Nodes Explored + GenerateNodesExploredStatistics(filteredResults); + break; + case 3: // Memory Used + GenerateMemoryStatistics(filteredResults); + break; + case 4: // Success Rate + GenerateSuccessRateStatistics(filteredResults); + break; + } + } + + private void GenerateTimeStatistics(List results) + { + var averageTimeByAlgorithm = results + .GroupBy(r => r.algorithm) + .Select(g => new { + Algorithm = g.Key, + AverageTime = g.Average(r => r.timeTaken) + }) + .OrderByDescending(x => x.AverageTime) + .ToList(); + + // Generate graph bars + CreateBarsForData(averageTimeByAlgorithm.Select(x => x.Algorithm).ToList(), + averageTimeByAlgorithm.Select(x => (float)x.AverageTime).ToList(), + "ms"); + + // Generate text summary + resultsText.text = "Average Execution Time by Algorithm:\n\n"; + foreach (var stat in averageTimeByAlgorithm) + { + resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.AverageTime:F2} ms\n"; + } + } + + private void GeneratePathLengthStatistics(List results) + { + var averagePathByAlgorithm = results + .Where(r => r.pathFound) // Only include successful paths + .GroupBy(r => r.algorithm) + .Select(g => new { + Algorithm = g.Key, + AveragePath = g.Average(r => r.pathLength) + }) + .OrderByDescending(x => x.AveragePath) + .ToList(); + + // Generate graph bars + CreateBarsForData(averagePathByAlgorithm.Select(x => x.Algorithm).ToList(), + averagePathByAlgorithm.Select(x => (float)x.AveragePath).ToList(), + "nodes"); + + // Generate text summary + resultsText.text = "Average Path Length by Algorithm:\n\n"; + foreach (var stat in averagePathByAlgorithm) + { + resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.AveragePath:F2} nodes\n"; + } + } + + private void GenerateNodesExploredStatistics(List results) + { + var averageNodesExploredByAlgorithm = results + .GroupBy(r => r.algorithm) + .Select(g => new { + Algorithm = g.Key, + AverageNodes = g.Average(r => r.nodesExplored) + }) + .OrderByDescending(x => x.AverageNodes) + .ToList(); + + // Generate graph bars + CreateBarsForData(averageNodesExploredByAlgorithm.Select(x => x.Algorithm).ToList(), + averageNodesExploredByAlgorithm.Select(x => (float)x.AverageNodes).ToList(), + "nodes"); + + // Generate text summary + resultsText.text = "Average Nodes Explored by Algorithm:\n\n"; + foreach (var stat in averageNodesExploredByAlgorithm) + { + resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.AverageNodes:F0} nodes\n"; + } + } + + private void GenerateMemoryStatistics(List results) + { + var averageMemoryByAlgorithm = results + .GroupBy(r => r.algorithm) + .Select(g => new { + Algorithm = g.Key, + AverageMemory = g.Average(r => r.memoryUsed) / 1024.0f // Convert to KB + }) + .OrderByDescending(x => x.AverageMemory) + .ToList(); + + // Generate graph bars + CreateBarsForData(averageMemoryByAlgorithm.Select(x => x.Algorithm).ToList(), + averageMemoryByAlgorithm.Select(x => (float)x.AverageMemory).ToList(), + "KB"); + + // Generate text summary + resultsText.text = "Average Memory Usage by Algorithm:\n\n"; + foreach (var stat in averageMemoryByAlgorithm) + { + resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.AverageMemory:F2} KB\n"; + } + } + + private void GenerateSuccessRateStatistics(List results) + { + var successRateByAlgorithm = results + .GroupBy(r => r.algorithm) + .Select(g => new { + Algorithm = g.Key, + SuccessRate = g.Count(r => r.pathFound) * 100.0f / g.Count() + }) + .OrderByDescending(x => x.SuccessRate) + .ToList(); + + // Generate graph bars + CreateBarsForData(successRateByAlgorithm.Select(x => x.Algorithm).ToList(), + successRateByAlgorithm.Select(x => (float)x.SuccessRate).ToList(), + "%"); + + // Generate text summary + resultsText.text = "Success Rate by Algorithm:\n\n"; + foreach (var stat in successRateByAlgorithm) + { + resultsText.text += $"{GetAlgorithmName(stat.Algorithm)}: {stat.SuccessRate:F1}%\n"; + } + } + + private void CreateBarsForData(List labels, List values, string unit) + { + if (barPrefab == null || graphContainer == null || labels.Count == 0) + return; + + float graphWidth = graphContainer.rect.width; + float graphHeight = graphContainer.rect.height; + float maxValue = values.Max(); + float barWidth = graphWidth / (labels.Count + 1); + + for (int i = 0; i < labels.Count; i++) + { + // Create bar + GameObject barObj = Instantiate(barPrefab, graphContainer); + RectTransform barRect = barObj.GetComponent(); + Image barImage = barObj.GetComponent(); + + // Calculate height based on value + float normalizedValue = values[i] / maxValue; + float barHeight = graphHeight * normalizedValue * 0.8f; // 80% of graph height max + + // Position and size bar + barRect.anchoredPosition = new Vector2((i + 0.5f) * barWidth, barHeight / 2); + barRect.sizeDelta = new Vector2(barWidth * 0.8f, barHeight); + + // Set bar color + string algorithm = labels[i]; + if (algorithmColors.ContainsKey(algorithm)) + barImage.color = algorithmColors[algorithm]; + + // Add label + GameObject labelObj = new GameObject($"Label_{labels[i]}"); + labelObj.transform.SetParent(barObj.transform); + + TMP_Text labelText = labelObj.AddComponent(); + labelText.text = $"{values[i]:F1}{unit}"; + labelText.fontSize = 12; + labelText.alignment = TextAlignmentOptions.Center; + labelText.color = Color.black; + + RectTransform labelRect = labelObj.GetComponent(); + labelRect.anchoredPosition = new Vector2(0, barHeight + 10); + labelRect.sizeDelta = new Vector2(barWidth, 20); + + // Add algorithm name label at bottom + GameObject nameObj = new GameObject($"Name_{labels[i]}"); + nameObj.transform.SetParent(barObj.transform); + + TMP_Text nameText = nameObj.AddComponent(); + nameText.text = GetAlgorithmName(labels[i]); + nameText.fontSize = 10; + nameText.alignment = TextAlignmentOptions.Center; + nameText.color = Color.black; + + RectTransform nameRect = nameObj.GetComponent(); + nameRect.anchoredPosition = new Vector2(0, -10); + nameRect.sizeDelta = new Vector2(barWidth, 20); + } + } + + private string GetAlgorithmName(string algorithm) + { + switch (algorithm) + { + case "ASTAR": return "A*"; + case "DIJKSTRA": return "Dijkstra"; + case "GREEDY": return "Greedy"; + case "BACKTRACKING": return "Backtracking"; + case "BFS": return "BFS"; + default: return algorithm; + } + } + + // Utility function to format memory size + private string FormatBytes(long bytes) + { + string[] sizes = { "B", "KB", "MB", "GB" }; + int order = 0; + double size = bytes; + while (size >= 1024 && order < sizes.Length - 1) + { + order++; + size = size / 1024; + } + return $"{size:0.##} {sizes[order]}"; + } +} \ No newline at end of file diff --git a/Assets/Scripts/PathfindingTestAnalyzer.cs.meta b/Assets/Scripts/PathfindingTestAnalyzer.cs.meta new file mode 100644 index 0000000..1709582 --- /dev/null +++ b/Assets/Scripts/PathfindingTestAnalyzer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9fee3f5c29ad21b428f68d801f2377f1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/PathfindingTester.cs b/Assets/Scripts/PathfindingTester.cs new file mode 100644 index 0000000..328c110 --- /dev/null +++ b/Assets/Scripts/PathfindingTester.cs @@ -0,0 +1,510 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +public class PathfindingTester : MonoBehaviour +{ + [Header("References")] + public GridMap gridMap; + public NPC npc; + public Button startTestButton; + public TMP_Text statusText; + public Slider progressBar; + + [Header("Test Configuration")] + [Tooltip("Number of times to run each test combination")] + public int testsPerCombination = 3; + + [Tooltip("Delay between tests in seconds")] + public float delayBetweenTests = 0.5f; + + [Tooltip("Whether to save results to a CSV file")] + public bool saveResultsToFile = true; + + [Tooltip("File name for test results (CSV)")] + public string resultsFileName = "pathfinding_tests.csv"; + + // Test matrix parameters + private NPC.PathFinderType[] algorithmsToTest = new NPC.PathFinderType[] { + NPC.PathFinderType.ASTAR, + NPC.PathFinderType.DIJKSTRA, + NPC.PathFinderType.GREEDY, + NPC.PathFinderType.BACKTRACKING, + NPC.PathFinderType.BFS + }; + + private Vector2Int[] gridSizesToTest = new Vector2Int[] { + new Vector2Int(20, 20), + new Vector2Int(35, 35), + new Vector2Int(50, 50), + new Vector2Int(40, 25) // Another non-square grid + }; + + private float[] mazeDensitiesToTest = new float[] { + 0f, // Empty (no walls) + 10f, // Very low + 30f, // Medium + 50f, // High + 100f // Fully blocked (all walls) + }; + + private bool[] diagonalMovementOptions = new bool[] { + true, + false + }; + + // Test state tracking + private bool isTestingRunning = false; + private int totalTests; + private int completedTests; + private List testResults = new List(); + + // Current test parameters + private NPC.PathFinderType currentTestAlgorithm; + private Vector2Int currentTestGridSize; + private float currentTestDensity; + private bool currentTestDiagonal; + private int currentTestIndex; + + // Structure to store test results + private struct PathfindingTestResult + { + public NPC.PathFinderType algorithm; + public Vector2Int gridSize; + public float mazeDensity; + public bool diagonalMovement; + public float timeTaken; + public int pathLength; + public int nodesExplored; + public long memoryUsed; + public bool pathFound; + public int testIndex; + } + + private void Start() + { + // Subscribe to NPC's pathfinding completion event + npc.OnPathfindingComplete += OnPathfindingComplete; + + // Set up the button listener + startTestButton.onClick.AddListener(StartTesting); + + // Initial status message + UpdateStatus("Ready to start testing. Click the Start Tests button."); + } + + private void OnDestroy() + { + // Unsubscribe from events + if (npc != null) + { + npc.OnPathfindingComplete -= OnPathfindingComplete; + } + } + + private void UpdateStatus(string message) + { + if (statusText != null) + { + statusText.text = message; + } + Debug.Log(message); + + // Update progress bar if available + if (progressBar != null && totalTests > 0) + { + progressBar.value = (float)completedTests / totalTests; + } + } + + public void StartTesting() + { + if (isTestingRunning) + { + Debug.LogWarning("Tests are already running."); + return; + } + + // Clear previous results + testResults.Clear(); + + // Calculate total number of tests + totalTests = algorithmsToTest.Length * + gridSizesToTest.Length * + mazeDensitiesToTest.Length * + diagonalMovementOptions.Length * + testsPerCombination; + + completedTests = 0; + isTestingRunning = true; + + UpdateStatus($"Starting {totalTests} tests..."); + + // Disable button during testing + startTestButton.interactable = false; + + // Start the test coroutine + StartCoroutine(RunTestMatrix()); + } + + private IEnumerator RunTestMatrix() + { + // Iterate through all test combinations + foreach (var algorithm in algorithmsToTest) + { + foreach (var gridSize in gridSizesToTest) + { + foreach (var density in mazeDensitiesToTest) + { + foreach (var useDiagonals in diagonalMovementOptions) + { + for (int testIndex = 0; testIndex < testsPerCombination; testIndex++) + { + yield return StartCoroutine(RunSingleTest(algorithm, gridSize, density, useDiagonals, testIndex)); + + // Wait between tests + yield return new WaitForSeconds(delayBetweenTests); + } + } + } + } + } + + // All tests completed + isTestingRunning = false; + startTestButton.interactable = true; + + // Save results if enabled + if (saveResultsToFile) + { + SaveResultsToCSV(); + } + + UpdateStatus($"Testing complete! {completedTests} tests run. Results saved to {resultsFileName}"); + } + + private IEnumerator RunSingleTest(NPC.PathFinderType algorithm, Vector2Int gridSize, float density, bool useDiagonals, int testIndex) + { + // Update status with current test info + UpdateStatus($"Test {completedTests+1}/{totalTests}: {algorithm} - Grid: {gridSize.x}x{gridSize.y} - Density: {density}% - Diagonals: {useDiagonals}"); + + // Configure the test environment + yield return StartCoroutine(SetupTestEnvironment(algorithm, gridSize, density, useDiagonals)); + + // Generate start and destination points + GridNode startNode = FindValidStartNode(); + GridNode destNode = FindValidDestinationNode(startNode); + + if (startNode == null || destNode == null) + { + Debug.LogWarning($"Failed to find valid start/destination nodes for test - density: {density}%"); + + // Record the test as "impossible" with a failed path + PathfindingTestResult result = new PathfindingTestResult + { + algorithm = algorithm, + gridSize = gridSize, + mazeDensity = density, + diagonalMovement = useDiagonals, + timeTaken = 0f, + pathLength = 0, + nodesExplored = 0, + memoryUsed = 0, + pathFound = false, + testIndex = testIndex + }; + + testResults.Add(result); + completedTests++; + yield break; + } + + // Position NPC at start node + npc.SetStartNode(startNode); + + // Position destination + gridMap.SetDestination(destNode.Value.x, destNode.Value.y); + + // Wait a frame to ensure everything is set up + yield return null; + + // Store the current test parameters + currentTestAlgorithm = algorithm; + currentTestGridSize = gridSize; + currentTestDensity = density; + currentTestDiagonal = useDiagonals; + currentTestIndex = testIndex; + + // Flag to track if callback was triggered + bool callbackTriggered = false; + + // Setup a temporary callback listener to detect if the event fires + System.Action tempCallback = (metrics) => { callbackTriggered = true; }; + npc.OnPathfindingComplete += tempCallback; + + // Run pathfinding in silent mode - now the event will still fire with our NPC fix + npc.MoveTo(destNode, true); + + // Wait for pathfinding to complete + float timeout = 10.0f; + float elapsed = 0f; + + while (npc.pathFinder != null && npc.pathFinder.Status == PathFinding.PathFinderStatus.RUNNING && elapsed < timeout) + { + yield return null; + elapsed += Time.deltaTime; + } + + // Wait a bit more to ensure completion + yield return new WaitForSeconds(0.1f); + + // Remove the temporary callback + npc.OnPathfindingComplete -= tempCallback; + + // If callback wasn't triggered but pathfinding is complete, manually record the result + if (!callbackTriggered && npc.pathFinder != null && npc.pathFinder.Status != PathFinding.PathFinderStatus.RUNNING) + { + Debug.LogWarning($"Callback wasn't triggered for test {completedTests+1}. Recording results manually."); + + // Create a metrics object with available data + PathfindingMetrics metrics = new PathfindingMetrics + { + timeTaken = elapsed * 1000f, // convert to ms + pathLength = CalculatePathLength(npc.pathFinder), + nodesExplored = npc.pathFinder.ClosedListCount, + memoryUsed = npc.LastMeasuredMemoryUsage // Get the last memory measurement from NPC + }; + + // Create a test result entry directly + PathfindingTestResult result = new PathfindingTestResult + { + algorithm = currentTestAlgorithm, + gridSize = currentTestGridSize, + mazeDensity = currentTestDensity, + diagonalMovement = currentTestDiagonal, + timeTaken = metrics.timeTaken, + pathLength = metrics.pathLength, + nodesExplored = metrics.nodesExplored, + memoryUsed = metrics.memoryUsed, + pathFound = npc.pathFinder.Status == PathFinding.PathFinderStatus.SUCCESS, + testIndex = currentTestIndex + }; + + // Add to results directly + testResults.Add(result); + + // Log result + Debug.Log($"Test {completedTests} (manual): {GetAlgorithmName(result.algorithm)} - {result.timeTaken:F2}ms - Path: {result.pathLength} - Nodes: {result.nodesExplored}"); + } + + // Increment test counter + completedTests++; + } + + private int CalculatePathLength(PathFinding.PathFinder pathFinder) + { + if (pathFinder == null || pathFinder.CurrentNode == null) + return 0; + + int length = 0; + var node = pathFinder.CurrentNode; + while (node != null) + { + length++; + node = node.Parent; + } + return length; + } + + private IEnumerator SetupTestEnvironment(NPC.PathFinderType algorithm, Vector2Int gridSize, float density, bool useDiagonals) + { + Debug.Log($"Setting up test environment: Algorithm={algorithm}, Grid={gridSize.x}x{gridSize.y}, Density={density}, Diagonals={useDiagonals}"); + + // Resize grid + gridMap.ResizeGrid(gridSize.x, gridSize.y); + yield return null; + + // Set algorithm + npc.ChangeAlgorithm(algorithm); + yield return null; + + // Set diagonal movement + gridMap.AllowDiagonalMovement = useDiagonals; + yield return null; + + // Generate maze with specified density + gridMap.GenerateRandomMaze(density); + yield return null; + + // Note: We no longer change visualization settings here + // This is handled in the RunSingleTest method + + yield return null; + } + + private GridNode FindValidStartNode() + { + // Find a walkable node for start position, starting from the top left + for (int x = 0; x < gridMap.NumX; x++) + { + for (int y = 0; y < gridMap.NumY; y++) + { + GridNode node = gridMap.GetGridNode(x, y); + if (node != null && node.IsWalkable) + { + return node; + } + } + } + return null; + } + + private GridNode FindValidDestinationNode(GridNode startNode) + { + if (startNode == null) + return null; + + // Find a walkable node far from the start position, starting from the bottom right + int maxDistance = 0; + GridNode bestNode = null; + + // First try to find a node with good distance + for (int x = gridMap.NumX - 1; x >= 0; x--) + { + for (int y = gridMap.NumY - 1; y >= 0; y--) + { + GridNode node = gridMap.GetGridNode(x, y); + if (node != null && node.IsWalkable && node != startNode) + { + int distance = Mathf.Abs(x - startNode.Value.x) + Mathf.Abs(y - startNode.Value.y); + if (distance > maxDistance) + { + maxDistance = distance; + bestNode = node; + } + } + } + } + + // If we found a node and the distance is decent, use it + if (bestNode != null && maxDistance > gridMap.NumX / 4) + { + return bestNode; + } + + // If no good node was found or distance is too small, try harder to find any walkable node + // different from start node (this helps with very high density mazes) + for (int x = 0; x < gridMap.NumX; x++) + { + for (int y = 0; y < gridMap.NumY; y++) + { + GridNode node = gridMap.GetGridNode(x, y); + if (node != null && node.IsWalkable && node != startNode) + { + return node; // Return the first walkable node that isn't the start + } + } + } + + // If we get here and bestNode is null, there's only one walkable node in the entire grid + // In this case, we have to return null and the test should be skipped + return bestNode; + } + + private void OnPathfindingComplete(PathfindingMetrics metrics) + { + if (!isTestingRunning) + { + Debug.Log("OnPathfindingComplete called but test is not running - ignoring"); + return; + } + + Debug.Log($"OnPathfindingComplete called for test {completedTests+1} with algorithm {currentTestAlgorithm}"); + + // Create a test result entry + PathfindingTestResult result = new PathfindingTestResult + { + algorithm = currentTestAlgorithm, + gridSize = currentTestGridSize, + mazeDensity = currentTestDensity, + diagonalMovement = currentTestDiagonal, + timeTaken = metrics.timeTaken, + pathLength = metrics.pathLength, + nodesExplored = metrics.nodesExplored, + memoryUsed = metrics.memoryUsed, + pathFound = npc.pathFinder.Status == PathFinding.PathFinderStatus.SUCCESS, + testIndex = currentTestIndex + }; + + // Add to results + testResults.Add(result); + + // Log result + Debug.Log($"Test {completedTests+1}: {GetAlgorithmName(result.algorithm)} - {result.timeTaken:F2}ms - Path: {result.pathLength} - Nodes: {result.nodesExplored} - Success: {result.pathFound} - Results count: {testResults.Count}"); + } + + private void SaveResultsToCSV() + { + try + { + Debug.Log($"Saving {testResults.Count} test results to CSV..."); + + if (testResults.Count == 0) + { + Debug.LogWarning("No test results to save!"); + return; + } + + // Create directory if it doesn't exist + string directory = Path.Combine(Application.persistentDataPath, "TestResults"); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + string filePath = Path.Combine(directory, resultsFileName); + + StringBuilder csv = new StringBuilder(); + + // Write header + csv.AppendLine("Algorithm,GridSizeX,GridSizeY,Density,DiagonalMovement,TimeTaken,PathLength,NodesExplored,MemoryUsed,PathFound,TestIndex"); + + // Write each result + foreach (var result in testResults) + { + csv.AppendLine($"{result.algorithm},{result.gridSize.x},{result.gridSize.y},{result.mazeDensity},{result.diagonalMovement}," + + $"{result.timeTaken},{result.pathLength},{result.nodesExplored},{result.memoryUsed},{result.pathFound},{result.testIndex}"); + } + + // Write file + File.WriteAllText(filePath, csv.ToString()); + + Debug.Log($"Test results saved to {filePath}"); + + // Make it easier to find in Windows Explorer + System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{filePath}\""); + } + catch (System.Exception e) + { + Debug.LogError($"Error saving test results: {e.Message}"); + } + } + + // Utility method to get algorithm name as a string + private string GetAlgorithmName(NPC.PathFinderType algorithm) + { + switch (algorithm) + { + case NPC.PathFinderType.ASTAR: return "A*"; + case NPC.PathFinderType.DIJKSTRA: return "Dijkstra"; + case NPC.PathFinderType.GREEDY: return "Greedy"; + case NPC.PathFinderType.BACKTRACKING: return "Backtracking"; + case NPC.PathFinderType.BFS: return "BFS"; + default: return "Unknown"; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/PathfindingTester.cs.meta b/Assets/Scripts/PathfindingTester.cs.meta new file mode 100644 index 0000000..2cef5ac --- /dev/null +++ b/Assets/Scripts/PathfindingTester.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ed03b279fa9f3194f8caed356ea838dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/PathfindingUIManager.cs b/Assets/Scripts/PathfindingUIManager.cs index a98f602..60f891c 100644 --- a/Assets/Scripts/PathfindingUIManager.cs +++ b/Assets/Scripts/PathfindingUIManager.cs @@ -1,7 +1,9 @@ +using System; +using System.Collections; +using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using TMPro; -using System.Collections; using System.IO; public class PathfindingUIManager : MonoBehaviour @@ -52,9 +54,23 @@ public class PathfindingUIManager : MonoBehaviour public TMP_Dropdown mazeSizeDropdown; public TMP_Dropdown mazeDensityDropdown; + [Header("Automated Testing")] + [SerializeField] private GameObject testerPanelPrefab; + [SerializeField] private Transform testerPanelParent; + private GameObject testerPanelInstance; + // Konstanta untuk perhitungan CPU usage private const float TARGET_FRAME_TIME_MS = 16.67f; // 60 FPS = 16.67ms per frame + // Add a flag to track if pathfinding is running + private bool isPathfindingRunning = false; + + // Add a flag to track if visualization is running + private bool isVisualizationRunning = false; + + // Add a flag to track if NPC is moving + private bool isNpcMoving = false; + private void Start() { // Initialize UI elements @@ -91,6 +107,13 @@ public class PathfindingUIManager : MonoBehaviour // Subscribe to NPC's pathfinding events npc.OnPathfindingComplete += UpdatePerformanceMetrics; + npc.OnPathfindingComplete += OnPathfindingCompleted; + + // Subscribe to visualization completion event + npc.OnVisualizationComplete += OnVisualizationCompleted; + + // Subscribe to movement completion event + npc.OnMovementComplete += OnMovementCompleted; // Initialize performance metrics ClearPerformanceMetrics(); @@ -213,6 +236,9 @@ public class PathfindingUIManager : MonoBehaviour if (npc != null) { npc.OnPathfindingComplete -= UpdatePerformanceMetrics; + npc.OnPathfindingComplete -= OnPathfindingCompleted; + npc.OnVisualizationComplete -= OnVisualizationCompleted; + npc.OnMovementComplete -= OnMovementCompleted; } // Unsubscribe from UI events @@ -228,6 +254,14 @@ public class PathfindingUIManager : MonoBehaviour gridSizeXInput.text = gridMap.NumX.ToString(); gridSizeYInput.text = gridMap.NumY.ToString(); + // Set input fields to only accept integers + gridSizeXInput.contentType = TMP_InputField.ContentType.IntegerNumber; + gridSizeYInput.contentType = TMP_InputField.ContentType.IntegerNumber; + + // Add input validation events + gridSizeXInput.onValidateInput += ValidateNumberInput; + gridSizeYInput.onValidateInput += ValidateNumberInput; + // Setup algorithm dropdown algorithmDropdown.ClearOptions(); algorithmDropdown.AddOptions(new System.Collections.Generic.List { @@ -270,6 +304,20 @@ public class PathfindingUIManager : MonoBehaviour ClearPerformanceMetrics(); } + // Validation function to only allow numeric input + private char ValidateNumberInput(string text, int charIndex, char addedChar) + { + // Only allow digits + if (char.IsDigit(addedChar)) + { + return addedChar; + } + else + { + return '\0'; // Return null character to reject the input + } + } + private void ClearPerformanceMetrics() { timeEstimateText.text = "0"; @@ -312,8 +360,42 @@ public class PathfindingUIManager : MonoBehaviour if (int.TryParse(gridSizeXInput.text, out int newSizeX) && int.TryParse(gridSizeYInput.text, out int newSizeY)) { - gridMap.ResizeGrid(newSizeX, newSizeY); - ClearPerformanceMetrics(); + // Validate grid size limits + const int MAX_GRID_SIZE = 200; + const int MIN_GRID_SIZE = 2; + + if (newSizeX > MAX_GRID_SIZE || newSizeY > MAX_GRID_SIZE) + { + // Display an error message + Debug.LogWarning($"Grid size cannot exceed {MAX_GRID_SIZE}x{MAX_GRID_SIZE}. Resize operation cancelled."); + + // Revert input fields to current grid size + gridSizeXInput.text = gridMap.NumX.ToString(); + gridSizeYInput.text = gridMap.NumY.ToString(); + + // Don't proceed with resize + return; + } + + // Check for minimum size + if (newSizeX < MIN_GRID_SIZE || newSizeY < MIN_GRID_SIZE) + { + // Display an error message + Debug.LogWarning($"Grid size cannot be less than {MIN_GRID_SIZE}x{MIN_GRID_SIZE}. Resize operation cancelled."); + + // Revert input fields to current grid size + gridSizeXInput.text = gridMap.NumX.ToString(); + gridSizeYInput.text = gridMap.NumY.ToString(); + + // Don't proceed with resize + return; + } + + // Apply the grid size (only if within limits) + if (gridMap.ResizeGrid(newSizeX, newSizeY)) + { + ClearPerformanceMetrics(); + } } } @@ -336,12 +418,28 @@ public class PathfindingUIManager : MonoBehaviour if (startNode != null && endNode != null) { ClearPerformanceMetrics(); + + // Set flags that pathfinding, visualization, and movement will happen + isPathfindingRunning = true; + isVisualizationRunning = true; + isNpcMoving = true; // assume movement will happen + SetUIInteractivity(false); + npc.MoveTo(endNode); } } private void OnResetPathfinding() { + // Reset all flags to ensure UI will be enabled after reload + isPathfindingRunning = false; + isVisualizationRunning = false; + isNpcMoving = false; + + // Force enable UI - this ensures buttons will be enabled + // after reset regardless of editor/build status + SetUIInteractivity(true); + // Reload the current scene UnityEngine.SceneManagement.SceneManager.LoadScene( UnityEngine.SceneManagement.SceneManager.GetActiveScene().name); @@ -352,10 +450,34 @@ public class PathfindingUIManager : MonoBehaviour private void OnAlgorithmChanged(int index) { NPC.PathFinderType newType = (NPC.PathFinderType)index; + string algorithmName = GetAlgorithmName(newType); + + Debug.Log($"Algorithm changed - Index: {index}, Type: {newType}, Name: {algorithmName}"); + npc.ChangeAlgorithm(newType); ClearPerformanceMetrics(); } + // Helper method to get the readable name of the algorithm + private string GetAlgorithmName(NPC.PathFinderType type) + { + switch (type) + { + case NPC.PathFinderType.ASTAR: + return "A*"; + case NPC.PathFinderType.DIJKSTRA: + return "Dijkstra"; + case NPC.PathFinderType.GREEDY: + return "Greedy BFS"; + case NPC.PathFinderType.BACKTRACKING: + return "Backtracking"; + case NPC.PathFinderType.BFS: + return "BFS"; + default: + return "Unknown"; + } + } + private void OnSaveMap() { if (string.IsNullOrEmpty(mapNameInput.text)) @@ -426,24 +548,45 @@ public class PathfindingUIManager : MonoBehaviour int sizeY = 20; bool isLargeGrid = false; + const int MAX_GRID_SIZE = 200; + const int MIN_GRID_SIZE = 2; + switch (mazeSizeDropdown.value) { case 0: // Kecil sizeX = sizeY = 20; break; case 1: // Sedang - sizeX = sizeY = 50; + sizeX = sizeY = 35; break; case 2: // Besar - sizeX = sizeY = 100; + sizeX = sizeY = 50; isLargeGrid = true; break; + // If more options are added that exceed MAX_GRID_SIZE, they'll be rejected + } + + // Check if size exceeds maximum or is below minimum - reject if it does + if (sizeX > MAX_GRID_SIZE || sizeY > MAX_GRID_SIZE) + { + Debug.LogWarning($"Maze size {sizeX}x{sizeY} exceeds maximum of {MAX_GRID_SIZE}x{MAX_GRID_SIZE}. Operation cancelled."); + return; + } + + if (sizeX < MIN_GRID_SIZE || sizeY < MIN_GRID_SIZE) + { + Debug.LogWarning($"Maze size {sizeX}x{sizeY} is below minimum of {MIN_GRID_SIZE}x{MIN_GRID_SIZE}. Operation cancelled."); + return; } // Resize grid if needed if (gridMap.NumX != sizeX || gridMap.NumY != sizeY) { - gridMap.ResizeGrid(sizeX, sizeY); + if (!gridMap.ResizeGrid(sizeX, sizeY)) + { + // Resize failed, abort maze generation + return; + } // Update grid size inputs gridSizeXInput.text = sizeX.ToString(); @@ -586,4 +729,196 @@ public class PathfindingUIManager : MonoBehaviour // Reset performance metrics ClearPerformanceMetrics(); } + + // New method to handle pathfinding completion + private void OnPathfindingCompleted(PathfindingMetrics metrics) + { + // Pathfinding is completed, but visualization might still be running + isPathfindingRunning = false; + + // Check if pathfinding failed by looking at metrics or path length + bool pathfindingFailed = (metrics.pathLength == 0) || + (npc.pathFinder != null && + npc.pathFinder.Status == PathFinding.PathFinderStatus.FAILURE); + + if (pathfindingFailed) + { + // If pathfinding failed, there won't be any visualization or movement + Debug.Log("Pathfinding failed - re-enabling UI controls immediately"); + isVisualizationRunning = false; + isNpcMoving = false; + + // Only re-enable in editor mode +#if UNITY_EDITOR + SetUIInteractivity(true); +#else + // In build, keep disabled + SetUIInteractivity(false); +#endif + + return; + } + + // If pathfinding succeeded, continue with normal flow + // Very important: Keep UI disabled regardless of visualization state to prevent + // the brief window of interactivity between pathfinding completion and visualization start + if (npc.showVisualization) + { + // If visualization is enabled in settings, assume it will start soon + // Keep UI disabled by keeping isVisualizationRunning true + isVisualizationRunning = true; + // Do NOT enable UI here - wait for visualization to complete + } + else + { + // Only if visualization is completely disabled in settings, enable UI + isVisualizationRunning = false; + + // Only re-enable in editor mode +#if UNITY_EDITOR + SetUIInteractivity(true); +#else + // In build, keep disabled + SetUIInteractivity(false); +#endif + } + } + + // New method to handle visualization completion + private void OnVisualizationCompleted() + { + // Visualization is completed, but NPC may start moving + isVisualizationRunning = false; + + // Check if NPC is moving or will move + if (npc.IsMoving || npc.wayPoints.Count > 0) + { + // Movement is starting or in progress + isNpcMoving = true; + // Leave UI disabled + } + else + { + // No movement expected + isNpcMoving = false; + + // Only re-enable in editor mode +#if UNITY_EDITOR + SetUIInteractivity(true); +#else + // In build, keep disabled + SetUIInteractivity(false); +#endif + } + } + + // New method to handle movement completion + private void OnMovementCompleted() + { + // Movement is completed, re-enable UI buttons only in editor + isNpcMoving = false; + + // Only re-enable in editor mode +#if UNITY_EDITOR + SetUIInteractivity(true); +#else + // In build, keep disabled + Debug.Log("Movement complete but keeping buttons disabled in build mode"); + SetUIInteractivity(false); +#endif + } + + // Method to enable/disable UI elements based on pathfinding, visualization, and movement state + private void SetUIInteractivity(bool enabled) + { + // If any process is running, disable controls + bool shouldEnable = enabled && !isPathfindingRunning && !isVisualizationRunning && !isNpcMoving; + + // In builds (not editor), once disabled, buttons stay disabled until reset +#if !UNITY_EDITOR + if (shouldEnable && (isPathfindingRunning || isVisualizationRunning || isNpcMoving)) + { + // In builds, once pathfinding started, keep buttons disabled regardless + Debug.Log("In build - keeping buttons disabled even after completion"); + shouldEnable = false; + } +#endif + + // Add debug logging + Debug.Log($"SetUIInteractivity called with enabled={enabled}, pathfinding={isPathfindingRunning}, " + + $"visualization={isVisualizationRunning}, movement={isNpcMoving}, shouldEnable={shouldEnable}, " + + $"inEditor={Application.isEditor}"); + + // Keep reset and exit buttons always enabled + // Disable all other buttons when processes are running + + if (applyGridSizeButton != null) + applyGridSizeButton.interactable = shouldEnable; + + if (runPathfindingButton != null) + runPathfindingButton.interactable = shouldEnable; + + if (algorithmDropdown != null) + algorithmDropdown.interactable = shouldEnable; + + if (allowDiagonalToggle != null) + allowDiagonalToggle.interactable = shouldEnable; + + if (saveButton != null) + saveButton.interactable = shouldEnable; + + if (loadButton != null) + loadButton.interactable = shouldEnable; + + if (generateMazeButton != null) + generateMazeButton.interactable = shouldEnable; + + if (visualizationSpeedSlider != null) + visualizationSpeedSlider.interactable = shouldEnable; + + if (visualizationBatchSlider != null) + visualizationBatchSlider.interactable = shouldEnable; + + if (mazeSizeDropdown != null) + mazeSizeDropdown.interactable = shouldEnable; + + if (mazeDensityDropdown != null) + mazeDensityDropdown.interactable = shouldEnable; + + if (gridSizeXInput != null) + gridSizeXInput.interactable = shouldEnable; + + if (gridSizeYInput != null) + gridSizeYInput.interactable = shouldEnable; + + if (mapNameInput != null) + mapNameInput.interactable = shouldEnable; + + // Reset and exit buttons remain enabled + // resetButton and exitButton stay interactable + } + + private void Update() + { + // Continuously check for various states - this ensures buttons stay disabled + if (npc != null) + { + // Check visualization + if (npc.IsVisualizingPath && !isVisualizationRunning) + { + Debug.Log("Detected active visualization - updating UI state"); + isVisualizationRunning = true; + SetUIInteractivity(false); + } + + // Check movement + if (npc.IsMoving && !isNpcMoving) + { + Debug.Log("Detected active NPC movement - updating UI state"); + isNpcMoving = true; + SetUIInteractivity(false); + } + } + } + } \ No newline at end of file diff --git a/Assets/Scripts/TestPlan.md b/Assets/Scripts/TestPlan.md new file mode 100644 index 0000000..df60eb3 --- /dev/null +++ b/Assets/Scripts/TestPlan.md @@ -0,0 +1,167 @@ +# Rencana Pengujian Sistem Pathfinding + +Dokumen ini menguraikan pendekatan pengujian komprehensif untuk sistem pathfinding, termasuk pengujian komponen sistem dan pengujian kinerja algoritma. + +## I. Pengujian Komponen Sistem + +### 1. Pengujian Input Ukuran Grid + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| GS-01 | Memasukkan ukuran grid yang valid (mis., 20x20) | Grid diubah ukuran dengan benar | □ | +| GS-02 | Memasukkan ukuran grid maksimum (200x200) | Grid diubah ukuran ke ukuran maksimum | □ | +| GS-03 | Memasukkan nilai melebihi maksimum (mis., 201x201) | Operasi ditolak, peringatan ditampilkan | □ | +| GS-04 | Memasukkan nilai non-numerik | Input ditolak | □ | +| GS-05 | Memasukkan nilai negatif atau nol | Input ditolak | □ | +| GS-06 | Mengubah ukuran selama pathfinding | Operasi diblokir selama pathfinding | □ | +| GS-07 | Menguji grid tidak persegi (mis., 20x30) | Grid membuat bentuk persegi panjang yang benar | □ | + +### 2. Pengujian Dropdown Algoritma + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| ALG-01 | Pilih A* | Algoritma berubah ke A* | □ | +| ALG-02 | Pilih Dijkstra | Algoritma berubah ke Dijkstra | □ | +| ALG-03 | Pilih Greedy | Algoritma berubah ke Greedy | □ | +| ALG-04 | Pilih Backtracking | Algoritma berubah ke Backtracking | □ | +| ALG-05 | Pilih BFS | Algoritma berubah ke BFS | □ | +| ALG-06 | Mengubah algoritma selama pathfinding | Operasi diblokir selama pathfinding | □ | + +### 3. Pengujian Toggle Pergerakan Diagonal + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| DIAG-01 | Aktifkan pergerakan diagonal | Pergerakan diagonal diaktifkan, jalur dapat menggunakan diagonal | □ | +| DIAG-02 | Nonaktifkan pergerakan diagonal | Pergerakan diagonal dinonaktifkan, jalur hanya menggunakan arah kardinal | □ | +| DIAG-03 | Toggle selama pathfinding | Operasi diblokir selama pathfinding | □ | + +### 4. Pengujian Kontrol Visualisasi + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| VIS-01 | Menyesuaikan kecepatan visualisasi (meningkat) | Visualisasi berjalan lebih cepat | □ | +| VIS-02 | Menyesuaikan kecepatan visualisasi (menurun) | Visualisasi berjalan lebih lambat | □ | +| VIS-03 | Mengatur ukuran batch ke 1 | Setiap langkah divisualisasikan secara individual | □ | +| VIS-04 | Mengatur ukuran batch ke nilai lebih tinggi | Beberapa langkah divisualisasikan bersama | □ | +| VIS-05 | Menyesuaikan kontrol selama pathfinding | Operasi diblokir selama pathfinding | □ | + +### 5. Pengujian Generator Labirin + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| MAZE-01 | Menghasilkan labirin kecil (kepadatan rendah) | Labirin dihasilkan dengan sedikit rintangan | □ | +| MAZE-02 | Menghasilkan labirin sedang (kepadatan sedang) | Labirin dihasilkan dengan rintangan moderat | □ | +| MAZE-03 | Menghasilkan labirin besar (kepadatan tinggi) | Labirin dihasilkan dengan banyak rintangan | □ | +| MAZE-04 | Menghasilkan labirin dengan kepadatan 0% | Grid kosong sepenuhnya dihasilkan | □ | +| MAZE-05 | Menghasilkan labirin dengan kepadatan 100% | Grid terisi penuh dihasilkan | □ | +| MAZE-06 | Menghasilkan labirin selama pathfinding | Operasi diblokir selama pathfinding | □ | + +### 6. Pengujian Simpan dan Muat + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| SAVE-01 | Simpan dengan nama file valid | Labirin berhasil disimpan | □ | +| SAVE-02 | Simpan dengan nama file kosong | Pesan kesalahan ditampilkan | □ | +| SAVE-03 | Simpan selama pathfinding | Operasi diblokir selama pathfinding | □ | +| LOAD-01 | Muat file yang ada | Labirin berhasil dimuat | □ | +| LOAD-02 | Muat dengan nama file yang tidak ada | Pesan kesalahan ditampilkan | □ | +| LOAD-03 | Muat selama pathfinding | Operasi diblokir selama pathfinding | □ | + +### 7. Pengujian Tombol Muat Ulang + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| RELOAD-01 | Klik tombol muat ulang | Scene dimuat ulang ke keadaan awal | □ | +| RELOAD-02 | Klik muat ulang selama pathfinding | Scene dimuat ulang, operasi diizinkan selama pathfinding | □ | +| RELOAD-03 | Klik muat ulang setelah pathfinding dalam mode build | Tombol diaktifkan kembali setelah reset | □ | + +### 8. Pengujian Status UI + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| UI-01 | Mulai pathfinding | Semua tombol dinonaktifkan kecuali reload dan exit | □ | +| UI-02 | Setelah pathfinding selesai (editor) | Tombol diaktifkan kembali | □ | +| UI-03 | Setelah pathfinding selesai (build) | Tombol tetap dinonaktifkan | □ | +| UI-04 | Interaksi mouse selama pathfinding | Repositioning NPC/tujuan diblokir | □ | + +## II. Pengujian Kinerja Algoritma + +### 1. Pengujian Algoritma A* + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| ASTAR-01 | Grid kecil (20x20), kepadatan rendah | Jalur ditemukan secara efisien | □ | +| ASTAR-02 | Grid sedang (50x50), kepadatan sedang | Jalur ditemukan dengan kinerja wajar | □ | +| ASTAR-03 | Grid besar (100x100), kepadatan tinggi | Jalur ditemukan tanpa waktu/memori berlebihan | □ | +| ASTAR-04 | Jalur mustahil (kepadatan 100%) | Dengan benar melaporkan tidak ada jalur yang ditemukan | □ | +| ASTAR-05 | Dengan pergerakan diagonal | Jalur diagonal yang lebih pendek digunakan | □ | +| ASTAR-06 | Tanpa pergerakan diagonal | Hanya jalur kardinal yang digunakan | □ | + +### 2. Pengujian Algoritma Dijkstra + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| DIJK-01 | Grid kecil (20x20), kepadatan rendah | Jalur ditemukan dengan eksplorasi lebih dari A* | □ | +| DIJK-02 | Grid sedang (50x50), kepadatan sedang | Jalur ditemukan dengan waktu/memori lebih tinggi dari A* | □ | +| DIJK-03 | Grid besar (100x100), kepadatan tinggi | Jalur ditemukan, mungkin dengan penggunaan sumber daya tinggi | □ | +| DIJK-04 | Jalur mustahil (kepadatan 100%) | Dengan benar melaporkan tidak ada jalur yang ditemukan | □ | +| DIJK-05 | Dengan pergerakan diagonal | Jalur optimal ditemukan | □ | +| DIJK-06 | Tanpa pergerakan diagonal | Jalur kardinal optimal ditemukan | □ | + +### 3. Pengujian Greedy Best-First Search + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| GREEDY-01 | Grid kecil (20x20), kepadatan rendah | Kinerja cepat, jalur berpotensi tidak optimal | □ | +| GREEDY-02 | Grid sedang (50x50), kepadatan sedang | Kinerja cepat dengan memori lebih sedikit dari A*/Dijkstra | □ | +| GREEDY-03 | Grid besar (100x100), kepadatan tinggi | Lebih cepat dari A*/Dijkstra, tetapi mungkin tidak optimal | □ | +| GREEDY-04 | Jalur mustahil (kepadatan 100%) | Dengan benar melaporkan tidak ada jalur yang ditemukan | □ | +| GREEDY-05 | Labirin dengan bottleneck | Mungkin menghasilkan jalur yang tidak optimal | □ | + +### 4. Pengujian Algoritma Backtracking + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| BACK-01 | Grid kecil (20x20), kepadatan rendah | Jalur ditemukan, kemungkinan lebih lambat dari algoritma lain | □ | +| BACK-02 | Grid sedang (50x50), kepadatan sedang | Degradasi kinerja dengan ukuran meningkat | □ | +| BACK-03 | Labirin kecil dengan kepadatan tinggi | Mungkin kesulitan dengan labirin kompleks | □ | +| BACK-04 | Jalur mustahil (kepadatan 100%) | Dengan benar melaporkan tidak ada jalur yang ditemukan | □ | + +### 5. Pengujian Algoritma BFS + +| ID Tes | Kasus Uji | Hasil yang Diharapkan | Lulus/Gagal | +|---------|-----------|----------------|-----------| +| BFS-01 | Grid kecil (20x20), kepadatan rendah | Jalur optimal untuk grid tanpa bobot | □ | +| BFS-02 | Grid sedang (50x50), kepadatan sedang | Penggunaan memori lebih tinggi dari A* | □ | +| BFS-03 | Grid besar (100x100), kepadatan tinggi | Konsumsi memori tinggi | □ | +| BFS-04 | Jalur mustahil (kepadatan 100%) | Dengan benar melaporkan tidak ada jalur yang ditemukan | □ | + +## III. Matriks Kinerja Sistem Keseluruhan + +| Algoritma | Grid Kecil | Grid Sedang | Grid Besar | Kepadatan Rendah | Kepadatan Tinggi | Dengan Diagonal | Tanpa Diagonal | +|-----------|------------|-------------|------------|------------------|-----------------|----------------|----------------| +| A* | □ | □ | □ | □ | □ | □ | □ | +| Dijkstra | □ | □ | □ | □ | □ | □ | □ | +| Greedy | □ | □ | □ | □ | □ | □ | □ | +| Backtracking | □ | □ | □ | □ | □ | □ | □ | +| BFS | □ | □ | □ | □ | □ | □ | □ | + +## IV. Instruksi Pelaksanaan Pengujian + +1. **Persiapan**: + - Pastikan proyek dalam keadaan bersih + - Untuk pengujian sistem, uji satu komponen pada satu waktu + - Untuk pengujian algoritma, gunakan penguji otomatis dengan berbagai kombinasi + +2. **Dokumentasi**: + - Tandai setiap pengujian sebagai Lulus/Gagal dalam daftar periksa + - Catat anomali atau perilaku tidak terduga + - Dokumentasikan metrik kinerja jika berlaku + +3. **Pengujian Regresi**: + - Setelah memperbaiki bug, jalankan kembali pengujian terkait untuk memastikan lulus + - Secara berkala jalankan rangkaian pengujian lengkap untuk menangkap regresi + +4. **Pengujian Otomatis**: + - Gunakan PathfindingTester untuk pengujian algoritma otomatis + - Rekam dan analisis output CSV untuk perbandingan komprehensif \ No newline at end of file diff --git a/Assets/Scripts/TestPlan.md.meta b/Assets/Scripts/TestPlan.md.meta new file mode 100644 index 0000000..9407575 --- /dev/null +++ b/Assets/Scripts/TestPlan.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 97b0707a63200c94cb3f55a0beb75ef2 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/TestScene_README.txt b/Assets/Scripts/TestScene_README.txt new file mode 100644 index 0000000..699b2df --- /dev/null +++ b/Assets/Scripts/TestScene_README.txt @@ -0,0 +1,69 @@ +# Pathfinding Greybox Matrix Testing Setup Instructions + +## Overview +This document outlines the step-by-step process for setting up a scene to perform automated greybox matrix testing of the pathfinding algorithms. + +## Scene Setup Steps + +1. Create a new scene or use an existing pathfinding scene + +2. Add the required components to the scene: + - GridMap + - NPC + - UI Canvas for the test controls + +3. Create the following UI elements on the Canvas: + - Panel (as a container) + - Text (TMP) for status display + - Button for starting tests + - Slider for progress visualization + +4. Create an empty GameObject and attach the PathfindingTester.cs script + +5. Configure the PathfindingTester component: + - Assign the GridMap reference + - Assign the NPC reference + - Assign the UI components (button, text, progress bar) + - Configure test parameters as needed: + - Tests Per Combination + - Delay Between Tests + - Results File Name + +## UI Layout Recommendations + +1. Place the status text at the top of the panel to show current test status +2. Place the progress bar below the status text +3. Place the "Start Tests" button at the bottom +4. Make the panel semi-transparent so you can still see the grid behind it + +## Test Matrix Configuration + +You can customize the test matrix by modifying these arrays in the PathfindingTester.cs script: + +- algorithmsToTest: Which algorithms to test +- gridSizesToTest: Which grid sizes to test +- mazeDensitiesToTest: Which maze densities to test +- diagonalMovementOptions: Whether to test with diagonals enabled/disabled + +## Running Tests + +1. Enter Play mode in the Unity editor +2. Click the "Start Tests" button +3. Wait for all tests to complete +4. Test results will be saved in a CSV file in the "TestResults" folder in the persistent data path + +## Analyzing Results + +The CSV file contains detailed information about each test: +- Algorithm used +- Grid size +- Maze density +- Diagonal movement setting +- Time taken (ms) +- Path length (nodes) +- Nodes explored +- Memory used +- Whether a path was found +- Test index (for repeated tests) + +Import the CSV into a spreadsheet application for further analysis and visualization. \ No newline at end of file diff --git a/Assets/Scripts/TestScene_README.txt.meta b/Assets/Scripts/TestScene_README.txt.meta new file mode 100644 index 0000000..07e716b --- /dev/null +++ b/Assets/Scripts/TestScene_README.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5fd2581c099e0644fa3c48212c93208d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/pathfinding_tests.csv b/Assets/Scripts/pathfinding_tests.csv new file mode 100644 index 0000000..eb68217 --- /dev/null +++ b/Assets/Scripts/pathfinding_tests.csv @@ -0,0 +1,271 @@ +Algorithm,GridSizeX,GridSizeY,Density,DiagonalMovement,TimeTaken,PathLength,NodesExplored,MemoryUsed,PathFound,TestIndex +ASTAR,20,20,10,True,1.0282,24,26,4096,True,0 +ASTAR,20,20,10,True,0.1261,22,22,4096,True,1 +ASTAR,20,20,10,True,0.1369,21,20,4096,True,2 +ASTAR,20,20,10,False,0.12,39,38,4096,True,0 +ASTAR,20,20,10,False,0.1708,39,53,8192,True,1 +ASTAR,20,20,10,False,0.2009,39,69,12288,True,2 +ASTAR,20,20,30,True,0.1264,26,27,8192,True,0 +ASTAR,20,20,30,True,0.1366,26,29,8192,True,1 +ASTAR,20,20,30,True,0.1186,24,25,8192,True,2 +ASTAR,20,20,30,False,0.1441,39,45,4096,True,0 +ASTAR,20,20,30,False,0.1042,39,48,12288,True,1 +ASTAR,20,20,30,False,0.2301,39,87,24576,True,2 +ASTAR,20,20,50,True,0.1618,43,89,16384,True,0 +ASTAR,20,20,50,True,0.3753,54,171,8192,True,1 +ASTAR,20,20,50,True,0.2745,48,130,8192,True,2 +ASTAR,20,20,50,False,0.1702,63,104,0,True,0 +ASTAR,20,20,50,False,0.1628,59,98,12288,True,1 +ASTAR,20,20,50,False,0.3159,135,180,40960,True,2 +ASTAR,35,35,10,True,0.2377,36,35,16384,True,0 +ASTAR,35,35,10,True,0.2482,38,39,16384,True,1 +ASTAR,35,35,10,True,0.2412,40,39,16384,True,2 +ASTAR,35,35,10,False,0.2433,69,68,8192,True,0 +ASTAR,35,35,10,False,0.281,69,95,24576,True,1 +ASTAR,35,35,10,False,0.2874,69,93,20480,True,2 +ASTAR,35,35,30,True,0.2388,47,46,20480,True,0 +ASTAR,35,35,30,True,0.3082,45,68,4096,True,1 +ASTAR,35,35,30,True,0.2438,42,44,4096,True,2 +ASTAR,35,35,30,False,0.3319,69,111,4096,True,0 +ASTAR,35,35,30,False,0.4247,69,139,20480,True,1 +ASTAR,35,35,30,False,0.2607,69,86,12288,True,2 +ASTAR,35,35,50,True,0.4856,101,261,36864,True,0 +ASTAR,35,35,50,True,1.0796,219,563,98304,True,1 +ASTAR,35,35,50,True,1.0919,97,529,114688,True,2 +ASTAR,35,35,50,False,0.4922,145,300,69632,True,0 +ASTAR,35,35,50,False,0.2173,97,134,24576,True,1 +ASTAR,35,35,50,False,0.4082,77,191,28672,True,2 +ASTAR,50,50,10,True,0.3457,54,53,40960,True,0 +ASTAR,50,50,10,True,0.3793,54,53,16384,True,1 +ASTAR,50,50,10,True,0.3698,57,56,16384,True,2 +ASTAR,50,50,10,False,0.3816,99,98,20480,True,0 +ASTAR,50,50,10,False,0.3855,99,120,20480,True,1 +ASTAR,50,50,10,False,0.4885,99,158,24576,True,2 +ASTAR,50,50,30,True,0.3388,59,61,16384,True,0 +ASTAR,50,50,30,True,0.3565,57,59,20480,True,1 +ASTAR,50,50,30,True,0.3343,64,67,20480,True,2 +ASTAR,50,50,30,False,1.1702,99,412,102400,True,0 +ASTAR,50,50,30,False,0.4831,99,153,32768,True,1 +ASTAR,50,50,30,False,0.4641,99,200,8192,True,2 +ASTAR,50,50,50,True,2.2106,147,926,73728,True,0 +ASTAR,50,50,50,True,1.8848,162,854,73728,True,1 +ASTAR,50,50,50,True,1.8428,305,1033,196608,True,2 +ASTAR,50,50,50,False,1.3853,259,813,155648,True,0 +ASTAR,50,50,50,False,1.3047,289,809,151552,True,1 +ASTAR,50,50,50,False,0.7769,223,485,77824,True,2 +DIJKSTRA,20,20,10,True,0.5732,21,360,69632,True,0 +DIJKSTRA,20,20,10,True,0.6119,21,360,69632,True,1 +DIJKSTRA,20,20,10,True,0.5995,20,360,81920,True,2 +DIJKSTRA,20,20,10,False,0.4575,39,360,126976,True,0 +DIJKSTRA,20,20,10,False,0.4468,39,359,114688,True,1 +DIJKSTRA,20,20,10,False,0.4462,39,361,32768,True,2 +DIJKSTRA,20,20,30,True,0.4703,23,281,32768,True,0 +DIJKSTRA,20,20,30,True,0.4805,25,281,32768,True,1 +DIJKSTRA,20,20,30,True,0.4821,24,281,61440,True,2 +DIJKSTRA,20,20,30,False,0.3919,39,282,65536,True,0 +DIJKSTRA,20,20,30,False,0.3812,41,280,61440,True,1 +DIJKSTRA,20,20,30,False,0.3457,39,281,61440,True,2 +DIJKSTRA,20,20,50,True,0.2829,44,197,28672,True,0 +DIJKSTRA,20,20,50,True,0.2141,58,144,20480,True,1 +DIJKSTRA,20,20,50,True,0.2861,66,192,32768,True,2 +DIJKSTRA,20,20,50,False,0.2427,59,189,28672,True,0 +DIJKSTRA,20,20,50,False,0.1485,51,115,24576,True,1 +DIJKSTRA,20,20,50,False,0.1985,91,159,45056,True,2 +DIJKSTRA,35,35,10,True,2.1114,37,1103,327680,True,0 +DIJKSTRA,35,35,10,True,2.0576,37,1103,94208,True,1 +DIJKSTRA,35,35,10,True,2.1407,37,1104,94208,True,2 +DIJKSTRA,35,35,10,False,1.5362,69,1103,253952,True,0 +DIJKSTRA,35,35,10,False,1.4893,69,1103,204800,True,1 +DIJKSTRA,35,35,10,False,1.4763,69,1103,200704,True,2 +DIJKSTRA,35,35,30,True,1.5209,42,858,163840,True,0 +DIJKSTRA,35,35,30,True,1.435,42,857,155648,True,1 +DIJKSTRA,35,35,30,True,1.4548,40,857,159744,True,2 +DIJKSTRA,35,35,30,False,1.129,69,856,253952,True,0 +DIJKSTRA,35,35,30,False,1.1949,69,855,253952,True,1 +DIJKSTRA,35,35,30,False,1.1977,69,856,294912,True,2 +DIJKSTRA,35,35,50,True,0.6041,136,400,28672,True,0 +DIJKSTRA,35,35,50,True,0.6758,145,487,28672,True,1 +DIJKSTRA,35,35,50,True,0.6081,137,424,28672,True,2 +DIJKSTRA,35,35,50,False,0.58,0,0,8192,False,0 +DIJKSTRA,35,35,50,False,0.4809,129,362,65536,True,1 +DIJKSTRA,35,35,50,False,0.7123,77,532,98304,True,2 +DIJKSTRA,50,50,10,True,4.5189,51,2250,409600,True,0 +DIJKSTRA,50,50,10,True,4.5321,53,2250,409600,True,1 +DIJKSTRA,50,50,10,True,4.3824,54,2251,573440,True,2 +DIJKSTRA,50,50,10,False,3.1707,99,2250,659456,True,0 +DIJKSTRA,50,50,10,False,3.3113,99,2250,663552,True,1 +DIJKSTRA,50,50,10,False,3.3981,99,2251,188416,True,2 +DIJKSTRA,50,50,30,True,3.2778,58,1750,258048,True,0 +DIJKSTRA,50,50,30,True,3.0818,58,1751,327680,True,1 +DIJKSTRA,50,50,30,True,3.1183,58,1750,323584,True,2 +DIJKSTRA,50,50,30,False,2.4624,99,1749,323584,True,0 +DIJKSTRA,50,50,30,False,2.5493,99,1751,323584,True,1 +DIJKSTRA,50,50,30,False,2.4891,99,1749,319488,True,2 +DIJKSTRA,50,50,50,True,1.2986,301,880,217088,True,0 +DIJKSTRA,50,50,50,True,1.6238,194,1187,344064,True,1 +DIJKSTRA,50,50,50,True,2.5347,174,565,184320,True,2 +DIJKSTRA,50,50,50,False,1.2548,271,896,73728,True,0 +DIJKSTRA,50,50,50,False,1.3304,409,934,90112,True,1 +DIJKSTRA,50,50,50,False,0.7657,357,612,122880,True,2 +GREEDY,20,20,10,True,0.1293,22,22,8192,True,0 +GREEDY,20,20,10,True,0.1508,21,21,8192,True,1 +GREEDY,20,20,10,True,0.1445,21,21,4096,True,2 +GREEDY,20,20,10,False,0.1191,39,39,4096,True,0 +GREEDY,20,20,10,False,0.1347,41,41,4096,True,1 +GREEDY,20,20,10,False,0.1692,43,46,12288,True,2 +GREEDY,20,20,30,True,0.1515,25,27,4096,True,0 +GREEDY,20,20,30,True,0.1471,25,25,8192,True,1 +GREEDY,20,20,30,True,0.1325,28,28,8192,True,2 +GREEDY,20,20,30,False,0.166,43,46,0,True,0 +GREEDY,20,20,30,False,0.1449,39,45,0,True,1 +GREEDY,20,20,30,False,0.1382,41,45,0,True,2 +GREEDY,20,20,50,True,0.2456,47,95,0,True,0 +GREEDY,20,20,50,True,0.3083,60,119,12288,True,1 +GREEDY,20,20,50,True,0.2403,60,80,8192,True,2 +GREEDY,20,20,50,False,0.2485,67,115,12288,True,0 +GREEDY,20,20,50,False,0.173,59,91,20480,True,1 +GREEDY,20,20,50,False,0.3466,79,193,20480,True,2 +GREEDY,35,35,10,True,0.2559,39,39,16384,True,0 +GREEDY,35,35,10,True,0.2487,38,38,16384,True,1 +GREEDY,35,35,10,True,0.2573,40,40,24576,True,2 +GREEDY,35,35,10,False,0.3217,83,95,20480,True,0 +GREEDY,35,35,10,False,0.6061,69,69,32768,True,1 +GREEDY,35,35,10,False,0.281,69,69,4096,True,2 +GREEDY,35,35,30,True,0.2408,43,45,8192,True,0 +GREEDY,35,35,30,True,0.2331,43,43,4096,True,1 +GREEDY,35,35,30,True,0.2392,42,42,12288,True,2 +GREEDY,35,35,30,False,0.225,71,77,8192,True,0 +GREEDY,35,35,30,False,0.2613,83,83,12288,True,1 +GREEDY,35,35,30,False,0.23,71,71,8192,True,2 +GREEDY,35,35,50,True,0.9538,91,365,65536,True,0 +GREEDY,35,35,50,True,0.9843,147,364,81920,True,1 +GREEDY,35,35,50,True,0.7238,197,300,94208,True,2 +GREEDY,35,35,50,False,0.3991,147,194,28672,True,0 +GREEDY,35,35,50,False,0.4047,117,211,49152,True,1 +GREEDY,35,35,50,False,0.3874,183,196,4096,True,2 +GREEDY,50,50,10,True,0.3848,57,58,16384,True,0 +GREEDY,50,50,10,True,0.3706,58,58,20480,True,1 +GREEDY,50,50,10,True,0.3754,54,54,24576,True,2 +GREEDY,50,50,10,False,0.3671,99,99,28672,True,0 +GREEDY,50,50,10,False,0.3749,99,99,12288,True,1 +GREEDY,50,50,10,False,0.3661,101,102,24576,True,2 +GREEDY,50,50,30,True,0.3392,63,63,24576,True,0 +GREEDY,50,50,30,True,0.3247,64,64,20480,True,1 +GREEDY,50,50,30,True,0.3234,68,69,24576,True,2 +GREEDY,50,50,30,False,0.4684,123,147,8192,True,0 +GREEDY,50,50,30,False,0.3809,107,108,4096,True,1 +GREEDY,50,50,30,False,0.3617,101,109,4096,True,2 +GREEDY,50,50,50,True,0.7341,208,268,45056,True,0 +GREEDY,50,50,50,True,0.849,172,316,65536,True,1 +GREEDY,50,50,50,True,0.6744,168,222,32768,True,2 +GREEDY,50,50,50,False,1.0245,211,568,98304,True,0 +GREEDY,50,50,50,False,1.2286,507,721,143360,True,1 +GREEDY,50,50,50,False,1.2725,419,670,172032,True,2 +BACKTRACKING,20,20,10,True,0.3594,121,131,69632,True,0 +BACKTRACKING,20,20,10,True,0.3929,111,164,61440,True,1 +BACKTRACKING,20,20,10,True,0.2945,104,116,65536,True,2 +BACKTRACKING,20,20,10,False,0.4242,183,241,28672,True,0 +BACKTRACKING,20,20,10,False,0.3568,187,205,28672,True,1 +BACKTRACKING,20,20,10,False,0.3895,177,221,28672,True,2 +BACKTRACKING,20,20,30,True,0.3235,75,129,28672,True,0 +BACKTRACKING,20,20,30,True,0.3471,77,167,36864,True,1 +BACKTRACKING,20,20,30,True,0.3286,92,140,36864,True,2 +BACKTRACKING,20,20,30,False,0.3422,147,203,45056,True,0 +BACKTRACKING,20,20,30,False,0.2824,137,191,32768,True,1 +BACKTRACKING,20,20,30,False,0.3286,157,193,36864,True,2 +BACKTRACKING,20,20,50,True,0.2677,30,137,28672,True,0 +BACKTRACKING,20,20,50,True,0.2214,37,130,24576,True,1 +BACKTRACKING,20,20,50,True,0.1721,42,104,20480,True,2 +BACKTRACKING,20,20,50,False,0.1367,71,124,36864,True,0 +BACKTRACKING,20,20,50,False,0.2432,79,179,8192,True,1 +BACKTRACKING,20,20,50,False,0.2324,43,186,8192,True,2 +BACKTRACKING,35,35,10,True,1.465,328,401,106496,True,0 +BACKTRACKING,35,35,10,True,1.3811,323,409,188416,True,1 +BACKTRACKING,35,35,10,True,1.1492,285,349,147456,True,2 +BACKTRACKING,35,35,10,False,1.4213,521,635,188416,True,0 +BACKTRACKING,35,35,10,False,1.4778,527,680,188416,True,1 +BACKTRACKING,35,35,10,False,1.4704,573,649,184320,True,2 +BACKTRACKING,35,35,30,True,1.1103,290,408,110592,True,0 +BACKTRACKING,35,35,30,True,1.0674,241,456,208896,True,1 +BACKTRACKING,35,35,30,True,1.1679,270,445,212992,True,2 +BACKTRACKING,35,35,30,False,0.931,429,556,245760,True,0 +BACKTRACKING,35,35,30,False,1.0727,403,561,69632,True,1 +BACKTRACKING,35,35,30,False,1.5718,401,618,69632,True,2 +BACKTRACKING,35,35,50,True,0.6532,77,428,24576,True,0 +BACKTRACKING,35,35,50,True,0.2979,79,179,28672,True,1 +BACKTRACKING,35,35,50,True,0.4046,125,241,40960,True,2 +BACKTRACKING,35,35,50,False,0.3571,0,0,4096,False,0 +BACKTRACKING,35,35,50,False,0.652,193,506,102400,True,1 +BACKTRACKING,35,35,50,False,0.3799,0,0,16384,False,2 +BACKTRACKING,50,50,10,True,3.1519,636,758,438272,True,0 +BACKTRACKING,50,50,10,True,2.5557,565,614,495616,True,1 +BACKTRACKING,50,50,10,True,3.3404,651,845,610304,True,2 +BACKTRACKING,50,50,10,False,3.7901,1137,1239,241664,True,0 +BACKTRACKING,50,50,10,False,3.601,1099,1299,200704,True,1 +BACKTRACKING,50,50,10,False,3.2003,967,1197,319488,True,2 +BACKTRACKING,50,50,30,True,2.6415,593,733,212992,True,0 +BACKTRACKING,50,50,30,True,2.1968,520,708,217088,True,1 +BACKTRACKING,50,50,30,True,2.4687,557,787,225280,True,2 +BACKTRACKING,50,50,30,False,2.5318,703,1337,294912,True,0 +BACKTRACKING,50,50,30,False,2.1927,687,1132,430080,True,1 +BACKTRACKING,50,50,30,False,2.2509,821,1179,438272,True,2 +BACKTRACKING,50,50,50,True,1.2183,145,849,262144,True,0 +BACKTRACKING,50,50,50,True,0.8606,234,570,204800,True,1 +BACKTRACKING,50,50,50,True,1.8035,150,1043,131072,True,2 +BACKTRACKING,50,50,50,False,1.2391,655,933,98304,True,0 +BACKTRACKING,50,50,50,False,0.9434,335,763,131072,True,1 +BACKTRACKING,50,50,50,False,0.7697,195,577,114688,True,2 +BFS,20,20,10,True,0.4677,20,360,61440,True,0 +BFS,20,20,10,True,0.621,21,361,77824,True,1 +BFS,20,20,10,True,0.5292,21,361,65536,True,2 +BFS,20,20,10,False,0.3891,39,361,65536,True,0 +BFS,20,20,10,False,0.4235,39,361,65536,True,1 +BFS,20,20,10,False,0.4674,39,360,90112,True,2 +BFS,20,20,30,True,0.3917,25,282,73728,True,0 +BFS,20,20,30,True,0.3715,26,281,86016,True,1 +BFS,20,20,30,True,0.449,23,282,24576,True,2 +BFS,20,20,30,False,0.3117,39,255,16384,True,0 +BFS,20,20,30,False,0.3499,39,281,24576,True,1 +BFS,20,20,30,False,0.3472,39,282,57344,True,2 +BFS,20,20,50,True,0.2092,42,144,20480,True,0 +BFS,20,20,50,True,0.2498,41,161,32768,True,1 +BFS,20,20,50,True,0.2399,38,163,24576,True,2 +BFS,20,20,50,False,0.2321,59,155,24576,True,0 +BFS,20,20,50,False,0.2403,71,162,24576,True,1 +BFS,20,20,50,False,0.1412,51,100,16384,True,2 +BFS,35,35,10,True,1.679,38,1104,233472,True,0 +BFS,35,35,10,True,1.6694,36,1103,294912,True,1 +BFS,35,35,10,True,1.7347,39,1104,425984,True,2 +BFS,35,35,10,False,1.4652,69,1103,118784,True,0 +BFS,35,35,10,False,1.3521,69,1103,118784,True,1 +BFS,35,35,10,False,1.3525,69,1103,221184,True,2 +BFS,35,35,30,True,1.2777,40,855,139264,True,0 +BFS,35,35,30,True,1.2761,43,857,143360,True,1 +BFS,35,35,30,True,1.3204,43,857,147456,True,2 +BFS,35,35,30,False,1.1577,69,857,176128,True,0 +BFS,35,35,30,False,1.1088,69,854,143360,True,1 +BFS,35,35,30,False,1.1091,69,857,188416,True,2 +BFS,35,35,50,True,0.6461,53,490,155648,True,0 +BFS,35,35,50,True,0.6363,88,510,155648,True,1 +BFS,35,35,50,True,0.4843,100,350,110592,True,2 +BFS,35,35,50,False,0.9047,79,544,57344,True,0 +BFS,35,35,50,False,0.3727,0,0,4096,False,1 +BFS,35,35,50,False,0.4923,0,0,8192,False,2 +BFS,50,50,10,True,3.7701,53,2250,581632,True,0 +BFS,50,50,10,True,3.4752,52,2250,462848,True,1 +BFS,50,50,10,True,3.5727,52,2250,458752,True,2 +BFS,50,50,10,False,2.7849,99,2251,471040,True,0 +BFS,50,50,10,False,2.7301,99,2251,598016,True,1 +BFS,50,50,10,False,2.6218,99,2251,716800,True,2 +BFS,50,50,30,True,2.346,62,1751,491520,True,0 +BFS,50,50,30,True,2.3452,58,1750,118784,True,1 +BFS,50,50,30,True,2.6295,58,1751,118784,True,2 +BFS,50,50,30,False,2.2686,99,1751,290816,True,0 +BFS,50,50,30,False,2.1289,99,1751,286720,True,1 +BFS,50,50,30,False,2.2602,99,1751,294912,True,2 +BFS,50,50,50,True,1.0863,240,773,139264,True,0 +BFS,50,50,50,True,0.8318,174,592,118784,True,1 +BFS,50,50,50,True,0.7613,160,535,110592,True,2 +BFS,50,50,50,False,1.3722,487,1120,282624,True,0 +BFS,50,50,50,False,0.8369,227,677,200704,True,1 +BFS,50,50,50,False,1.3386,223,996,311296,True,2 diff --git a/Assets/Scripts/pathfinding_tests.csv.meta b/Assets/Scripts/pathfinding_tests.csv.meta new file mode 100644 index 0000000..ae7f4ef --- /dev/null +++ b/Assets/Scripts/pathfinding_tests.csv.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b5f5e061be1cbf946b769794cb0ba604 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: