Merge pull request #2 from Xevion/gui

Simple GUI & Controls
This commit is contained in:
2020-11-26 09:27:39 -06:00
committed by GitHub
15 changed files with 1380 additions and 183 deletions

View File

@@ -123,6 +123,410 @@ NavMeshSettings:
debug: debug:
m_Flags: 0 m_Flags: 0
m_NavMeshData: {fileID: 0} m_NavMeshData: {fileID: 0}
--- !u!1 &17751484
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 17751485}
- component: {fileID: 17751487}
- component: {fileID: 17751486}
m_Layer: 5
m_Name: Text (TMP)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &17751485
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 17751484}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 229754413}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &17751486
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 17751484}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Button
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4281479730
m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 24
m_fontSizeBase: 24
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 1
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!222 &17751487
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 17751484}
m_CullTransparentMesh: 0
--- !u!1 &229754412
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 229754413}
- component: {fileID: 229754416}
- component: {fileID: 229754415}
- component: {fileID: 229754414}
m_Layer: 5
m_Name: Button
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &229754413
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 229754412}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 17751485}
m_Father: {fileID: 1155758495}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 70.714, y: -12.815}
m_SizeDelta: {x: 141.42737, y: 25.629944}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &229754414
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 229754412}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 229754415}
m_OnClick:
m_PersistentCalls:
m_Calls: []
--- !u!114 &229754415
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 229754412}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!222 &229754416
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 229754412}
m_CullTransparentMesh: 0
--- !u!1 &344474894
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 344474895}
- component: {fileID: 344474897}
- component: {fileID: 344474896}
m_Layer: 5
m_Name: Handle
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &344474895
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 344474894}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.65, y: 0.65, z: 1}
m_Children: []
m_Father: {fileID: 831361552}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 35, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &344474896
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 344474894}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!222 &344474897
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 344474894}
m_CullTransparentMesh: 0
--- !u!1 &422608066
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 422608067}
- component: {fileID: 422608069}
- component: {fileID: 422608068}
m_Layer: 5
m_Name: Background
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &422608067
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 422608066}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 1127471362}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.25}
m_AnchorMax: {x: 1, y: 0.75}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &422608068
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 422608066}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.26415092, g: 0.26415092, b: 0.26415092, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!222 &422608069
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 422608066}
m_CullTransparentMesh: 0
--- !u!1 &519420028 --- !u!1 &519420028
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -206,6 +610,81 @@ Transform:
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 0 m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &542076403
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 542076404}
- component: {fileID: 542076406}
- component: {fileID: 542076405}
m_Layer: 5
m_Name: Fill
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &542076404
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 542076403}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 2071673332}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 10, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &542076405
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 542076403}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.16981131, g: 0.16981131, b: 0.16981131, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!222 &542076406
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 542076403}
m_CullTransparentMesh: 0
--- !u!850595691 &628914115 --- !u!850595691 &628914115
LightingSettings: LightingSettings:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -267,7 +746,7 @@ LightingSettings:
m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1 m_PVRFilteringAtrousPositionSigmaAO: 1
--- !u!1 &1373243071 --- !u!1 &831361551
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0} m_CorrespondingSourceObject: {fileID: 0}
@@ -275,47 +754,340 @@ GameObject:
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 1373243072} - component: {fileID: 831361552}
- component: {fileID: 1373243073} m_Layer: 5
m_Layer: 0 m_Name: Handle Slide Area
m_Name: Game Manager
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
m_NavMeshLayer: 0 m_NavMeshLayer: 0
m_StaticEditorFlags: 0 m_StaticEditorFlags: 0
m_IsActive: 1 m_IsActive: 1
--- !u!4 &1373243072 --- !u!224 &831361552
Transform: RectTransform:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0} m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1373243071} m_GameObject: {fileID: 831361551}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: [] m_Children:
m_Father: {fileID: 0} - {fileID: 344474895}
m_RootOrder: 3 m_Father: {fileID: 1127471362}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1373243073 m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: -20, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &938674168
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 938674171}
- component: {fileID: 938674170}
- component: {fileID: 938674169}
m_Layer: 0
m_Name: EventSystem
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &938674169
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0} m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0} m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1373243071} m_GameObject: {fileID: 938674168}
m_Enabled: 1 m_Enabled: 1
m_EditorHideFlags: 0 m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ca142250b9964eb1adf66ac7c5cc3e3e, type: 3} m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
mainCamera: {fileID: 519420031} m_HorizontalAxis: Horizontal
gridObject: {fileID: 2092623184} m_VerticalAxis: Vertical
m_SubmitButton: Submit
m_CancelButton: Cancel
m_InputActionsPerSecond: 10
m_RepeatDelay: 0.5
m_ForceModuleActive: 0
--- !u!114 &938674170
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 938674168}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_FirstSelected: {fileID: 0}
m_sendNavigationEvents: 1
m_DragThreshold: 10
--- !u!4 &938674171
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 938674168}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1127471361
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1127471362}
- component: {fileID: 1127471363}
m_Layer: 5
m_Name: ProgressSlider
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1127471362
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1127471361}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 422608067}
- {fileID: 2071673332}
- {fileID: 831361552}
m_Father: {fileID: 1155758495}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: -0}
m_AnchoredPosition: {x: 0.2999878, y: 11}
m_SizeDelta: {x: -22.869019, y: 35}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1127471363
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1127471361}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1502f97daf7446a48b977b5b7fa485ad, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 0.5660378, g: 0.5660378, b: 0.5660378, a: 1}
m_HighlightedColor: {r: 0.7735849, g: 0.7735849, b: 0.7735849, a: 1}
m_PressedColor: {r: 0.3301887, g: 0.3301887, b: 0.3301887, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 344474896}
m_FillRect: {fileID: 542076404}
m_HandleRect: {fileID: 344474895}
m_Direction: 0
m_MinValue: 0
m_MaxValue: 1
m_WholeNumbers: 0
m_Value: 0
m_OnValueChanged:
m_PersistentCalls:
m_Calls: []
--- !u!1 &1155758491
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1155758495}
- component: {fileID: 1155758494}
- component: {fileID: 1155758493}
- component: {fileID: 1155758492}
m_Layer: 5
m_Name: Canvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1155758492
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1155758491}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!114 &1155758493
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1155758491}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 0
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
--- !u!223 &1155758494
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1155758491}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_AdditionalShaderChannelsFlag: 25
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!224 &1155758495
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1155758491}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_Children:
- {fileID: 1127471362}
- {fileID: 1998222258}
- {fileID: 229754413}
m_Father: {fileID: 0}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!1 &1378668893
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1378668895}
- component: {fileID: 1378668894}
m_Layer: 0
m_Name: UIController
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1378668894
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1378668893}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fd9c76568dd84a92b068dd1e622bd101, type: 3}
m_Name:
m_EditorClassIdentifier:
progressSlider: {fileID: 1127471363}
gridController: {fileID: 2092623185} gridController: {fileID: 2092623185}
mainCamera: {fileID: 519420031}
debugText: {fileID: 1436240699} debugText: {fileID: 1436240699}
gridObject: {fileID: 2092623184}
clampIncrement: 1.25
speed: 2500 speed: 2500
clampIncrement: 2 --- !u!4 &1378668895
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1378668893}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 1.0482129, y: 0.49563652, z: -32.86509}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 6
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1436240698 --- !u!1 &1436240698
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -478,7 +1250,7 @@ RectTransform:
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: [] m_Children: []
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 4 m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5}
@@ -577,6 +1349,117 @@ Transform:
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 2 m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1998222257
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1998222258}
- component: {fileID: 1998222260}
- component: {fileID: 1998222259}
m_Layer: 5
m_Name: Panel
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &1998222258
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1998222257}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 1155758495}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -297.2, y: 123.3}
m_SizeDelta: {x: -683.3771, y: -359.0529}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1998222259
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1998222257}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!222 &1998222260
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1998222257}
m_CullTransparentMesh: 0
--- !u!1 &2071673331
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2071673332}
m_Layer: 5
m_Name: Fill Area
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2071673332
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2071673331}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 542076404}
m_Father: {fileID: 1127471362}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.25}
m_AnchorMax: {x: 1, y: 0.75}
m_AnchoredPosition: {x: -5, y: 0}
m_SizeDelta: {x: -20, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &2092623184 --- !u!1 &2092623184
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@@ -609,8 +1492,8 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
gridMaterial: {fileID: 2100000, guid: 780d53bea4df7b0418e9c8ed8afd6410, type: 2} gridMaterial: {fileID: 2100000, guid: 780d53bea4df7b0418e9c8ed8afd6410, type: 2}
width: 10 width: 60
height: 10 height: 34
--- !u!23 &2092623186 --- !u!23 &2092623186
MeshRenderer: MeshRenderer:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@@ -3,18 +3,18 @@ using UnityEngine;
namespace Algorithms { namespace Algorithms {
public class AStar : IPathfinding { public class AStar : IPathfinding {
private NodeGrid _nodeGrid; public NodeGrid NodeGrid { get; }
private Stack<Node> _path; private Stack<Node> _path;
private List<Node> _openList; private List<Node> _openList;
private List<Node> _closedList; private List<Node> _closedList;
public ChangeController ChangeController { get; private set; } public ChangeController ChangeController { get; }
public Vector2Int Start { get; private set; } public Vector2Int Start { get; private set; }
public Vector2Int End { get; private set; } public Vector2Int End { get; private set; }
public AStar(NodeGrid nodeGrid) { public AStar(NodeGrid nodeGrid) {
this._nodeGrid = nodeGrid; NodeGrid = nodeGrid;
ChangeController = new ChangeController(nodeGrid.RenderNodeTypes()); ChangeController = new ChangeController(nodeGrid.RenderNodeTypes());
} }
@@ -25,8 +25,8 @@ namespace Algorithms {
ChangeController.AddChange(new Change(start.x, start.y, GridNodeType.Start, GridNodeType.Empty)); ChangeController.AddChange(new Change(start.x, start.y, GridNodeType.Start, GridNodeType.Empty));
ChangeController.AddChange(new Change(end.x, end.y, GridNodeType.End, GridNodeType.Empty)); ChangeController.AddChange(new Change(end.x, end.y, GridNodeType.End, GridNodeType.Empty));
Node startNode = _nodeGrid.Grid[start.x, start.y]; Node startNode = NodeGrid.Grid[start.x, start.y];
Node endNode = _nodeGrid.Grid[end.x, end.y]; Node endNode = NodeGrid.Grid[end.x, end.y];
_path = new Stack<Node>(); _path = new Stack<Node>();
_openList = new List<Node>(); _openList = new List<Node>();
@@ -39,19 +39,23 @@ namespace Algorithms {
_openList.Add(startNode); _openList.Add(startNode);
while (_openList.Count != 0) { while (_openList.Count != 0) {
// take the first node out (lowest F score)
current = _openList[0]; current = _openList[0];
_openList.RemoveAt(0); _openList.RemoveAt(0);
// add it to closed list & mark
current.State = NodeState.Closed; current.State = NodeState.Closed;
ChangeController.AddChange(new Change( ChangeController.AddChange(new Change(
current.Position.x, current.Position.y, current.Position.x, current.Position.y,
GridNodeType.Expanded, GridNodeType.Seen)); GridNodeType.Expanded, GridNodeType.Seen));
_closedList.Add(current); _closedList.Add(current);
// exit if this is the end node
if (current.Position == endNode.Position) if (current.Position == endNode.Position)
break; break;
Node[] adjacentNodes = this._nodeGrid.GetAdjacentNodesArray(current); // look at all adjacent nodes and add them to the open list if possible
Node[] adjacentNodes = this.NodeGrid.GetAdjacentNodesArray(current);
for (int i = 0; i < adjacentNodes.Length; i++) { for (int i = 0; i < adjacentNodes.Length; i++) {
Node node = adjacentNodes[i]; Node node = adjacentNodes[i];
if (node != null && node.State == NodeState.None && node.Walkable) { if (node != null && node.State == NodeState.None && node.Walkable) {
@@ -60,6 +64,7 @@ namespace Algorithms {
node.DistanceToTarget = NodeGrid.Manhattan(node, endNode); node.DistanceToTarget = NodeGrid.Manhattan(node, endNode);
node.Cost = node.Weight + node.Parent.Cost; node.Cost = node.Weight + node.Parent.Cost;
// mark as open
node.State = NodeState.Open; node.State = NodeState.Open;
ChangeController.AddChange(new Change(node.Position.x, node.Position.y, GridNodeType.Seen, ChangeController.AddChange(new Change(node.Position.x, node.Position.y, GridNodeType.Seen,
GridNodeType.Empty)); GridNodeType.Empty));
@@ -77,19 +82,46 @@ namespace Algorithms {
return null; return null;
} }
// Fix start position being overriden
ChangeController.RemovePositions(start, 1);
// if all good, return path // if all good, return path
Node temp = _closedList[_closedList.IndexOf(current)]; Node temp = _closedList[_closedList.IndexOf(current)];
if (temp == null) return null; if (temp == null) return null;
do { do {
_path.Push(temp); _path.Push(temp);
ChangeController.AddChange(new Change(temp.Position.x, temp.Position.y, GridNodeType.Path, GridNodeType.Expanded)); ChangeController.AddChange(new Change(temp.Position.x, temp.Position.y, GridNodeType.Path,
GridNodeType.Expanded));
temp = temp.Parent; temp = temp.Parent;
} while (temp != null && !temp.Equals(startNode)); } while (temp != null && !temp.Equals(startNode));
// Fix start and end position being overriden
// TODO: Look into using a proper fix for this instead of a 'patch'.
ChangeController.RemovePositions(start, 1);
ChangeController.RemovePositions(end, 3);
return _path; return _path;
} }
/// <summary>
/// Attempts to clean the NodeGrid of all edits made to heuristic values and such, fast.
/// This is done by clearing the open and closed list, and for each node, resetting them (clearing heuristic
/// values and setting state back to it's default).
/// </summary>
public void Cleanup() {
while (_openList.Count > 0) {
Node node = _openList[0];
_openList.RemoveAt(0);
node.Reset();
}
while (_closedList.Count > 0) {
Node node = _closedList[0];
_closedList.RemoveAt(0);
node.Reset();
}
}
} }
} }

View File

@@ -37,6 +37,16 @@ namespace Algorithms {
Walkable = walkable; Walkable = walkable;
} }
/// <summary>
/// Resets this Node back to it's assumed default values.
/// </summary>
public void Reset() {
Parent = null;
DistanceToTarget = null;
Cost = 1;
State = NodeState.None;
}
public override bool Equals(object obj) { public override bool Equals(object obj) {
return obj is Node node && Position.Equals(node.Position); return obj is Node node && Position.Equals(node.Position);
} }

View File

@@ -37,8 +37,15 @@ namespace Algorithms {
Width = this.Grid.GetLength(1); Width = this.Grid.GetLength(1);
} }
/// <summary>
/// Returns adjacent Node objects in each of the cardinal directions.
/// Only valid nodes will be included. Invalid positions will return 0 nodes.
/// </summary>
/// <param name="node">The node from which adjacents will be found.</param>
/// <returns>A length 4 or less list containing nodes.</returns>
/// <seealso cref="GetAdjacentNodesArray"/>
public List<Node> GetAdjacentNodesList(Node node) { public List<Node> GetAdjacentNodesList(Node node) {
List<Node> temp = new List<Node>(); List<Node> temp = new List<Node>(4);
int col = node.Position.x; int col = node.Position.x;
int row = node.Position.y; int row = node.Position.y;
@@ -51,11 +58,29 @@ namespace Algorithms {
return temp; return temp;
} }
/// <summary>
/// Returns True if a Vector2Int position is within the grid's boundaries.
/// </summary>
/// <param name="position">A Vector2Int coordinate position.</param>
/// <returns>True if valid and real (a node with the same position exists).</returns>
public bool IsValid(Vector2Int position) {
return position.x >= 0 && position.y >= 0 && position.x < Width && position.y < Height;
}
/// <summary>
/// Returns a 1D Array describing the valid nearby nodes.
/// Invalid nodes will be represented by null.
/// Currently only returns nodes in cardinal directions (no diagonals).
/// Only valid positions within the grid are returned.
/// </summary>
/// <param name="node">A valid position on the grid.</param>
/// <returns>A 4 length array containing valid nodes in each of the cardinal directions.</returns>
/// <seealso cref="GetAdjacentNodesList"/>
public Node[] GetAdjacentNodesArray(Node node) { public Node[] GetAdjacentNodesArray(Node node) {
int col = node.Position.x; int col = node.Position.x;
int row = node.Position.y; int row = node.Position.y;
return new [] { return new[] {
row + 1 < Height ? Grid[col, row + 1] : null, row + 1 < Height ? Grid[col, row + 1] : null,
row - 1 >= 0 ? Grid[col, row - 1] : null, row - 1 >= 0 ? Grid[col, row - 1] : null,
col - 1 >= 0 ? Grid[col - 1, row] : null, col - 1 >= 0 ? Grid[col - 1, row] : null,
@@ -139,7 +164,7 @@ namespace Algorithms {
} }
} }
public GridNodeType[,] RenderNodeTypes() { public GridNodeType[,] RenderNodeTypes(Vector2Int? start = null, Vector2Int? end = null) {
GridNodeType[,] nodeTypeGrid = new GridNodeType[Grid.GetLength(0), Grid.GetLength(1)]; GridNodeType[,] nodeTypeGrid = new GridNodeType[Grid.GetLength(0), Grid.GetLength(1)];
for (int x = 0; x < Grid.GetLength(0); x++) { for (int x = 0; x < Grid.GetLength(0); x++) {
@@ -148,6 +173,13 @@ namespace Algorithms {
} }
} }
// Start / End node addition
if (start.HasValue)
nodeTypeGrid[start.Value.x, start.Value.y] = GridNodeType.Start;
if (end.HasValue)
nodeTypeGrid[end.Value.x, end.Value.y] = GridNodeType.End;
return nodeTypeGrid; return nodeTypeGrid;
} }
} }

View File

@@ -1,4 +1,8 @@
public readonly struct Change { /// <summary>
/// A Change struct represents a Change in the grid's rendering.
/// This struct remembers the original and new GridNodeType, the time it was recorded and of course the relevant position.
/// </summary>
public readonly struct Change {
public readonly int X; public readonly int X;
public readonly int Y; public readonly int Y;
public readonly GridNodeType New; public readonly GridNodeType New;

View File

@@ -9,16 +9,16 @@ public class ChangeController {
private readonly GridNodeType[,] _initial; private readonly GridNodeType[,] _initial;
public GridNodeType[,] Current { get; private set; } public GridNodeType[,] Current { get; private set; }
public bool[,] DirtyFlags { get; private set; } public bool[,] DirtyFlags { get; private set; }
public int Index { get; private set; } public int CurrentChangeIndex { get; private set; }
private readonly List<Change> _changes; private readonly List<Change> _changes;
public int Count => _changes.Count; public int Count => _changes.Count;
public Change CurrentChange => _changes[Index]; public Change CurrentChange => _changes[CurrentChangeIndex];
public double CurrentRuntime => _changes[Index].Time - _changes[0].Time; public double CurrentRuntime => _changes[CurrentChangeIndex].Time - _changes[0].Time;
public ChangeController(GridNodeType[,] initial) { public ChangeController(GridNodeType[,] initial) {
_initial = initial; _initial = initial;
Current = initial; Current = initial;
Index = -1; CurrentChangeIndex = -1;
_changes = new List<Change>(); _changes = new List<Change>();
DirtyFlags = new bool[initial.GetLength(0), initial.GetLength(1)]; DirtyFlags = new bool[initial.GetLength(0), initial.GetLength(1)];
SetDirty(); SetDirty();
@@ -64,7 +64,7 @@ public class ChangeController {
/// <summary> /// <summary>
/// Move the ChangeController's current index forward by index. /// Move the ChangeController's current index forward by a certain number of times.
/// Positive values only, wil only result in forward movement (if any). /// Positive values only, wil only result in forward movement (if any).
/// </summary> /// </summary>
/// <param name="n">The number of times to move forward.</param> /// <param name="n">The number of times to move forward.</param>
@@ -73,14 +73,14 @@ public class ChangeController {
throw new ArgumentOutOfRangeException(nameof(n)); throw new ArgumentOutOfRangeException(nameof(n));
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
Change cur = _changes[++Index]; Change cur = _changes[++CurrentChangeIndex];
Current[cur.X, cur.Y] = cur.New; Current[cur.X, cur.Y] = cur.New;
SetDirty(new Vector2Int(cur.X, cur.Y)); SetDirty(new Vector2Int(cur.X, cur.Y));
} }
} }
/// <summary> /// <summary>
/// Move the ChangeController's current index backward by index. /// Move the ChangeController's current index backward by a certain number of times.
/// Positive values only, will only result in backward movement (if any). /// Positive values only, will only result in backward movement (if any).
/// </summary> /// </summary>
/// <param name="n">The number of times to move backward.</param> /// <param name="n">The number of times to move backward.</param>
@@ -88,11 +88,11 @@ public class ChangeController {
if (n < 0) if (n < 0)
throw new ArgumentOutOfRangeException(nameof(n)); throw new ArgumentOutOfRangeException(nameof(n));
if (n > 5 && Index - n == 0) if (n > 5 && CurrentChangeIndex - n == 0)
Reset(); // resetting by copying values instead of mutating might be easier. Reset(); // resetting by copying values instead of mutating might be easier.
else { else {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
Change cur = _changes[--Index]; Change cur = _changes[CurrentChangeIndex--]; // post decrement as we apply the current Change's old, not the previous
Current[cur.X, cur.Y] = cur.Old; Current[cur.X, cur.Y] = cur.Old;
SetDirty(new Vector2Int(cur.X, cur.Y)); SetDirty(new Vector2Int(cur.X, cur.Y));
} }
@@ -124,7 +124,7 @@ public class ChangeController {
$"Cannot move to change index {index}. Only indexes from 0 to {_changes.Count - 1} are valid."); $"Cannot move to change index {index}. Only indexes from 0 to {_changes.Count - 1} are valid.");
// diff & move to // diff & move to
int diff = index - Index; int diff = index - CurrentChangeIndex;
if (diff != 0) if (diff != 0)
// prefer resetting if index is 0 and it needs to move at least some. // prefer resetting if index is 0 and it needs to move at least some.
if (index == 0 && diff > 5) if (index == 0 && diff > 5)

View File

@@ -0,0 +1,23 @@
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// A simple custom slider implementation that adds a single boolean field; IsPressed.
/// </summary>
public class CustomSlider: Slider {
/// <summary>
/// Whether or not the Slider is currently being pressed on by the Left Click mouse button.
/// </summary>
public new bool IsPressed { get; private set; }
public override void OnPointerDown(PointerEventData eventData)
{
base.OnPointerDown(eventData);
IsPressed = true;
}
public override void OnPointerUp(PointerEventData eventData) {
base.OnPointerUp(eventData);
IsPressed = false;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1502f97daf7446a48b977b5b7fa485ad
timeCreated: 1606327381

View File

@@ -25,9 +25,11 @@ public class GridController : MonoBehaviour {
private static readonly int Values = Shader.PropertyToID("_values"); private static readonly int Values = Shader.PropertyToID("_values");
private static readonly int GridWidth = Shader.PropertyToID("_GridWidth"); private static readonly int GridWidth = Shader.PropertyToID("_GridWidth");
private static readonly int GridHeight = Shader.PropertyToID("_GridHeight"); private static readonly int GridHeight = Shader.PropertyToID("_GridHeight");
public Vector2Int Size => new Vector2Int(width, height);
private void Start() { private void Start() {
_values = new int[width * height]; _values = new int[width * height];
// TODO: Decide at some point how to improve how the ComputerBuffer's size is allocated.
_buffer = new ComputeBuffer((int) Mathf.Pow(2048, 2), 4); _buffer = new ComputeBuffer((int) Mathf.Pow(2048, 2), 4);
// Update all Shader properties // Update all Shader properties
@@ -130,4 +132,32 @@ public class GridController : MonoBehaviour {
public int GetIndex(int x, int y) { public int GetIndex(int x, int y) {
return width * y + x; return width * y + x;
} }
/// <summary>
/// Translate a world position to the approximate position on the Grid.
/// May not return valid grid coordinates (outside the range).
/// </summary>
/// <param name="worldPosition">a Vector3 World Position; Z coordinates are inconsequential.</param>
/// <returns>A Vector2Int representing a grid position from the bottom left.</returns>
public Vector2Int GetGridPosition(Vector3 worldPosition) {
Vector3 localScale = transform.localScale;
Vector2 gridPosition = (worldPosition + (localScale / 2f)) / new Vector2(localScale.x, localScale.y);
return Size - new Vector2Int(
(int) (gridPosition.x * width),
(int) (gridPosition.y * height)) - Vector2Int.one;
}
/// <summary>
/// Translates a position on the grid into a real World Position.
/// </summary>
/// <param name="gridPosition">The XY position on the grid</param>
/// <returns>A Vector3 centered on the grid square in the World</returns>
public Vector3 GetWorldPosition(Vector2Int gridPosition) {
Transform ttransform = transform;
Vector3 localScale = ttransform.localScale;
Vector2 topRight = ttransform.position + (localScale / 2f);
var singleSquare = new Vector2(localScale.x / width, localScale.y / height);
Vector2 worldPosition = topRight - (singleSquare * (gridPosition + Vector2Int.one)) + (singleSquare / 2f);
return worldPosition;
}
} }

View File

@@ -13,8 +13,9 @@ public interface IPathfinding {
/// <param name="end">The position trying to be found via pathfinding</param> /// <param name="end">The position trying to be found via pathfinding</param>
/// <returns>A List of NodeGridGrid objects representing the timeline of Pathfinding</returns> /// <returns>A List of NodeGridGrid objects representing the timeline of Pathfinding</returns>
Stack<Node> FindPath(Vector2Int start, Vector2Int end); Stack<Node> FindPath(Vector2Int start, Vector2Int end);
NodeGrid NodeGrid { get; }
Vector2Int Start { get; } Vector2Int Start { get; }
Vector2Int End { get; } Vector2Int End { get; }
ChangeController ChangeController { get; } ChangeController ChangeController { get; }
void Cleanup();
} }

View File

@@ -1,126 +0,0 @@
using System;
using System.Collections.Generic;
using Algorithms;
using LevelGeneration;
using TMPro;
using UnityEngine;
using UnityEngine.Analytics;
/// <summary>
/// The primary controller of the entire application, managing state, events and sending commands
/// </summary>
public class Manager : MonoBehaviour {
private IPathfinding _algorithm;
private ChangeController _state;
private int _curIndex;
private Stack<Node> _path;
private float _runtime;
public Camera mainCamera;
public GameObject gridObject;
public GridController gridController;
public TextMeshPro debugText;
public float speed;
public float clampIncrement;
private int CurrentIndex {
get => (int) _runtime;
set => _runtime = value;
}
public void Start() {
GeneratePath();
Resize();
}
// public void OnDrawGizmos() {
// float size = (float) (10.0 / gridController.size);
// Gizmos.DrawWireCube(transform.position, new Vector3(size, size, size));
// }
/// <summary>
/// Returns the current time multiplier, based on the latest change in the path.
/// </summary>
/// <returns>A positive non-zero float representing how fast the current frame should be processed.</returns>
private float CurrentMultiplier() {
if (_state.Index == -1)
return 1;
switch (_state.CurrentChange.New) {
case GridNodeType.Path:
return 1/5f;
case GridNodeType.Empty:
break;
case GridNodeType.Wall:
break;
case GridNodeType.Start:
break;
case GridNodeType.End:
break;
case GridNodeType.Seen:
break;
case GridNodeType.Expanded:
break;
default:
throw new ArgumentOutOfRangeException();
}
return 1;
}
public void Update() {
var increment = Time.deltaTime * speed * CurrentMultiplier();
if (clampIncrement > 0)
increment = Mathf.Clamp(increment, 0, _state.Count * Time.deltaTime / clampIncrement);
_runtime += increment;
if (CurrentIndex < _state.Count)
LoadNextState();
else {
GeneratePath();
CurrentIndex = 0;
}
}
private void GeneratePath() {
var nodeGrid = new NodeGrid(gridController.width, gridController.height);
Vector2Int start = nodeGrid.RandomPosition();
// Vector2Int start = new Vector2Int(30, 30);
Vector2Int end = nodeGrid.RandomPosition();
nodeGrid.ApplyGenerator(new RandomPlacement(0.3f, true, true));
nodeGrid.GetNode(start).Walkable = true;
nodeGrid.GetNode(end).Walkable = true;
_algorithm = new AStar(nodeGrid);
_path = _algorithm.FindPath(start, end);
_state = _algorithm.ChangeController;
}
private void LoadNextState() {
_state.MoveTo(CurrentIndex);
gridController.LoadDirtyGridState(_state.Current, _state.DirtyFlags);
string pathCount = _path != null ? $"{_path.Count}" : "N/A";
debugText.text = $"{_state.CurrentRuntime * 1000.0:F1}ms\n" +
$"{this.CurrentIndex:000} / {_state.Count:000}\n" +
$"Path: {pathCount} tiles";
}
/// <summary>
/// Scales the GridController GameObject to fit within the Camera
/// </summary>
private void Resize() {
float ratioImage = (float) gridController.width / gridController.height;
float ratioScreen = mainCamera.aspect;
var orthographicSize = mainCamera.orthographicSize;
var image = new Vector2(gridController.width, gridController.height);
var screen = new Vector2(2 * orthographicSize * mainCamera.aspect, orthographicSize * 2);
gridObject.transform.localScale = ratioScreen > ratioImage
? new Vector3(image.x * screen.y / image.y, screen.y, 0.001f)
: new Vector3(screen.x, image.y * screen.x / image.x, 0.001f);
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ca142250b9964eb1adf66ac7c5cc3e3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 300
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,313 @@
using System;
using System.Collections.Generic;
using Algorithms;
using TMPro;
using UnityEditor;
using UnityEngine;
/// <summary>
/// Denotes what the user clicked and is now modifying.
/// Add/Remove is for wall addition/removal, Start and End is for dragging the Start and End nodes around.
/// </summary>
public enum ClickType {
Add,
Remove,
Start,
End,
ProgressBar
}
/// <summary>
/// Denotes the precise state of the animation as the user clicks, stops or starts it.
/// Stopped is a full stop, allowing editing and manipulation. The path will not be shown or generated.
/// Started is fully engaged when the user is not currently editing or manipulating.
/// Reloading occurs when the user manipulates the grid while the program is
/// </summary>
public enum AnimationState {
Stopped,
Paused,
Started,
Reloading
}
/// <summary>
/// A expansive class that controls all UI interactions including grid modifications, slider movement, tool usage etc.
/// All UI elements are referenced and controlled here.
/// </summary>
public class UIController : MonoBehaviour {
// UI & important App references
public CustomSlider progressSlider;
public GridController gridController;
public Camera mainCamera;
public TextMeshPro debugText;
public GameObject gridObject;
// Animation State, Click Management
private Vector2Int _lastClickLocation;
private ClickType _modify;
private AnimationState _animationState;
private AnimationState _previousAnimationState;
private bool EditShouldReload =>
_animationState == AnimationState.Started;
// Grid State & Pathfinding
private NodeGrid _grid;
private Vector2Int _start;
private Vector2Int _end;
private IPathfinding _algorithm;
private Stack<Node> _path;
private ChangeController _state;
// Animation speed & indexing
public float clampIncrement;
private float _runtime;
public int CurrentIndex => (int) Math.Ceiling(_runtime);
public float speed;
private void Start() {
_grid = new NodeGrid(gridController.width, gridController.height);
_animationState = AnimationState.Stopped;
_previousAnimationState = _animationState;
_start = _grid.RandomPosition();
_end = _grid.RandomPosition();
_runtime = 0;
Resize();
progressSlider.onValueChanged.AddListener((value) => MoveToSlider(value));
}
private void Update() {
if (Input.GetMouseButton(0)) {
Vector3 worldMouse = mainCamera.ScreenToWorldPoint(Input.mousePosition);
Vector2Int position = gridController.GetGridPosition(worldMouse);
// Initial click, remember what they clicked
if (Input.GetMouseButtonDown(0)) {
if (progressSlider.IsPressed)
_modify = ClickType.ProgressBar;
else if (position == _start)
_modify = ClickType.Start;
else if (position == _end)
_modify = ClickType.End;
else {
Node node = _grid.GetNode(position);
_modify = node.Walkable ? ClickType.Add : ClickType.Remove;
node.Walkable = !node.Walkable;
if (_animationState == AnimationState.Paused)
_animationState = AnimationState.Stopped;
else if (_animationState == AnimationState.Started)
_animationState = AnimationState.Reloading;
}
_lastClickLocation = position;
}
else {
// If still holding down the button & the latest movement is over a new grid
if (_lastClickLocation != position) {
_lastClickLocation = position;
if (_grid.IsValid(position)) {
Node node = _grid.GetNode(position);
switch (_modify) {
// regular clicking toggles walls
// Note: Wall toggling instantly reloads, but only real start/end node movement reloads.
case ClickType.Add:
node.Walkable = false;
if (EditShouldReload)
_animationState = AnimationState.Reloading;
break;
case ClickType.Remove:
node.Walkable = true;
if (EditShouldReload)
_animationState = AnimationState.Reloading;
break;
case ClickType.Start:
if (node.Walkable) {
_start = position;
if (EditShouldReload)
_animationState = AnimationState.Reloading;
}
break;
case ClickType.End:
if (node.Walkable) {
_end = position;
if (EditShouldReload)
_animationState = AnimationState.Reloading;
}
break;
case ClickType.ProgressBar:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
}
// Handle user start/stopping
if (Input.GetKeyDown(KeyCode.Space)) {
switch (_animationState) {
case AnimationState.Stopped:
_animationState = AnimationState.Reloading;
break;
case AnimationState.Paused:
_animationState = AnimationState.Started;
break;
case AnimationState.Started:
// Restart if already on final frame, else simply pause
if (CurrentIndex >= _state.Count)
_runtime = 0;
else
_animationState = AnimationState.Paused;
break;
case AnimationState.Reloading:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
switch (_animationState) {
case AnimationState.Reloading:
// Reloading seizes while the mouse button is depressed
if (!Input.GetMouseButton(0)) {
GeneratePath();
LoadNextState();
_animationState = AnimationState.Started;
}
else
gridController.LoadGridState(_grid.RenderNodeTypes(_start, _end));
progressSlider.SetValueWithoutNotify(_runtime / _state.Count);
break;
case AnimationState.Started:
// Calculate how much to move forward
if (!progressSlider.IsPressed) {
var increment = Time.deltaTime * speed * CurrentMultiplier();
if (clampIncrement > 0)
increment = Mathf.Clamp(increment, 0, _state.Count * Time.deltaTime / clampIncrement);
_runtime += increment;
}
progressSlider.SetValueWithoutNotify(_runtime / _state.Count);
if (_runtime < _state.Count)
LoadNextState();
break;
case AnimationState.Stopped:
// Render editable grid when fully stopped
gridController.LoadGridState(_grid.RenderNodeTypes(_start, _end));
break;
case AnimationState.Paused:
if (_runtime < _state.Count)
LoadNextState();
break;
default:
throw new ArgumentOutOfRangeException();
}
if (_animationState != _previousAnimationState) {
Debug.Log($"Animation State {_previousAnimationState} -> {_animationState}");
_previousAnimationState = _animationState;
}
}
private void GeneratePath() {
_algorithm?.Cleanup(); // cleanup algorithm's edits to node grid
_runtime = 0f;
_algorithm = new AStar(_grid);
_path = _algorithm.FindPath(_start, _end);
_state = _algorithm.ChangeController;
}
private void LoadNextState() {
// Move to the new calculated index
_state.MoveTo(Mathf.Clamp(CurrentIndex, 1, _state.Count - 1)); // use Math.max to ensure both start/end nodes are always rendered
gridController.LoadDirtyGridState(_state.Current, _state.DirtyFlags);
string pathCount = _path != null ? $"{_path.Count}" : "N/A";
debugText.text = $"{_state.CurrentRuntime * 1000.0:F1}ms\n" +
$"{CurrentIndex:000} / {_state.Count:000}\n" +
$"Path: {pathCount} tiles";
}
/// <summary>
/// Returns the current time multiplier, based on the latest change in the path.
/// </summary>
/// <returns>A positive non-zero float representing how fast the current frame should be processed.</returns>
private float CurrentMultiplier() {
if (_state.CurrentChangeIndex == -1)
return 1;
switch (_state.CurrentChange.New) {
case GridNodeType.Path:
return 1 / 5f;
case GridNodeType.Empty:
break;
case GridNodeType.Wall:
break;
case GridNodeType.Start:
break;
case GridNodeType.End:
break;
case GridNodeType.Seen:
break;
case GridNodeType.Expanded:
break;
default:
throw new ArgumentOutOfRangeException();
}
return 1;
}
public void OnDrawGizmos() {
if (!Application.isPlaying) return;
Vector3 mouse = mainCamera.ScreenToWorldPoint(Input.mousePosition);
Vector3 localScale = gridObject.transform.localScale;
Vector2Int gridPosition = gridController.GetGridPosition(mouse);
var style = new GUIStyle();
style.normal.textColor = Color.blue;
Gizmos.color = Color.blue;
Gizmos.DrawWireCube(gridController.GetWorldPosition(gridPosition), localScale / (Vector2) gridController.Size);
Handles.Label(mouse, String.Format("{0}{1}",
gridPosition,
_algorithm != null && _algorithm.NodeGrid.IsValid(gridPosition)
? $"\n{_state.Current[gridPosition.x, gridPosition.y]}"
: ""
), style);
}
/// <summary>
/// Update the animation progress to the slider's (new) position.
/// </summary>
/// <param name="new">The new position on the slider.</param>
private void MoveToSlider(float @new) {
if (_state != null)
_runtime = @new * _state.Count;
}
/// <summary>
/// Scales the GridController GameObject to fit within the Camera
/// </summary>
public void Resize() {
float ratioImage = (float) gridController.width / gridController.height;
float ratioScreen = mainCamera.aspect;
var orthographicSize = mainCamera.orthographicSize;
var image = new Vector2(gridController.width, gridController.height);
var screen = new Vector2(2 * orthographicSize * mainCamera.aspect, orthographicSize * 2);
gridObject.transform.localScale = ratioScreen > ratioImage
? new Vector3(image.x * screen.y / image.y, screen.y, 0.001f)
: new Vector3(screen.x, image.y * screen.x / image.x, 0.001f);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fd9c76568dd84a92b068dd1e622bd101
timeCreated: 1606316179

View File

@@ -41,7 +41,7 @@ Shader "PDT Shaders/TestGrid"
static const float4 _gridColors[7] = { static const float4 _gridColors[7] = {
float4(255 / 255.0, 255 / 255.0, 255 / 255.0, 1.0), // Empty float4(255 / 255.0, 255 / 255.0, 255 / 255.0, 1.0), // Empty
float4(0 / 255.0, 0 / 255.0, 0 / 255.0, 1.0), // Wall float4(5 / 255.0, 5 / 255.0, 5 / 255.0, 1.0), // Wall
float4(0 / 255.0, 255 / 255.0, 0 / 255.0, 1.0), // Start float4(0 / 255.0, 255 / 255.0, 0 / 255.0, 1.0), // Start
float4(255 / 255.0, 0 / 255.0, 0 / 255.0, 1.0), // End float4(255 / 255.0, 0 / 255.0, 0 / 255.0, 1.0), // End
float4(252 / 255.0, 236 / 255.0, 3 / 255.0, 1.0), // Seen float4(252 / 255.0, 236 / 255.0, 3 / 255.0, 1.0), // Seen