diff --git a/Final Platformer/Assets/Animations.meta b/Final Platformer/Assets/Animations.meta new file mode 100644 index 0000000..400024c --- /dev/null +++ b/Final Platformer/Assets/Animations.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3955126241a482c4e960a6bf66d06051 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/AnnoyingRobot.anim b/Final Platformer/Assets/Animations/AnnoyingRobot.anim new file mode 100644 index 0000000..7237523 --- /dev/null +++ b/Final Platformer/Assets/Animations/AnnoyingRobot.anim @@ -0,0 +1,74 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: AnnoyingRobot + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: bc35810c3f18915489d3aa32a7c6da63, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: bc35810c3f18915489d3aa32a7c6da63, type: 3} + - time: 0.16666667 + value: {fileID: 21300004, guid: bc35810c3f18915489d3aa32a7c6da63, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: bc35810c3f18915489d3aa32a7c6da63, type: 3} + - {fileID: 21300002, guid: bc35810c3f18915489d3aa32a7c6da63, type: 3} + - {fileID: 21300004, guid: bc35810c3f18915489d3aa32a7c6da63, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.25 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/AnnoyingRobot.anim.meta b/Final Platformer/Assets/Animations/AnnoyingRobot.anim.meta new file mode 100644 index 0000000..c71edad --- /dev/null +++ b/Final Platformer/Assets/Animations/AnnoyingRobot.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 38a38b163c2f2dd4881969727b554f21 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Bomb.anim b/Final Platformer/Assets/Animations/Bomb.anim new file mode 100644 index 0000000..ad3aa90 --- /dev/null +++ b/Final Platformer/Assets/Animations/Bomb.anim @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Bomb + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: e016143618132394ca34ea04495fd99c, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: e016143618132394ca34ea04495fd99c, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: e016143618132394ca34ea04495fd99c, type: 3} + - {fileID: 21300002, guid: e016143618132394ca34ea04495fd99c, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.16666667 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Bomb.anim.meta b/Final Platformer/Assets/Animations/Bomb.anim.meta new file mode 100644 index 0000000..14d61a4 --- /dev/null +++ b/Final Platformer/Assets/Animations/Bomb.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4c8cc5455f88e994386e4e4153ab07d5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Bomb_0.controller b/Final Platformer/Assets/Animations/Bomb_0.controller new file mode 100644 index 0000000..d76705b --- /dev/null +++ b/Final Platformer/Assets/Animations/Bomb_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Bomb_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107818767448915618} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102954525110185372 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Bomb + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 4c8cc5455f88e994386e4e4153ab07d5, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107818767448915618 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102954525110185372} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102954525110185372} diff --git a/Final Platformer/Assets/Animations/Bomb_0.controller.meta b/Final Platformer/Assets/Animations/Bomb_0.controller.meta new file mode 100644 index 0000000..5bb0095 --- /dev/null +++ b/Final Platformer/Assets/Animations/Bomb_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2e4592951b0517647a77ff1a1a670103 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Crouch.anim b/Final Platformer/Assets/Animations/Crouch.anim new file mode 100644 index 0000000..33f2aaa --- /dev/null +++ b/Final Platformer/Assets/Animations/Crouch.anim @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Crouch + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: bd596db73941a6c41a23a4abaa789313, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: bd596db73941a6c41a23a4abaa789313, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: bd596db73941a6c41a23a4abaa789313, type: 3} + - {fileID: 21300002, guid: bd596db73941a6c41a23a4abaa789313, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.16666667 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Crouch.anim.meta b/Final Platformer/Assets/Animations/Crouch.anim.meta new file mode 100644 index 0000000..3d33d50 --- /dev/null +++ b/Final Platformer/Assets/Animations/Crouch.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6c81627af1ce7e443a6e6023bc176710 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/EnemySoldierWalk.anim b/Final Platformer/Assets/Animations/EnemySoldierWalk.anim new file mode 100644 index 0000000..b25ed4a --- /dev/null +++ b/Final Platformer/Assets/Animations/EnemySoldierWalk.anim @@ -0,0 +1,89 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: EnemySoldierWalk + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: c97868e8b16bca941ac831864b690374, type: 3} + - time: 0.11111111 + value: {fileID: 21300002, guid: c97868e8b16bca941ac831864b690374, type: 3} + - time: 0.22222222 + value: {fileID: 21300004, guid: c97868e8b16bca941ac831864b690374, type: 3} + - time: 0.33333334 + value: {fileID: 21300006, guid: c97868e8b16bca941ac831864b690374, type: 3} + - time: 0.44444445 + value: {fileID: 21300008, guid: c97868e8b16bca941ac831864b690374, type: 3} + - time: 0.5555556 + value: {fileID: 21300010, guid: c97868e8b16bca941ac831864b690374, type: 3} + - time: 0.6666667 + value: {fileID: 21300012, guid: c97868e8b16bca941ac831864b690374, type: 3} + - time: 0.7777778 + value: {fileID: 21300014, guid: c97868e8b16bca941ac831864b690374, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 9 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: c97868e8b16bca941ac831864b690374, type: 3} + - {fileID: 21300002, guid: c97868e8b16bca941ac831864b690374, type: 3} + - {fileID: 21300004, guid: c97868e8b16bca941ac831864b690374, type: 3} + - {fileID: 21300006, guid: c97868e8b16bca941ac831864b690374, type: 3} + - {fileID: 21300008, guid: c97868e8b16bca941ac831864b690374, type: 3} + - {fileID: 21300010, guid: c97868e8b16bca941ac831864b690374, type: 3} + - {fileID: 21300012, guid: c97868e8b16bca941ac831864b690374, type: 3} + - {fileID: 21300014, guid: c97868e8b16bca941ac831864b690374, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.8888889 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/EnemySoldierWalk.anim.meta b/Final Platformer/Assets/Animations/EnemySoldierWalk.anim.meta new file mode 100644 index 0000000..884cd4e --- /dev/null +++ b/Final Platformer/Assets/Animations/EnemySoldierWalk.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e58da93ad1d07d54887510d7cb6cb73b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/EnemySoldier_0.controller b/Final Platformer/Assets/Animations/EnemySoldier_0.controller new file mode 100644 index 0000000..af2b32d --- /dev/null +++ b/Final Platformer/Assets/Animations/EnemySoldier_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: EnemySoldier_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107305707288143730} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102545940477301718 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: EnemySoldierWalk + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: e58da93ad1d07d54887510d7cb6cb73b, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107305707288143730 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102545940477301718} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102545940477301718} diff --git a/Final Platformer/Assets/Animations/EnemySoldier_0.controller.meta b/Final Platformer/Assets/Animations/EnemySoldier_0.controller.meta new file mode 100644 index 0000000..e7e3358 --- /dev/null +++ b/Final Platformer/Assets/Animations/EnemySoldier_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5fb2e60ddbfaf324aa46c7d79cd37837 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Explosion.anim b/Final Platformer/Assets/Animations/Explosion.anim new file mode 100644 index 0000000..43a5027 --- /dev/null +++ b/Final Platformer/Assets/Animations/Explosion.anim @@ -0,0 +1,86 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Explosion + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - time: 0.16666667 + value: {fileID: 21300004, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - time: 0.25 + value: {fileID: 21300006, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - time: 0.33333334 + value: {fileID: 21300008, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - time: 0.41666666 + value: {fileID: 21300010, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - time: 0.5 + value: {fileID: 21300012, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - {fileID: 21300002, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - {fileID: 21300004, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - {fileID: 21300006, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - {fileID: 21300008, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - {fileID: 21300010, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + - {fileID: 21300012, guid: dc4e7b57888a107409c3355644d01c35, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.5833333 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Explosion.anim.meta b/Final Platformer/Assets/Animations/Explosion.anim.meta new file mode 100644 index 0000000..d2f5eaa --- /dev/null +++ b/Final Platformer/Assets/Animations/Explosion.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c9ff614e981cbc34f930248571877493 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Explosion_0.controller b/Final Platformer/Assets/Animations/Explosion_0.controller new file mode 100644 index 0000000..94f8b3c --- /dev/null +++ b/Final Platformer/Assets/Animations/Explosion_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Explosion_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107290026821288538} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102070755229744732 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Explosion + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: c9ff614e981cbc34f930248571877493, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107290026821288538 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102070755229744732} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102070755229744732} diff --git a/Final Platformer/Assets/Animations/Explosion_0.controller.meta b/Final Platformer/Assets/Animations/Explosion_0.controller.meta new file mode 100644 index 0000000..6e0fc55 --- /dev/null +++ b/Final Platformer/Assets/Animations/Explosion_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 11f4a0b2afb727448b4b02170f5741ca +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Fire.anim b/Final Platformer/Assets/Animations/Fire.anim new file mode 100644 index 0000000..9be229f --- /dev/null +++ b/Final Platformer/Assets/Animations/Fire.anim @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Fire + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: 11795c5b39923874a848d0e5bde8eae4, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: 11795c5b39923874a848d0e5bde8eae4, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: 11795c5b39923874a848d0e5bde8eae4, type: 3} + - {fileID: 21300002, guid: 11795c5b39923874a848d0e5bde8eae4, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.16666667 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Fire.anim.meta b/Final Platformer/Assets/Animations/Fire.anim.meta new file mode 100644 index 0000000..981dee8 --- /dev/null +++ b/Final Platformer/Assets/Animations/Fire.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 64e60e9f716e8b34c968c673e568533b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Fire_0.controller b/Final Platformer/Assets/Animations/Fire_0.controller new file mode 100644 index 0000000..c84a992 --- /dev/null +++ b/Final Platformer/Assets/Animations/Fire_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Fire_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107619870627287880} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102748369144886850 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Fire + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 64e60e9f716e8b34c968c673e568533b, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107619870627287880 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102748369144886850} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102748369144886850} diff --git a/Final Platformer/Assets/Animations/Fire_0.controller.meta b/Final Platformer/Assets/Animations/Fire_0.controller.meta new file mode 100644 index 0000000..e83908e --- /dev/null +++ b/Final Platformer/Assets/Animations/Fire_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6117782ffe79f814681bf7e3febc9a94 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/FlyingRobot 1_0.controller b/Final Platformer/Assets/Animations/FlyingRobot 1_0.controller new file mode 100644 index 0000000..0833ac2 --- /dev/null +++ b/Final Platformer/Assets/Animations/FlyingRobot 1_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FlyingRobot 1_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107384341262617590} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102163242884382038 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FlyingRobot + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: f47cfd86962d42b4da6335de80fab0c3, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107384341262617590 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102163242884382038} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102163242884382038} diff --git a/Final Platformer/Assets/Animations/FlyingRobot 1_0.controller.meta b/Final Platformer/Assets/Animations/FlyingRobot 1_0.controller.meta new file mode 100644 index 0000000..4cbe7bd --- /dev/null +++ b/Final Platformer/Assets/Animations/FlyingRobot 1_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 65b630b130745bd448624b66eff6911f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/FlyingRobot.anim b/Final Platformer/Assets/Animations/FlyingRobot.anim new file mode 100644 index 0000000..15da03e --- /dev/null +++ b/Final Platformer/Assets/Animations/FlyingRobot.anim @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FlyingRobot + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: 5027d5afa25df344eb829fd6a81c53fa, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: 5027d5afa25df344eb829fd6a81c53fa, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: 5027d5afa25df344eb829fd6a81c53fa, type: 3} + - {fileID: 21300002, guid: 5027d5afa25df344eb829fd6a81c53fa, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.16666667 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/FlyingRobot.anim.meta b/Final Platformer/Assets/Animations/FlyingRobot.anim.meta new file mode 100644 index 0000000..ef52474 --- /dev/null +++ b/Final Platformer/Assets/Animations/FlyingRobot.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f47cfd86962d42b4da6335de80fab0c3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Idle.anim b/Final Platformer/Assets/Animations/Idle.anim new file mode 100644 index 0000000..7444897 --- /dev/null +++ b/Final Platformer/Assets/Animations/Idle.anim @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Idle + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: e0b3da40c48d42e4e9a46b7a337f3679, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: e0b3da40c48d42e4e9a46b7a337f3679, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: e0b3da40c48d42e4e9a46b7a337f3679, type: 3} + - {fileID: 21300002, guid: e0b3da40c48d42e4e9a46b7a337f3679, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.16666667 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 0 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Idle.anim.meta b/Final Platformer/Assets/Animations/Idle.anim.meta new file mode 100644 index 0000000..42b442d --- /dev/null +++ b/Final Platformer/Assets/Animations/Idle.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed947d54463caaa40956f79e75756614 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Jump.anim b/Final Platformer/Assets/Animations/Jump.anim new file mode 100644 index 0000000..a22c3c4 --- /dev/null +++ b/Final Platformer/Assets/Animations/Jump.anim @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Jump + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: dabff26ef6ad84d4ca2093ab47f18736, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: dabff26ef6ad84d4ca2093ab47f18736, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: dabff26ef6ad84d4ca2093ab47f18736, type: 3} + - {fileID: 21300002, guid: dabff26ef6ad84d4ca2093ab47f18736, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.16666667 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Jump.anim.meta b/Final Platformer/Assets/Animations/Jump.anim.meta new file mode 100644 index 0000000..021beff --- /dev/null +++ b/Final Platformer/Assets/Animations/Jump.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: deb10e8c1331059428a6fcb3d7b9524c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/M484SpaceSoldier (1) (1) (2) (1)_0.controller b/Final Platformer/Assets/Animations/M484SpaceSoldier (1) (1) (2) (1)_0.controller new file mode 100644 index 0000000..87473f4 --- /dev/null +++ b/Final Platformer/Assets/Animations/M484SpaceSoldier (1) (1) (2) (1)_0.controller @@ -0,0 +1,394 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: M484SpaceSoldier (1) (1) (2) (1)_0 + serializedVersion: 5 + m_AnimatorParameters: + - m_Name: velocityX + m_Type: 1 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 0} + - m_Name: grounded + m_Type: 4 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 1 + m_Controller: {fileID: 0} + - m_Name: Crouch + m_Type: 4 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 0} + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107035788341513498} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1101 &1101164597389212236 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: grounded + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102619235705885088} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &1101207145056513340 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 3 + m_ConditionEvent: velocityX + m_EventTreshold: 0.001 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102997981354537274} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &1101283603047695568 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: grounded + m_EventTreshold: 0 + - m_ConditionMode: 1 + m_ConditionEvent: Crouch + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102612104218118688} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &1101341507038740950 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 2 + m_ConditionEvent: grounded + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102221933929385494} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &1101380293662221144 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: grounded + m_EventTreshold: 0 + - m_ConditionMode: 3 + m_ConditionEvent: velocityX + m_EventTreshold: 0.01 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102997981354537274} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &1101488541000812514 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 2 + m_ConditionEvent: grounded + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102221933929385494} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &1101902674943803534 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 2 + m_ConditionEvent: Crouch + m_EventTreshold: 0 + - m_ConditionMode: 1 + m_ConditionEvent: grounded + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102619235705885088} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0.75 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &1101948232033795726 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 4 + m_ConditionEvent: velocityX + m_EventTreshold: 0.001 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102619235705885088} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0.4044117 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1102 &1102221933929385494 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Jump + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: + - {fileID: 1101380293662221144} + - {fileID: 1101164597389212236} + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: deb10e8c1331059428a6fcb3d7b9524c, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1102 &1102612104218118688 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Crouch + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: + - {fileID: 1101902674943803534} + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 0} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1102 &1102619235705885088 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Idle + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: + - {fileID: 1101207145056513340} + - {fileID: 1101341507038740950} + - {fileID: 1101283603047695568} + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: ed947d54463caaa40956f79e75756614, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1102 &1102997981354537274 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Walk + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: + - {fileID: 1101948232033795726} + - {fileID: 1101488541000812514} + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 208e6425cdb053441a5af0d6cdc593d3, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107035788341513498 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102619235705885088} + m_Position: {x: 180, y: -36, z: 0} + - serializedVersion: 1 + m_State: {fileID: 1102997981354537274} + m_Position: {x: 509.87396, y: -40.608765, z: 0} + - serializedVersion: 1 + m_State: {fileID: 1102221933929385494} + m_Position: {x: 420, y: -168, z: 0} + - serializedVersion: 1 + m_State: {fileID: 1102612104218118688} + m_Position: {x: 396, y: 108, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: -96, y: 0, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102619235705885088} diff --git a/Final Platformer/Assets/Animations/M484SpaceSoldier (1) (1) (2) (1)_0.controller.meta b/Final Platformer/Assets/Animations/M484SpaceSoldier (1) (1) (2) (1)_0.controller.meta new file mode 100644 index 0000000..5a81046 --- /dev/null +++ b/Final Platformer/Assets/Animations/M484SpaceSoldier (1) (1) (2) (1)_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 40fe2d5607d167c418198e04c330e46a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1) (1) (1)_0.controller b/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1) (1) (1)_0.controller new file mode 100644 index 0000000..0c35139 --- /dev/null +++ b/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1) (1) (1)_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: OpenGunnerEnemies2 (1) (1) (1)_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107849523872359564} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102890165501728300 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Tank + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 0885ef91c2ed89f41a36f15430f1d73d, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107849523872359564 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102890165501728300} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102890165501728300} diff --git a/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1) (1) (1)_0.controller.meta b/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1) (1) (1)_0.controller.meta new file mode 100644 index 0000000..73dfa50 --- /dev/null +++ b/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1) (1) (1)_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e86d6ec18d1be0d439be956f47885d19 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1)_0.controller b/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1)_0.controller new file mode 100644 index 0000000..45fa659 --- /dev/null +++ b/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1)_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: OpenGunnerEnemies2 (1)_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107281108778306198} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102511738600166592 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Tank + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 0885ef91c2ed89f41a36f15430f1d73d, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107281108778306198 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102511738600166592} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102511738600166592} diff --git a/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1)_0.controller.meta b/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1)_0.controller.meta new file mode 100644 index 0000000..07e83a4 --- /dev/null +++ b/Final Platformer/Assets/Animations/OpenGunnerEnemies2 (1)_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5f623dae1a9561141877739f3320e2ad +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/OpenGunnerEnemies2_0.controller b/Final Platformer/Assets/Animations/OpenGunnerEnemies2_0.controller new file mode 100644 index 0000000..f7187a3 --- /dev/null +++ b/Final Platformer/Assets/Animations/OpenGunnerEnemies2_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: OpenGunnerEnemies2_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107062226749951608} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102460941184339966 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: AnnoyingRobot + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 38a38b163c2f2dd4881969727b554f21, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107062226749951608 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102460941184339966} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102460941184339966} diff --git a/Final Platformer/Assets/Animations/OpenGunnerEnemies2_0.controller.meta b/Final Platformer/Assets/Animations/OpenGunnerEnemies2_0.controller.meta new file mode 100644 index 0000000..0178199 --- /dev/null +++ b/Final Platformer/Assets/Animations/OpenGunnerEnemies2_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 49d76e511c2ff0645ba15674375707fa +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Tank.anim b/Final Platformer/Assets/Animations/Tank.anim new file mode 100644 index 0000000..0674720 --- /dev/null +++ b/Final Platformer/Assets/Animations/Tank.anim @@ -0,0 +1,77 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Tank + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: 14cbf653804ea8646b90644a0935d735, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: 14cbf653804ea8646b90644a0935d735, type: 3} + - time: 0.16666667 + value: {fileID: 21300004, guid: 14cbf653804ea8646b90644a0935d735, type: 3} + - time: 0.25 + value: {fileID: 21300006, guid: 14cbf653804ea8646b90644a0935d735, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: 14cbf653804ea8646b90644a0935d735, type: 3} + - {fileID: 21300002, guid: 14cbf653804ea8646b90644a0935d735, type: 3} + - {fileID: 21300004, guid: 14cbf653804ea8646b90644a0935d735, type: 3} + - {fileID: 21300006, guid: 14cbf653804ea8646b90644a0935d735, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.33333334 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Tank.anim.meta b/Final Platformer/Assets/Animations/Tank.anim.meta new file mode 100644 index 0000000..28593d6 --- /dev/null +++ b/Final Platformer/Assets/Animations/Tank.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0885ef91c2ed89f41a36f15430f1d73d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Turret (1)_0.controller b/Final Platformer/Assets/Animations/Turret (1)_0.controller new file mode 100644 index 0000000..55e057e --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret (1)_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Turret (1)_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107707776921393522} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102144005516007442 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: TurretFire + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 11fed47c1133fea4f91337f6ea76e353, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107707776921393522 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102144005516007442} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102144005516007442} diff --git a/Final Platformer/Assets/Animations/Turret (1)_0.controller.meta b/Final Platformer/Assets/Animations/Turret (1)_0.controller.meta new file mode 100644 index 0000000..afb2a9c --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret (1)_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 54679c4525e873b4ebbcaae2046d6ff1 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Turret 1_0.controller b/Final Platformer/Assets/Animations/Turret 1_0.controller new file mode 100644 index 0000000..b865f8a --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret 1_0.controller @@ -0,0 +1,168 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Turret 1_0 + serializedVersion: 5 + m_AnimatorParameters: + - m_Name: IsFire + m_Type: 4 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 0 + m_Controller: {fileID: 0} + - m_Name: Idle + m_Type: 4 + m_DefaultFloat: 0 + m_DefaultInt: 0 + m_DefaultBool: 1 + m_Controller: {fileID: 0} + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107886632814642292} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1101 &1101491211977174380 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 2 + m_ConditionEvent: IsFire + m_EventTreshold: 0 + - m_ConditionMode: 1 + m_ConditionEvent: Idle + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102659613690681390} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0.75 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1101 &1101553344138589332 +AnimatorStateTransition: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_Conditions: + - m_ConditionMode: 1 + m_ConditionEvent: IsFire + m_EventTreshold: 0 + m_DstStateMachine: {fileID: 0} + m_DstState: {fileID: 1102847234881252158} + m_Solo: 0 + m_Mute: 0 + m_IsExit: 0 + serializedVersion: 3 + m_TransitionDuration: 0 + m_TransitionOffset: 0 + m_ExitTime: 0 + m_HasExitTime: 0 + m_HasFixedDuration: 1 + m_InterruptionSource: 0 + m_OrderedInterruption: 1 + m_CanTransitionToSelf: 1 +--- !u!1102 &1102659613690681390 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Turret Idle + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: + - {fileID: 1101553344138589332} + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 5f99ec5dc5a554344af95e4fb2488783, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1102 &1102847234881252158 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: TurretFire + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: + - {fileID: 1101491211977174380} + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 11fed47c1133fea4f91337f6ea76e353, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107886632814642292 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102659613690681390} + m_Position: {x: 192, y: -36, z: 0} + - serializedVersion: 1 + m_State: {fileID: 1102847234881252158} + m_Position: {x: 492, y: -96, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102659613690681390} diff --git a/Final Platformer/Assets/Animations/Turret 1_0.controller.meta b/Final Platformer/Assets/Animations/Turret 1_0.controller.meta new file mode 100644 index 0000000..e1cacd2 --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret 1_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b07e94a3671a1c74c86dab12c609c48b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Turret Idle.anim b/Final Platformer/Assets/Animations/Turret Idle.anim new file mode 100644 index 0000000..8f0bf22 --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret Idle.anim @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Turret Idle + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: f8c2f43a09fef4847b902c8b7cbb04da, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: f8c2f43a09fef4847b902c8b7cbb04da, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: f8c2f43a09fef4847b902c8b7cbb04da, type: 3} + - {fileID: 21300002, guid: f8c2f43a09fef4847b902c8b7cbb04da, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.16666667 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Turret Idle.anim.meta b/Final Platformer/Assets/Animations/Turret Idle.anim.meta new file mode 100644 index 0000000..fc314a4 --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret Idle.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5f99ec5dc5a554344af95e4fb2488783 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Turret2.anim b/Final Platformer/Assets/Animations/Turret2.anim new file mode 100644 index 0000000..e6c0a79 --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret2.anim @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Turret2 + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: f8a6d32aadaf0ec42a4d76b34b1d4017, type: 3} + - time: 0.25 + value: {fileID: 21300002, guid: f8a6d32aadaf0ec42a4d76b34b1d4017, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 4 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: f8a6d32aadaf0ec42a4d76b34b1d4017, type: 3} + - {fileID: 21300002, guid: f8a6d32aadaf0ec42a4d76b34b1d4017, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.5 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Turret2.anim.meta b/Final Platformer/Assets/Animations/Turret2.anim.meta new file mode 100644 index 0000000..b077a99 --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret2.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 10e6f1bc80706064fa020d695ec0dfb2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Turret2idle (1)_0.controller b/Final Platformer/Assets/Animations/Turret2idle (1)_0.controller new file mode 100644 index 0000000..35e8492 --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret2idle (1)_0.controller @@ -0,0 +1,72 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Turret2idle (1)_0 + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: 1107890486112983382} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &1102350261439855136 +AnimatorState: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Turret2 + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 10e6f1bc80706064fa020d695ec0dfb2, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &1107890486112983382 +AnimatorStateMachine: + serializedVersion: 5 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: 1102350261439855136} + m_Position: {x: 200, y: 0, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: 50, y: 20, z: 0} + m_EntryPosition: {x: 50, y: 120, z: 0} + m_ExitPosition: {x: 800, y: 120, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: 1102350261439855136} diff --git a/Final Platformer/Assets/Animations/Turret2idle (1)_0.controller.meta b/Final Platformer/Assets/Animations/Turret2idle (1)_0.controller.meta new file mode 100644 index 0000000..5ea998d --- /dev/null +++ b/Final Platformer/Assets/Animations/Turret2idle (1)_0.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 179ef4ba59685f44db0f253feb10d112 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/TurretFire.anim b/Final Platformer/Assets/Animations/TurretFire.anim new file mode 100644 index 0000000..7f74f7f --- /dev/null +++ b/Final Platformer/Assets/Animations/TurretFire.anim @@ -0,0 +1,71 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: TurretFire + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: ec1786e3492a2a4469492256e39845fd, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: ec1786e3492a2a4469492256e39845fd, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: ec1786e3492a2a4469492256e39845fd, type: 3} + - {fileID: 21300002, guid: ec1786e3492a2a4469492256e39845fd, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.16666667 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/TurretFire.anim.meta b/Final Platformer/Assets/Animations/TurretFire.anim.meta new file mode 100644 index 0000000..02a7b07 --- /dev/null +++ b/Final Platformer/Assets/Animations/TurretFire.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 11fed47c1133fea4f91337f6ea76e353 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/Animations/Walk.anim b/Final Platformer/Assets/Animations/Walk.anim new file mode 100644 index 0000000..d9f048f --- /dev/null +++ b/Final Platformer/Assets/Animations/Walk.anim @@ -0,0 +1,89 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!74 &7400000 +AnimationClip: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Walk + serializedVersion: 6 + m_Legacy: 0 + m_Compressed: 0 + m_UseHighQualityCurve: 1 + m_RotationCurves: [] + m_CompressedRotationCurves: [] + m_EulerCurves: [] + m_PositionCurves: [] + m_ScaleCurves: [] + m_FloatCurves: [] + m_PPtrCurves: + - curve: + - time: 0 + value: {fileID: 21300000, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - time: 0.083333336 + value: {fileID: 21300002, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - time: 0.16666667 + value: {fileID: 21300004, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - time: 0.25 + value: {fileID: 21300006, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - time: 0.33333334 + value: {fileID: 21300008, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - time: 0.41666666 + value: {fileID: 21300010, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - time: 0.5 + value: {fileID: 21300012, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - time: 0.5833333 + value: {fileID: 21300014, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + attribute: m_Sprite + path: + classID: 212 + script: {fileID: 0} + m_SampleRate: 12 + m_WrapMode: 0 + m_Bounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 0, y: 0, z: 0} + m_ClipBindingConstant: + genericBindings: + - serializedVersion: 2 + path: 0 + attribute: 0 + script: {fileID: 0} + typeID: 212 + customType: 23 + isPPtrCurve: 1 + pptrCurveMapping: + - {fileID: 21300000, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - {fileID: 21300002, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - {fileID: 21300004, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - {fileID: 21300006, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - {fileID: 21300008, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - {fileID: 21300010, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - {fileID: 21300012, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + - {fileID: 21300014, guid: 82b423134da387a48a4da497b65d69c1, type: 3} + m_AnimationClipSettings: + serializedVersion: 2 + m_AdditiveReferencePoseClip: {fileID: 0} + m_AdditiveReferencePoseTime: 0 + m_StartTime: 0 + m_StopTime: 0.6666666 + m_OrientationOffsetY: 0 + m_Level: 0 + m_CycleOffset: 0 + m_HasAdditiveReferencePose: 0 + m_LoopTime: 1 + m_LoopBlend: 0 + m_LoopBlendOrientation: 0 + m_LoopBlendPositionY: 0 + m_LoopBlendPositionXZ: 0 + m_KeepOriginalOrientation: 0 + m_KeepOriginalPositionY: 1 + m_KeepOriginalPositionXZ: 0 + m_HeightFromFeet: 0 + m_Mirror: 0 + m_EditorCurves: [] + m_EulerEditorCurves: [] + m_HasGenericRootTransform: 0 + m_HasMotionFloatCurves: 0 + m_Events: [] diff --git a/Final Platformer/Assets/Animations/Walk.anim.meta b/Final Platformer/Assets/Animations/Walk.anim.meta new file mode 100644 index 0000000..7f5a779 --- /dev/null +++ b/Final Platformer/Assets/Animations/Walk.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 208e6425cdb053441a5af0d6cdc593d3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject.meta b/Final Platformer/Assets/AstarPathfindingProject.meta new file mode 100644 index 0000000..347f01b --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6942e1e80e8b54fde9147e843f1bec4f diff --git a/Final Platformer/Assets/AstarPathfindingProject/AstarPathfindingProject.asmdef b/Final Platformer/Assets/AstarPathfindingProject/AstarPathfindingProject.asmdef new file mode 100644 index 0000000..96b5a01 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/AstarPathfindingProject.asmdef @@ -0,0 +1,8 @@ +{ + "name": "AstarPathfindingProject", + "references": [], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false +} \ No newline at end of file diff --git a/Final Platformer/Assets/AstarPathfindingProject/AstarPathfindingProject.asmdef.meta b/Final Platformer/Assets/AstarPathfindingProject/AstarPathfindingProject.asmdef.meta new file mode 100644 index 0000000..841e06d --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/AstarPathfindingProject.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: efa45043feb7e4147a305b73b5cea642 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Behaviors.meta b/Final Platformer/Assets/AstarPathfindingProject/Behaviors.meta new file mode 100644 index 0000000..5e842d2 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Behaviors.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 5eeb3c5df5ca840d3ae6b93e6b207d74 +folderAsset: yes +timeCreated: 1497206641 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Behaviors/AIDestinationSetter.cs b/Final Platformer/Assets/AstarPathfindingProject/Behaviors/AIDestinationSetter.cs new file mode 100644 index 0000000..1fd9af6 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Behaviors/AIDestinationSetter.cs @@ -0,0 +1,39 @@ +using UnityEngine; +using System.Collections; + +namespace Pathfinding { + /// + /// Sets the destination of an AI to the position of a specified object. + /// This component should be attached to a GameObject together with a movement script such as AIPath, RichAI or AILerp. + /// This component will then make the AI move towards the set on this component. + /// + /// See: + /// + /// [Open online documentation to see images] + /// + [UniqueComponent(tag = "ai.destination")] + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_a_i_destination_setter.php")] + public class AIDestinationSetter : VersionedMonoBehaviour { + /// The object that the AI should move to + public Transform target; + IAstarAI ai; + + void OnEnable () { + ai = GetComponent(); + // Update the destination right before searching for a path as well. + // This is enough in theory, but this script will also update the destination every + // frame as the destination is used for debugging and may be used for other things by other + // scripts as well. So it makes sense that it is up to date every frame. + if (ai != null) ai.onSearchPath += Update; + } + + void OnDisable () { + if (ai != null) ai.onSearchPath -= Update; + } + + /// Updates the AI's destination every frame + void Update () { + if (target != null && ai != null) ai.destination = target.position; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Behaviors/AIDestinationSetter.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Behaviors/AIDestinationSetter.cs.meta new file mode 100644 index 0000000..e63d0e6 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Behaviors/AIDestinationSetter.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c9679e68a0f1144e79c664d9a11ca121 +timeCreated: 1495015523 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Behaviors/Patrol.cs b/Final Platformer/Assets/AstarPathfindingProject/Behaviors/Patrol.cs new file mode 100644 index 0000000..1a8d913 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Behaviors/Patrol.cs @@ -0,0 +1,59 @@ +using UnityEngine; +using System.Collections; + +namespace Pathfinding { + /// + /// Simple patrol behavior. + /// This will set the destination on the agent so that it moves through the sequence of objects in the array. + /// Upon reaching a target it will wait for seconds. + /// + /// See: + /// See: + /// See: + /// See: + /// + [UniqueComponent(tag = "ai.destination")] + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_patrol.php")] + public class Patrol : VersionedMonoBehaviour { + /// Target points to move to in order + public Transform[] targets; + + /// Time in seconds to wait at each target + public float delay = 0; + + /// Current target index + int index; + + IAstarAI agent; + float switchTime = float.PositiveInfinity; + + protected override void Awake () { + base.Awake(); + agent = GetComponent(); + } + + /// Update is called once per frame + void Update () { + if (targets.Length == 0) return; + + bool search = false; + + // Note: using reachedEndOfPath and pathPending instead of reachedDestination here because + // if the destination cannot be reached by the agent, we don't want it to get stuck, we just want it to get as close as possible and then move on. + if (agent.reachedEndOfPath && !agent.pathPending && float.IsPositiveInfinity(switchTime)) { + switchTime = Time.time + delay; + } + + if (Time.time >= switchTime) { + index = index + 1; + search = true; + switchTime = float.PositiveInfinity; + } + + index = index % targets.Length; + agent.destination = targets[index].position; + + if (search) agent.SearchPath(); + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Behaviors/Patrol.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Behaviors/Patrol.cs.meta new file mode 100644 index 0000000..9bd0332 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Behaviors/Patrol.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 22e6c29e32504465faa943c537d8029b +timeCreated: 1495286303 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/CHANGELOG.md b/Final Platformer/Assets/AstarPathfindingProject/CHANGELOG.md new file mode 100644 index 0000000..1080048 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/CHANGELOG.md @@ -0,0 +1,1906 @@ +## 4.2.15 (2020-03-30) +- Added \link Pathfinding.IAstarAI.GetRemainingPath ai.GetRemainingPath\endlink. +- Fixed importing the package using the unity package manager would cause the A* inspector to not be able to load since it couldn't find the editor resources folder. +- Fixed the package would report the wrong version number. This could cause the "New Update Available" window to show up unnecessarily. + +## 4.2.14 (2020-03-23) +- Fixed DynamicGridObstacle throwing an exception when scanning sometimes. This bug was introduced in 4.2.13. + +## 4.2.13 (2020-03-21) +- This release contains fixes and features that have been backported from the 4.3 beta. +- Fixed paths could sometimes be cancelled without a reason if 'draw gizmos' was disabled on the Seeker component. Thanks Eran for reporting the bug. +- Fixed DynamicGridObstacle logging an error about not having a collider attached even outside of play mode. + +## 4.2.12 (2020-02-20) +- Fixed "Not allowed to access vertices on mesh" error which some users are seeing after upgrading to Unity 2019.3. + +## 4.2.11 (2019-11-28) +- Fixed animations for the agent character were missing in the example scenes in some newer versions of Unity. This could cause exceptions to be thrown in some example scenes. +- Removed some stray uses of the old and deprecated GUIText component. This could cause descriptions for some example scenes not to show up in newer versions of Unity. + +## 4.2.10 (2019-11-19) +- This release contains fixes and features that have been backported from the 4.3 beta. +- Upgrade notes + - This release is supported in Unity 2017.4 LTS and later. Support for earlier versions has been dropped (however it probably still works in all 2017.x releases). + - The Unity WebPlayer target is no longer supported. This target was deprecated in Unity 5.4. +- Known bugs + - In some versions of Unity the spider bot in the example scenes may be missing its animations. This is due to a bug in how Unity upgrades scenes and is unfortunately tricky to fix for all Unity versions simultaneously. This does not affect any other part of the package. +- Fixed a crash when scanning a graph on the WebGL platform and exception support is disabled. + +## 4.2.9 (2019-11-15) +- This release contains fixes and features that have been backported from the 4.3 beta. +- Upgrade notes + - This release is supported in Unity 2017.4 LTS and later. Support for earlier versions has been dropped (however it probably still works in all 2017.x releases). + - The Unity WebPlayer target is no longer supported. This target was deprecated in Unity 5.4. + - Added a visualization for which dimension of the hexagons that is being edited when using a hexagonal grid graph. + \shadowimage{gridgraph/hexagon_dimension.png} +- RichAI's Funnel Simplification now does a straight line check as the first thing it does. + This can help improve both performance and the quality of the path. +- Added a small helper window to the scene view when the A* Inspector is open. + I think this should work well with the dark Unity theme as well, but please start a thread in the support forum if something looks off. + \shadowimage{changelog/scene_view_helper.png} +- Using managed code stripping even up to the High level is now supported out of the box. +- If an RVOController was attached to a GameObject with an AIPath/RichAI component during runtime after the movement script had been initialized then the movement script would previously possibly not find it. + This is fixed now and the RVOController notifies the AIPath/RichAI script that it has been attached. +- The \link Pathfinding.IAstarAI.SetPath SetPath\endlink method on all built-in movement scripts can now be passed a null parameter. This will clear the current path of the agent. + In earlier versions this caused an exception to be thrown. +- Fixed NavmeshBase.Linecast (used by RecastGraph and Navmesh linecast methods) would not fill the \a trace out parameter with the starting node in case the start point of the linecast was identical to the end point. +- AIPath and RichAI now clear their paths when they are disabled. Not clearing the path has caused some issues when using object pooling and also some other unexpected behavior. + If you want to just temporarily disable the movement then use the \link Pathfinding.IAstarAI.canMove canMove\endlink or \link Pathfinding.IAstarAI.isStopped isStopped\endlink properties. +- Fixed RichAI.remainingDistance could refer to the previous path for one frame after a new path had been calculated. +- Fixed AIPath and RichAI scripts sometimes starting with a height of 0.01 when creating a new component in the unity inspector. + Now they start with a more reasonable height of 2.0. +- The AIBase.usingGravity setter is now protected instead of private which helps when overriding some methods. +- The "Show Surface" mode on recast and navmesh graphs is now enabled by default when creating a new graph. +- The system no longer destroys itself in OnApplicationQuit and is instead always destroyed during OnDestroy. Using OnApplicationQuit could cause trouble when the quitting process was cancelled (e.g. using Application.wantsToQuit). +- Fixed exceptions could be thrown in the editor if the project contains some assemblies that can for some reason not be read. Thanks joshcamas for reporting this. +- Changed the automatic graph coloring limits code to ignore nodes that are unwalkable. This improves the contrast when some unwalkable nodes, that are not visible anyway, have very high penalties (or whatever other value you are visualizing in the scene view). +- Fixed missing null checks in TriangleMeshNode.GetPortal and TriangleMeshNode.SharedEdge. +- The RichAI inspector will now show a helpful warning if one tries to use it in a scene that does not contain a navmesh or recast graph. +- #Pathfinding.GraphUtilities.GetContours for grid graphs now simplifies the contour more consistently. Previously there could be one or two additional points where the algorithm started to traverse the contour. + +## 4.2.8 (2019-04-29) +- Made it possible for nearest node queries on point graphs to find the closest connection instead of just the closest node. + This will make it easier to use graphs when you have many long connections. + See #Pathfinding.PointGraph.nearestNodeDistanceMode. +- Improved the Seeker->StartEndModifier's Connection snapping mode. Now it will behave better if the path only moves along a single connection in the graph. +- Fixed a crash when deploying for Nintendo Switch due to a Unity bug when setting thread names. Thanks ToastyStoemp for reporting this. +- Fixed some compiler warnings in the ObjImporter class that would show up on some platforms. +- Fixed GridGraph.CalculateConnectionsForCellAndNeighbours would throw an exception when called with the coordinates for a node on the border of the grid. Thanks davidpare for reporting this. + +## 4.2.7 (2019-04-05) +- Significantly improved graph rendering performance for recast graphs when using a very large number of small tiles. +- Fixed GridGraph.CountNodes throwing an exception when the graph is not scanned. Now it will return 0. + +## 4.2.6 (2019-03-23) +- Fixed AIPath.reachedDestination and RichAI.reachedDestination only worked when the y coordinate of the agent was close to zero... which it of course was in all my tests. + Sorry about this silly bug and the headache it may have caused. +- Fixed loading a serialized navmesh graph when the source mesh no longer existed would cause the graph to be offset if a navmesh cut was later used to cut it. + +## 4.2.5 (2019-02-14) +- Added a new documentation page for how to create and configure graphs during runtime. \ref runtime-graphs. +- Added a new documentation page about editing point graph connections manually. \ref editing-graphs. +- Fixed exceptions could be thrown if the project contains some assemblies that can for some reason not be read. +- Fixed the visualization for unwalkable nodes (red cubes) sometimes disappearing in newer versions of Unity (2018.2+ I think) due to a change in how Time.renderedFrameCount works. + Thanks Kevin Jenkins for reporting this. +- Fixed applying optimizations (under the Optimizations tab) could cause several error messages to be logged about unsupported platforms in Unity 2018.3 or newer. Thanks NoxMortem for reporting the issue. +- Fixed AIPath throwing an exception if it was given a valid path that contained no nodes at all. +- Made Path.GetTagPenalty public instead of internal. +- Added #Pathfinding.ABPath.FakePath. +- Worked around a null reference exception bug when using IL2CPP and deploying for iPhone. + This is caused by a bug in the IL2CPP compiler. +- Fixed custom graph types could not be used if they were in another assembly. Thanks juskelis for reporting this and founderio for finding a fix. + +## 4.2.4 (2018-12-03) +- Added an option for which dimension of the hexagon to adjust in the grid graph editor when using the hexagonal mode. + This significantly helps with making a hexagonal graph line up with your other game elements as previously you might have had to manually calculate some complicated conversion factors in order to do this. +- Fixed loading navmesh graphs from a file could be extremely slow if the graph had been saved with the source mesh field set to a mesh with an empty name and your project had a lot of things in its Resources folder. +- Fixed a massive performance regression when using RVO together with IL2CPP and .net 4.6 due to changes in how the .net framework handles locking internally. +- Made GraphNode.Destroy public again (it was made internal in 4.2) because without that, it is not possible to make custom graph types. +- Made Path.PipelineState, Path.duration and Path.pathID public again (they were made internal in 4.2) because those properties are actually useful even for non-internal use. + This also fixes some incompatibility issues with the Node Canvas integration package. Thanks jsaracev and Grofit for reporting this. + +## 4.2.3 (2018-11-07) +- Fixed some compiler warnings in the free version on newer versions of Unity. +- Fixed a bug which caused point graphs to interpret the nearest node distance limit as being 1/1000th the actual value in the free version of the package and in the pro version when not using the 'optimize for sparse graph' option. + This bug caused the point graph example scene to not work in the free version of the package. + +## 4.2.2 (2018-10-25) +- Fixed upgrading from an earlier 4.x version to 4.2 could cause compiler errors in some newer versions of Unity because the UnityPackage doesn't import the new directory structure correctly. + +## 4.2.1 (2018-10-23) +- Fixed a bug which caused scanning navmesh graphs to throw an exception when using the free version of the package. Thanks Hunted for reporting the bug. + +## 4.2 (2018-10-17) +- 4.1.17 through 4.1.26 were beta versions, all their changelogs have been merged into this one. +- Upgrade notes + - This release contains some breaking changes (primarily the pivot point change for characters that you can read about below). + This will affect very few users, but I still recommend that you take a backup of your project before upgrading. + - Since 4.1.23 the base of the character when using AIPath/RichAI is always at the pivot point of the Transform. + This reduces code complexity and improves performance. Most users would already have had configured their characters that way, but in + rare cases you may have to adjust your characters a bit (mostly just move them up or down a bit). + - When changing some low level fields that can affect the connectivity of the graph such as #Pathfinding.MeshNode.connections you must now call #Pathfinding.GraphNode.SetConnectivityDirty(). + The documentation for all the relevant fields mention this. Currently it only applies to the connection fields on the MeshNode, PointNode and GridNodeBase classes. + All high level methods that modify the connectivity do this automatically. Most likely you do not have to change your code. + - When updating the graph using a work item, it is no longer necessary to call the QueueFloodFill or FloodFill methods. Due to the hierarchical graph update mentioned below, this is all handled transparently behind the scenes. + You should remove any calls to those deprecated methods as they will (for backwards compatibility reasons) force a full recalculation of the connected components which is much slower than what is likely necessary. +- Improvements + - Converted all comments to XML comments. + This will drastically improve intellisense for most users as Doxygen style comments are not supported by that many editors. + As the whole codebase has been converted using a script, there is a potential for errors in the translation. + Please let me know if you find anything which looks odd or where the intellisense info could be improved. + - Improved recast graph scanning speed by up to 60% in the unity editor on Mono2x. + Surisingly this optimization doesn't seem to matter much for other compiler backends. + This may cause minor changes to your navmesh. If this change causes bad navmeshes to be generated or if it reduces performance, please start a thread in the forum. + - The RVOController and AIPath/RichAI scripts now take the scale of the object into account when using their respective height and radius fields. + If you have an existing character with a non-unit scale the height and radius fields will automatically be updated to compensate for this scale. + However if you are scaling characters during runtime, this may change their behaviour. + - Added a new \link Pathfinding.GraphMask GraphMask\endlink struct to represent a set of graphs. + Previously graph masks have been represented using pure integers (e.g. in #Pathfinding.Seeker.graphMask and #Pathfinding.NNConstraint.graphMask). + The new struct contains some nice helper methods like #Pathfinding.GraphMask.FromGraphName which has been requested by many users. + - Added a new documentation page describing the inspector: \ref inspector. + - Added a new documentation page with an overview of the different included movement scripts: \ref movementscripts. + - Added a new property on all movement scripts called \link Pathfinding.IAstarAI.reachedDestination reachedDestination\endlink. + The existing \link Pathfinding.IAstarAI.reachedEndOfPath reachedEndOfPath\endlink property has some quirks, which are very reasonable when considering how everything works, + but are not very intuitive for new users and it can easily lead to quite brittle code. This new property will work as expected for most use cases. + - Added a height and radius for the AIPath and RichAI movement scripts. + This in turn deprecates the old #Pathfinding.AIBase.centerOffset field as it is now implicitly height/2. + If an RVOController is attached to the same GameObject, its height and radius will be driven by the movement script's values. + The script will try to autodetect reasonable height and radius values from other attached components upon upgrading. + - Improved look of all movement script inspectors by grouping the fields into different sections. Also added some validation to prevent invalid field values. + - Added #Pathfinding.AstarData.GetNodes. + - Optimized graph rendering a bit. In particular if multiple scene views/in game cameras are used. + - The default graph rendering mode is now 'Solid Color' instead of 'Areas'. This new mode will render the graphs with a single color as the name implies. + The Area coloring mode has turned out to sometimes be confusing for new users and it is not useful that often anyway. + - Improved performance of small graph updates on large graphs by a very large amount. + Previously when making a small update to a large graph, updating the connected components of the graph using a flood fill has been the thing which took the longest time by far. + Using a new internal hierarchical graph it is now possible to update large graphs much faster. For example making a small update to a 1024*1024 grid graph is on the order of 30 times faster and is now perfectly reasonable to do in real time (slightly depending on how the graph looks and its settings). + The cost of the flood fill was previously offloaded to a separate thread, so it would not always be noticed in the Unity profiler, but it was there and could affect the performance of the game in strange ways. + The performance of scanning a graph or updating the whole graph remains roughly the same. + For more information about how this works, see #Pathfinding.HierarchicalGraph. + - Removed small allocation that was previously done for each calculated path on grid graphs. + - Pathfinding threads now show up in the Unity Profiler in the Timeline view when using a recent Unity version (2017.3 or higher). + - Scanning layered grid graphs is now approximately 2.2x as fast. + - Updating layered grid graphs is now approximately 1.5x as fast. + - Scanning and updating layered grid graphs now allocates a lot less memory. + - Added assembly definition (.asmdef) files to the package and restructured things a bit. + This will help cut down on the compilation times in your project. See https://docs.unity3d.com/Manual/ScriptCompilationAssemblyDefinitionFiles.html for more info. + - Improved performance of scanning and updating recast graphs, in particular with very large tiles. + - Improved performance of get nearest queries on point graphs when not using \link Pathfinding.PointGraph.optimizeForSparseGraph optimizeForSparseGraph\endlink. + - Reduced memory usage of local avoidance slightly. + - All navmesh/recast graphs now support navmesh cutting out of the box and the TileHandlerHelper component is no longer necessary. + If you have a TileHandlerHelper component in your scene you can safely delete it after upgrading. + - AIPath/RichAI now has an option for disabling rotation completely. This was possible before as well, but the options you had to set were not entirely intuitive. + The inspector has been restructured a bit to improve the ease of use. +- Changes + - Removed non-generic version of PathPool which has been deprecated since early 2016. + - Removed various methods in the AstarMath and Polygon classes which have been deprecated since early 2016. + - Removed a few other things that have been deprecated since early 2016. + - Removed AstarPath.ScanLoop which has been deprecated since 2015. + - Removed IntRect.Rotate and IntRect.Offset because they were not used by any part of the package. + - Removed the mostly internal method #Pathfinding.GraphCollision.Raycast because it was not used by any code in the package. + - Changed signature and behavior slightly of the mostly internal method #Pathfinding.GraphCollision.CheckHeightAll. + - Replaced #Pathfinding.LayerGridGraph.SampleCell with the new method #Pathfinding.LayerGridGraph.SampleHeights which does essentially the same thing, but in a more efficient way. + - The rotationIn2D option for the built-in movement scripts has been renamed to \link Pathfinding.AIBase.orientation orientation\endlink and is now an enum. +- Fixes + - Replaced usage of the WWW class with the newer UnityWebRequest in the update checker to avoid a deprecation compiler warning in newer versions of Unity. + - Fixed a bug in the A* inspector that could cause the inspector to log errors and look weird in Unity 2018.3b5 and up. Thanks Aisit for reporting the bug. + - Fixed a rare race condition that could cause exceptions in various parts of the code due to the StackPool class not being thread safe. Thanks nindim for reporting the issue. + - Fixed applying optimizations (under the Optimizations tab) could cause several error messages to be logged about unsupported platforms in Unity 2018.2 or newer and in 2017.4. + - Fixed trying to set a tag on a node to a value outside the valid range (0...32) could silently cause very weird behavior as other fields were affected by that. + - Fixed minor allocation when calling #AstarPath.GetNearest without an NNConstraint. + - Fixed a bug introduced in 4.1.22 which could cause a null reference exception to sometimes be thrown when visualizing graphs in the scene view. + - Fixed #Pathfinding.Path.IsDone could due to a race condition return true a tiny amount of time before the path was actually calculated. + - Fixed a bug introduced in 4.1.18 which caused various rotation settings to be hidden in the inspector for the RichAI component. + - Fixed removing a movement script from the same component as an RVOController during runtime would not cause the RVOController to stop using the (now destroyed) movement script's position for local avoidance calculations. + - Fixed the ObjectPlacer example script which is used in some example scenes would not always update the graph properly after destroying objects. + In Unity object destruction is delayed until after the Update loop, but the script did not wait to update the graph until after the object had actually been destroyed. Thanks djzombie for reporting this. + - Fixed recalculating a part of a recast graph using a graph update object would not expand the bounding box with the character radius to make sure that all tiles that could be affected by something inside the box were updated. + - Worked around occational crashes when using RVO with the IL2CPP backend due to a bug in IL2CPP. + - Fixed scanning a recast graph could throw an exception if there was a RecastMeshObj component attached to a GameObject with a MeshFilter that had a null/missing mesh. Thanks ccm for finding the bug. + - Fixed FloodPath throwing an exception when starting on a node that isn't connected to any other node. Thanks BulwarkStudios for finding the bug. + - Fixed applying optimizations (under the Optimizations tab) could cause several error messages to be logged about unsupported platforms in Unity 2018.1 or newer. Thanks NFMonster for reporting the issue. + - Rotation speed and acceleration are now decoupled for AIPath and RichAI. Previously the acceleration limited how quickly the agents could rotate due to how the math for centripetal acceleration works out. + This was originally added in 4.1.11, but due to a typo the change was disabled. Now it is properly working. + - Fixed GraphModifier.OnGraphsPostUpdate being called multiple times during a single update if multiple tiles were updated due to navmesh cutting. + This can significantly improve performance if you are using many off-mesh links or you are using the RVONavmesh component. + - Fixed an edge case bug which could cause get nearest node queries on a newly scanned point graph to return older destroyed nodes. + - Fixed work items throwing exceptions could cause some internal data to get into an invalid state. + - Fixed tree colliders would not be rasterized correctly when using the recast graph and the tree prefabs had been scaled. + - Fixed not finding graph types and graph editors from other assemblies than the one which contains the pathfinding system (important when using asmdef files). + - Fixed #AstarPath.FlushWorkItems always pausing the pathfinding threads even if no work items were queued. + - Fixed loading navmesh graphs (and potentially in rare cases: recast graphs) from the cache or from a file could result in bad graph data which confused the funnel modifier which would create very zig-zaggy paths. + If you have been experiencing this then just re-save your graphs/regenerate the cache after you have updated and then it should work as it should. Thanks Igor Aherne for reporting this. + +## 4.1.16 (2018-04-26) +- Fixed PointNode.ContainsConnection could throw an exception if the node didn't have any connections. +- Fixed AILerp's started out with a destination set to (0,0,0) instead of not having a destination set. + So if you did not set a destination for it, it would try to move to the world origin. + +## 4.1.15 (2018-04-06) +- Fixed RichAI.desiredVelocity always being zero. Thanks sukrit1234 for finding the bug. +- Added some video examples to \link Pathfinding.AIPath.pickNextWaypointDist AIPath.pickNextWaypointDist\endlink. +- Fixed a bug introduced in 4.1.14 which caused scanning recast graphs in the Unity editor to fail with an error sometimes. +- Fixed the position returned from querying the closest point on the graph to a point (AstarPath.GetNearest) on layered grid graphs would always be the node center, not the closest point on the node's surface. Thanks Kevin_Jenkins for reporting this. + This caused among other things the ClosestOnNode option for the Seeker's StartEndModifier to be identical to the SnapToNode option. +- Fixed RVOController.velocity being zero when the game was paused (Time.timeScale = 0). + +## 4.1.14 (2018-03-06) +- Fixed Pathfinding.GridNode.ClosestPointOnNode being completely broken. Thanks Ivan for reporting this. + This was used internally in some cases when pathfinding on grid graphs. So this fixes a few cases of strange pathfinding results too. +- It is now possible to use pathfinding from editor scripts. See \ref editor-mode. + +## 4.1.13 (2018-03-06) +- Fixed LayerGridGraph.GetNode not performing out of bounds checks. +- Exposed a public method \link Pathfinding.PointGraph.ConnectNodes PointGraph.ConnectNodes\endlink which can be useful if you are creating a graph from scratch using e.g PointGraph.AddNode. +- Improved the \ref multiple-agent-types tutorial. +- Improved the \ref custom_movement_script tutorial, among other things it can now also be followed if you are creating a 2D game. + The movement script that you write has also been improved. +- Improved how the RichAI movement script keeps track of the node it is on. It should now be more stable in some cases, especially when the ground's y-coordinate lines up badly with the y-coordinate of the navmesh. +- Added an \link Pathfinding.AIPath.constrainInsideGraph option\endlink to AIPath for constraining the agent to be inside the traversable surface of the graph at all times. + I think it should work everywhere without any issues, but please post in the forum if anything seems to break. +- Fixed the proper fonts were not imported in the documentation html, so for many browsers it fell back to some other less pretty font. + +## 4.1.12 (2018-02-27) +- Fixed right clicking on array elements in the Unity inspector would bring up the 'Show in online documentation' context menu instead of the Unity built-in context menu (which is very useful). +- Navmesh assets used in the navmesh graph no longer have to be at the root of the Resources folder, they can be in any subfolder to the Resources folder. + +## 4.1.11 (2018-02-22) +- You can now set which graphs an agent should use directly on the Seeker component instead of having to do it through code. + \shadowimage{multiple_agents/seeker.png} +- Added tutorial for how to deal with agents of different sizes: \ref multiple-agent-types. +- Fixed scanning recast graphs could in rare cases throw an exception due to a multithreading race condition. Thanks emrys90 for reporting the bug. +- Fixed a regression in 4.0.6 which caused position based penalty to stop working for layered grid graphs. Thanks DougW for reporting the bug. +- Rotation speed and acceleration are now decoupled for AIPath and RichAI. Previously the acceleration limited how quickly the agents could rotate due to how the math for centripetal acceleration works out. +- Acceleration can now be set to a custom value on the AIPath class. It defaults to a 'Default' mode which calculates an acceleration such that the agent reaches its top speed in about 0.4 seconds. This is the same behaviour that was hardcoded in earlier versions. +- Fixed a bug in \link Pathfinding.GraphUtilities.GetContours GraphUtilities.GetContours\endlink for grid graphs when the nodes parameter was explicitly passed as non null that could cause some contours not to be generated. Thanks andrewBeers for reporting the bug. +- Improved documentation for \link Pathfinding.StartEndModifier.Exactness StartEndModifier.Exactness\endlink. + +## 4.1.10 (2018-01-21) +- 4.1.0 through 4.1.9 were beta versions, all their changelogs have been merged into this one. +- Upgrade notes + - Fixed the AIPath script with rotationIn2D would rotate so that the Z axis pointed in the -Z direction instead of as is common for Unity 2D objects: to point in the +Z direction. + - ALL classes are now inside the Pathfinding namespace to reduce potential naming collisions with other packages. + Make sure you have "using Pathfinding;" at the top of your scripts. + Previously most scripts have been inside the Pathfinding namespace, but not all of them. + The exception is the AstarPath script to avoid breaking too much existing code (and it has a very distinctive name so name collisions are not likely). + - Since the API for several movement scripts have been unified (see below), many members of the movement scripts have been deprecated. + Your code should continue to work exactly as before (except bugs of course, but if some other behaviour is broken, please start a thread in the forum) but you may get deprecation warnings. + In most cases the changes should be very easy to make as the visible changes mostly consist of renames. + - A method called \link Pathfinding.IAstarAI.SetPath SetPath\endlink has been added to all movement scripts. This replaces some hacks you could achieve by calling the OnPathComplete method on the movement scripts + from other scripts. If you have been doing that you should now call SetPath instead. + - Paths calculated with a heuristic scale greater than 1 (the default is 1) might be slightly less optimal compared to before. + See below for more information. + - The StartEndModifier's raycasting options are now only used if the 'Original' snapping option is used as that's the only one it makes sense for. + - The RaycastModifier has changed a bit, so your paths might look slightly different, however in all but very rare cases it should be at least as good as in previous versions. + - Linecast methods will now assign the #Pathfinding.GraphHitInfo.node field with the last node that was traversed in case no obstacle was hit, previously it was always null. + - Multithreading is now enabled by default (1 thread). This may affect you if you have been adding the AstarPath component during runtime using a script, though the change is most likely positive. + - The DynamicGridObstacle component will now properly update the graph when the object is deactivated since the object just disappeared and shouldn't block the graph anymore. + Previously it only did this if the object was destroyed, not if it was deactivated. + - If you have written a custom graph type you may have to change the access modifier on some methods. + For example the ScanInternal method has been changed from being public to being protected. + - Some internal methods on graphs have been hidden. They should never have been used by user code + but in case you have done that anyway you will have to access them using the IGraphInternals or IUpdatableGraph interface now. + - Removed some compatibility code for Seekers for when upgrading from version 3.6.7 and earlier (released about 2 years ago). + If you are upgrading from a version that old then the 'Valid Tags' field on the Seeker component may get reset to the default value. + If you did not use that field then you will not have to do anything. + - AIPath now rotates towards actual movement direction when RVO is used. +- Improvements + - Improved pathfinding performance by around 8% for grid graphs, possibly more for other graph types. + This involved removing a special case for when the pathfinding heuristic is not admissable (in short, when A* Inspector -> Settings -> Heuristic Scale was greater than 1). + Now paths calculated with the heuristic scale greater than 1 might be slightly less optimal compared to before. + If this is important I suggest you reduce the heuristic scale to compensate. + Note that as before: a heuristic scale of 1 is the default and if it is greater than 1 then the calculated paths may no longer be the shortest possible ones. + - Improved overall pathfinding performance by an additional 10-12% by heavily optimizing some core algorithms. + - Improved performance of querying for the closest node to a point when using the PointGraph and \link Pathfinding.PointGraph.optimizeForSparseGraph optimizeForSparseGraph\endlink. + The improvements are around 7%. + - Unified the API for the included movement scripts (AIPath, RichAI, AILerp) and added a large number of nice properties and functionality. + - The \link Pathfinding.IAstarAI IAstarAI\endlink interface can now be used with all movement scripts. + - To make it easier to migrate from Unity's navmesh system, this interface has been designed to be similar to Unity's NavmeshAgent API. + - The interface has several nice properties like: + \link Pathfinding.IAstarAI.remainingDistance remainingDistance\endlink, + \link Pathfinding.IAstarAI.reachedEndOfPath reachedEndOfPath\endlink, + \link Pathfinding.IAstarAI.pathPending pathPending\endlink, + \link Pathfinding.IAstarAI.steeringTarget steeringTarget\endlink, + \link Pathfinding.IAstarAI.isStopped isStopped\endlink, + \link Pathfinding.IAstarAI.destination destination\endlink, and many more. + - You no longer need to set the destination of an agent using a Transform object, instead you can simply set the \link Pathfinding.IAstarAI.destination destination\endlink property. + Note that when you upgrade, a new AIDestinationSetter component will be automatically created which has a 'target' field. So your existing code will continue to work. + - Improved behavior when AIPath/RichAI characters move down slopes. + Previously the way gravity was handled could sometimes lead to a 'bouncing' behavior unless the gravity was very high. Old behavior on the left, new behavior on the right. + \htmlonly \endhtmlonly + - Improved the grid graph inspector by adding preconfigured modes for different node shapes: square grids, isometric grids and hexagons. + This also reduces clutter in the inspector since irrelevant options can be hidden. + \shadowimage{changelog/grid_shape.png} + - For 2D grid graphs the inspector will now show a single rotation value instead of a full 3D rotation which makes it a lot easier to configure. + - Improved the performance of the \link Pathfinding.RaycastModifier RaycastModifier\endlink significantly. Common speedups on grid graphs range from 2x to 10x. + - The RaycastModifier now has a \link Pathfinding.RaycastModifier.Quality quality enum\endlink. The higher quality options use a new algorithm that is about the same performance (or slightly slower) compared to the RaycastModifier in previous versions + however it often manages to simplify the path a lot more. + The quality of the previous RaycastModifier with default settings corresponds to somewhere between the Low and Medium qualities. + - Improved support for HiDPI (retina) screens as well as improved visual coherency for some icons. + \shadowimage{changelog/retina_icons.png} + - Improved the 'eye' icon for when a graph's gizmos are disabled to make it easier to spot. + - Added \link Pathfinding.GridGraph.CalculateConnectionsForCellAndNeighbours GridGraph.CalculateConnectionsForCellAndNeighbours\endlink. + - AIPath now works with point graphs in 2D as well (assuming the 'rotate in 2D' checkbox is enabled). + - Improved the performance of the RVONavmesh component when used together with navmesh cutting, especially when many navmesh cuts are moving at the same time. + - A warning is now displayed in the editor if one tries to use both the AIDestinationSetter and Patrol components on an agent at the same time. + - Improved linecasts on recast/navmesh graphs. They are now more accurate (there were some edge cases that previously could cause it to fail) and faster. + Performance has been improved by by around 3x for longer linecasts and 1.4x for shorter ones. + - Linecast methods will now assign the #Pathfinding.GraphHitInfo.node field with the last node that was traversed in case no obstacle was hit. + - Linecast on graphs now set the hit point to the endpoint of the line if no obstacle was hit. Previously the endpoint would be set to Vector3.zero. Thanks borluse for suggesting this. + - Multithreading is now enabled by default (1 thread). + - The DynamicGridObstacle component now works with 2D colliders. + - Clicking on the graph name in the inspector will no longer focus the name text field. + To edit the graph name you will now have to click the Edit/Pen button to the right of the graph name. + Previously it was easy to focus the text field by mistake when you actually wanted to show the graph settings. + \shadowimage{changelog/edit_icon.png} + - Reduced memory usage of the PointGraph when using \link Pathfinding.PointGraph.optimizeForSparseGraph optimizeForSparseGraph\endlink. + - Improved the StartEndModifier inspector slightly. + - The Seeker inspector now has support for multi-editing. + - The AIPath and RichAI scripts now rotate to face the direction they are actually moving with when using local avoidance (RVO) + instead of always facing the direction they want to move with. At very low speeds they fall back to looking the direction they want to move with to avoid jitter. + - Improved the Seeker inspector. Unified the UI for setting tag penalties and determining if a tag should be traversable. + \shadowimage{changelog/seeker_tags.png} + - Reduced string allocations for error messages when paths fail. + - Added support for 2D physics to the #Pathfinding.RaycastModifier component. + - Improved performance of GraphUpdateObjects with updatePhysics=false on rotated navmesh/recast graphs. + - Improved the inspector for AILerp. + - RVO obstacles can now be visualized by enabling the 'Draw Obstacles' checkbox on the RVOSimulator component. + \shadowimage{changelog/rvo_navmesh_obstacle.png} + - Reduced allocations in the funnel modifier. + - Added a 'filter' parameter to \link Pathfinding.PathUtilities.BFS PathUtilities.BFS\endlink and \link Pathfinding.PathUtilities.GetReachableNodes PathUtilities.GetReachableNodes\endlink. + - Added a method called \link Pathfinding.IAstarAI.SetPath SetPath\endlink to all movement scripts. + - Added \link Pathfinding.GraphNode.Graph GraphNode.Graph\endlink. + - Added #Pathfinding.MeshNode.ContainsPoint(Vector3) in addition to the already existing MeshNode.ContainsPoint(Int3). + - Added #Pathfinding.MeshNode.ContainsPointInGraphSpace. + - Added #Pathfinding.TriangleMeshNode.GetVerticesInGraphSpace. + - Added Pathfinding.AstarData.FindGraph(predicate). + - Added Pathfinding.AstarData.FindGraphWhichInheritsFrom(type). + - Added a new class \link Pathfinding.GraphUtilities GraphUtilities\endlink which has some utilities for extracting contours of graphs. + - Added a new method \link Pathfinding.GridGraph.Linecast(GridNodeBase,GridNodeBase) Linecast(GridNodeBase,GridNodeBase)\endlink to the GridGraph class which is much faster than the normal Linecast methods. + - Added \link Pathfinding.GridGraph.GetNode(int,int) GridGraph.GetNode(int,int)\endlink. + - Added MeshNode.AddConnection(node,cost,edge) in addition to the already existing AddConnection(node,cost) method. + - Added a \link Pathfinding.NavMeshGraph.recalculateNormals\endlink setting to the navmesh graph for using the original mesh normals. This is useful for spherical/curved worlds. +- Documentation + - Added a documentation page on error messages: \ref error-messages. + - Added a tutorial on how to create a wandering AI: \ref wander. + - Added tutorial on bitmasks: \ref bitmasks. + - You can now right-click on most fields in the Unity Inspector to bring up a link to the online documentation. + \shadowimage{inspector_doc_links.png} + - Various other documentation improvements and fixes. +- Changes + - Height or collision testing for grid graphs now never hits triggers, regardless of the Unity Physics setting 'Queries Hit Triggers' + which has previously controlled this. + - Seeker.StartPath will no longer overwrite the path's graphMask unless it was explicitly passed as a parameter to the StartPath method. + - The built in movement scripts no longer uses a coroutine for scheduling path recalculations. + This shouldn't have any impact for you unless you have been modifying those scripts. + - Replaced the MineBotAI script that has been used in the tutorials with MineBotAnimation. + The new script does not inherit from AIPath so in the example scenes there is now one AIPath component and one MineBotAnimation script on each unit. + - Removed prompt to make the package support UnityScript which would show up the first time you used the package in a new project. + Few people use UnityScript nowadays so that prompt was mostly annoying. UnityScript support can still be enabled, see \ref javscript. + - If deserialization fails, the graph data will no longer be stored in a backup byte array to be able to be recovered later. + This was not very useful, but more importantly if the graph data was very large (several megabytes) then Unity's Undo system would choke on it + and essentially freeze the Unity editor. + - The StartEndModifier's raycasting options are now only used if the 'Original' snapping option is used as that's the only one it makes sense for. + - The RaycastModifier.subdivideEveryIter field has been removed, this is now always enabled except for the lowest quality setting. + - The RaycastModifier.iterations field has been removed. The number of iterations is now controlled by the quality field. + Unfortunately this setting cannot be directly mapped to a quality value, so if you are upgrading all RaycastModifier components will use the quality Medium after the upgrade. + - The default value for \link Pathfinding.RVOController.lockWhenNotMoving RVOController.lockWhenNotMoving\endlink is now false. + - Tiles are now enabled by default on recast graphs. + - Modifiers now register/unregister themselves with the Seeker component during OnEnable/OnDisable instead of Awake/OnDestroy. + If you have written any custom modifiers which defines those methods you may have to add the 'override' modifier to those methods and call base.OnEnable/OnDisable. + - When paths fail this is now always logged as a warning in the Unity console instead of a normal log message. + - Node connections now store which edge of the node shape that is used for that node. This is used for navmesh/recast graphs. + - The \link Pathfinding.RVOController.velocity velocity\endlink property on the RVOController can now be assigned to and that has the same effect as calling ForceSetVelocity. + - Deprecated the RVOController.ForceSetVelocity method. You should use the velocity property instead. + - All graphs now explicitly implement the IUpdatableGraph interface. + This is done to hide those methods (which should not be used directly) and thereby reduce confusion about which methods should be used to update graphs. + - Hid several internal methods behind the IGraphInternals interface to reduce clutter in the documentation and IntelliSense suggestions. + - Removed NavGraph.UnloadGizmoMeshes because it was not used for anything. + - Since 4.0 individual graphs can be scanned using AstarPath.Scan. The older NavGraph.Scan method now redirects to that method + which is more robust. This may cause slight changes in behavior, however the recommendation in the documentation has always been to use AstarPath.Scan anyway + so I do not expect many to have used the NavGraph.Scan method. + - Deprecated the NavGraph.ScanGraph method since it just does the same thing as NavGraph.Scan. + - Deprecated the internal methods Path.LogError and Path.Log. + - Added the new internal method Path.FailWithError which replaces LogError and Log. + - Made the AIPath.TrySearchPath method private, it should never have been public to begin with. +- Fixes + - Fixed AIPath/RichAI throwing exceptions in the Unity Editor when drawing gizmos if the game starts while they are enabled in a disabled gameObject. + - Fixed some typos in the documentation for PathUtilities.BFS and PathUtilities.GetReachableNodes. + - For some point graph settings, path requests to points that could not be reached would fail completely instead of going to the closest node that it could reach. Thanks BYELIK for reporting this bug. + If you for some reason have been relying on the old buggy behavior you can emulate it by setting A* Inspector -> Settings -> Max Nearest Node Distance to a very low value. + - Fixed connection costs were assumed to be equal in both directions for bidirectional connections. + - Fixed a compiler error when building for UWP/HoloLens. + - Fixed some cases where circles used for debugging could have a much lower resolution than intended (#Pathfinding.Util.Draw.Debug.CircleXZ). + - Fixed RVO agents which were locked but some script sent it movement commands would cause the RVO system to think it was moving + even though it was actually stationary, causing some odd behavior. Now locked agents are always treated as stationary. + - Fixed RVO obstacles generated from graph borders (using the RVONavmesh component) could be incorrect if a tiled recast graph and navmesh cutting was used. + The bug resulted in an RVO obstacle around the tile that was most recently updated by a navmesh cut even where there should be no obstacle. + - Fixed the RVONavmesh component could throw an exception in some cases when using tiled recast graphs. + - Fixed a regression in some 4.0.x version where setting \link Pathfinding.RVOController.velocity RVOController.velocity\endlink to make the agent's movement externally controlled + would not work properly (the system would always think the agent had a velocity of zero). + - Fixed the RichAI movement script could sometimes get stuck on the border between two tiles. + (due to a possibility of division by zero that would cause its velocity to become NaN). + - Fixed AIPath/RichAI movement not working properly with rigidbodies in Unity 2017.3+ when the new physics setting "Auto Sync Transforms" was disabled. Thanks DougW for reporting this and coming up with a fix. + - Fixed a few cases where \link Pathfinding.RichAI RichAI\endlink would automatically recalculate its path even though \link Pathfinding.RichAI.canSearch canSearch\endlink was disabled. + - Fixed some compiler warnings when using Unity 2017.3 or later. + - Fixed graphical artifacts in the graph visualization line drawing code which could show up at very large coordinate values or steep viewing angles. + Differential calculus can be really useful sometimes. + - Fixed the \ref MultiTargetPathExample.cs. + - Fixed the width/depth fields in the recast graph inspector causing warnings to be logged (introduced in 4.1.7). Thanks NoxMortem for reporting this. + - Fixed the Pathfinding.GraphHitInfo.tangentOrigin field was offset by half a node when using linecasting on grid graphs. + - Fixed the AIPath script with rotationIn2D would rotate so that the Z axis pointed in the -Z direction instead of as is common for Unity 2D objects: to point in the +Z direction. + - Fixed the AILerp script with rotationIn2D would rotate incorrectly if it started out with the Z axis pointed in the -Z direction. + - Clamp recast graph bounding box size to be non-zero on all axes. + - The DynamicGridObstacle component will now properly update the graph when the object is deactivated since the object just disappeared and shouldn't block the graph anymore. + Previously it only did this if the object was destroyed, not if it was deactivated. + - Fixed \link Pathfinding.AILerp AILerp\endlink ceasing to work properly if one of the paths it tries to calculate fails. + - Fixed the \link Pathfinding.FunnelModifier FunnelModifier\endlink could yield a zero length path in some rare circumstances when using custom node links. + This could lead to an exception in some of the movement scripts. Thanks DougW for reporting the bug. + - Fixed calling Seeker.CancelCurrentPathRequest could in some cases cause an exception to be thrown due to multithreading race conditions. + - Fixed a multithreading race condition which could cause a path canceled by Seeker.CancelCurrentPathRequest to not actually be canceled. + - Fixed a rare ArrayOutOfBoundsException when using the FunnelModifier with the 'unwrap' option enabled. + - Fixed Seeker -> Start End Modifier could not be expanded in the Unity inspector. Thanks Dee_Lucky for reporting this. + - Fixed a few compatiblity bugs relating to AIPath/RichAI that were introduced in 4.1.0. + - Fixed funnel modifier could sometimes fail if the agent started exactly on the border between two nodes. + - Fixed another bug which could cause the funnel modifier to produce incorrect results (it was checking for colinearity of points in 2D instead of in 3D). + - Fixed the funnel modifier would sometimes clip a corner near the end of the path. + - Fixed ProceduralGridMover would not detect user defined graphs that were subclasses of the GridGraph class. Thanks viveleroi for reporting this. + - Fixed enabling and disabling a AIPath or RichAI component a very large number of times could potentially have a negative performance impact. + - Fixed AIPath/RichAI would continue searching for paths even when the component had been disabled. + - MeshNode.ContainsPoint now supports rotated graphs properly. MeshNode is used in navmesh and recast graphs. + - Fixed Linecast for navmesh and recast graphs not working for rotated graphs. + - Fixed RVONavmesh component not working properly with grid graphs that had height differences. + - Fixed 2D RVO agents sometimes ignoring obstacles. + - Fixed RVONavmesh not removing the obstacles it had created when the component was disabled. + - Fixed RaycastModifier could miss obstacles when thick raycasting was used due to Unity's Physics.SphereCast method not + reporting hits very close to the start of the raycast. + - In the free version the inspector for RaycastModifier now displays a warning if graph raycasting is enabled since + for all built-in graphs raycasts are only supported in the pro version. + - Fixed some cases where the funnel modifier would produce incorrect results. + - Fixed typo in a private method in the AstarPath class. Renamed the UpdateGraphsInteral method to UpdateGraphsInternal. + - Fixed AIPath.remainingDistance and AIPath.targetReached could be incorrect for 1 frame when a new path had just been calculated (introduced in a previous beta release). + +## 4.0.11 (2017-09-09) +- Fixed paths would ignore the ITraversalProvider (used for the turn based utilities) on the first node of the path, resulting in successful paths where they should have failed. +- Fixed BlockManager.BlockMode.AllExceptSelector could often produce incorrect results. Thanks Cquels for spotting the bug. +- Fixed various bugs related to destroying/adding graphs that could cause exceptions. Thanks DougW for reporting this. +- Fixed destroying a grid graph would not correctly clear all custom connections. Thanks DougW for reporting this. +- Fixed the MultiTargetPath did not reset all fields to their default values when using path pooling. +- Added some additional error validation in the MultiTargetPath class. +- Fixed scanning a recast graph that was not using tiles using Unity 2017.1 or later on Windows could block indefinitely. Thanks David Drummond and ceebeee for reporting this. +- Improved compatibility with Nintendo Switch. Thanks Noogy for the help. +- Fixed GraphUpdateScene would not handle the GameObject's scale properly which could cause it to not update some nodes. +- Fixed a regression in 4.0 which could cause the error to be omitted from log messages when paths failed. +- Fixed several bugs relating to #Pathfinding.NNConstraint.distanceXZ and #Pathfinding.NavmeshBase.nearestSearchOnlyXZ. Thanks koirat for reporting this. +- Fixed scanning a graph that threw an error would prevent any future scans. Thanks Baste for reporting this. +- Added a new get started video tutorial. See \ref getstarted. +- The PointGraph.nodeCount property is now protected instead of private, which fixes some compatibility issues. +- Improved compatibility with Unity 2017.1, esp. when using the experimental .Net 4.6 target. Thanks Scott_Richmond for reporting the issues. +- Fixed DynamicGridObstacle trying to update the graphs even when outside of play mode. +- Fixed runtime error when targeting the Windows Store. Thanks cedtat for reporting the bug. +- Fixed compilation error when targeting the Windows Store. Introduced in 4.0.3. Thanks cedtat for reporting the bug. + +## 4.0.10 (2017-05-01) +- Fixed compiler errors in the free version because the ManualRVOAgent.cs script being included by mistake. Thanks hummerbummer for reporting the issue. +- Fixed Unity's scene view picking being blocked by graph gizmos. Thanks Scott_Richmond for reporting the bug. + +## 4.0.9 (2017-04-28) +- Significantly improved performance and reduced allocations when recalculating indivudal recast tiles during runtime and there are terrains in the scene. +- Fixed the GraphUpdateScene inspector showing a warning for one frame after the 'convex' field has been changed. +- Fixed a few compiler warnings in Unity 5.6. Thanks TotalXep for reporting the issue. +- Fixed graph drawing could generate large amounts of garbage due to a missing GetHashCode override which causes Mono to have to allocate some dummy objects. +- Fixed graph gizmo lines could be rendered incorrectly on Unity 5.6 on mac and possibly on Windows too. + +## 4.0.8 (2017-04-28) +- Added \link Pathfinding.AIBase.rotationIn2D rotationIn2D\endlink to the AIPath script. It makes it possible to use the Y axis as the forward axis of the character which is useful for 2D games. +- Exposed the GridGraph.LayerCount property which works for both grid graphs and layered grid graphs (for grid graphs it always returns 1). +- Made the LayerGridGraph.layerCount field internal to discourage its use outside the LayerGridGraph class. +- Fixed exception when destroying some graph types (introduced in 4.0.6). Thanks unfalco for reporting the bug. +- Fixed exception in GridGraph.GetNodesInRegion when being called with an invalid rectangle or a rectangle or bounds object that was completely outside the graph. Thanks WillG for finding the bug. +- Fixed AIPath/RichAI not rotating to the correct direction if they started in a rotation such that the forward axis was perpendicular to the movement plane. + +## 4.0.7 (2017-04-27) +- Fixed 2D example scenes had their grids rotated by (90,0,0) instead of (-90,0,0). + It doesn't matter for those scenes, but the (-90,0,0) leads to more intuitive axis rotations for most use cases. Thanks GeloMan for noticing this. +- Renamed AISimpleLerp to AILerp in the component menu as the documentation only refers to it by the name 'AILerp'. +- Added a new documentation page and video tutorial (\ref pathfinding-2d) showing how to configure pathfinding in 2D games. + +## 4.0.6 (2017-04-21) +- Fixed creating a RichAI and in the same frame setting the target and calling UpdatePath would always result in that path being canceled. +- Fixed a race condition which meant that if you called RichAI.UpdatePath, AILerp.SearchPath or AIPath.SearchPath during the same frame that the agent was created + then the callback for that path would sometimes be missed and the AI would wait indefinitely for it. This could cause the agents to sometimes never start moving. +- Fixed adding a new graph while graph updates were running at the same time could potentially cause errors. +- Added NavGraph.exists which will become false when a graph has been destroyed. +- Fixed TileHandlerHelper could throw exceptions if the graph it was tracking was destroyed. +- Fixed TileHandlerHelper not detecting new NavmeshCut or NavmeshAdd components that were created before the + TileHandlerHelper component was created or when it was disabled. +- TileHandlerHelper no longer logs an error if it is created before a recast/navmesh graph exists in the scene + and when one is created the TileHandlerHelper will automatically detect it and start to update it. +- Fixed TileHandlerHelper could throw exceptions if the graph it was tracking changed dimensions. +- Fixed recast graphs would always rasterize capsule colliders as if they had their 'direction' setting set to 'Y-axis'. Thanks emrys90 for reporting the bug. +- The package now contains a 'documentation.html' file which contains an offline version of the 'Get Started' tutorial. + +## 4.0.5 (2017-04-18) +- Improved compatibility with Opsive's Behavior Designer - Movement Pack (https://www.assetstore.unity3d.com/en/#!/content/16853). + - The 4.0 update combined with the Movement Pack caused some compiler errors previously. + +## 4.0.4 (2017-04-17) +- Fixed the funnel modifier not working if 'Add Points' on the Seeker's Start End Modifier was enabled. Thanks Blaze_Barclay for reporting it. +- Fixed code typo in the \ref write-modifiers tutorial as well as made a few smaller improvements to it. +- Fixed some cases where the LegacyRVOController would not behave like the RVOController before version 4.0. +- Fixed LegacyAIPath not using the same custom inspector as the AIPath component. + +## 4.0.3 (2017-04-16) +- Improved code style and improved documentation for some classes. +- Reduced memory allocations a bit when using the NavmeshAdd component. +- Fixed graph types not necessarily being initialized when scanning the graph outside of play mode. +- Fixed LayerGridGraph not reporting scanning progress properly. + This caused it to not work well with ScanAsync and when scanning the graph in the editor the progress bar would only update once the whole graph had been scanned. +- Removed the DebugUtility class which was only used for development when debugging the recast graph. + +## 4.0.2 (2017-04-16) +- Fixed a minor bug in the update checker. +- Deduplicated code for drawing circles and other shapes using Debug.Draw* or Gizmos.Draw* and moved this code to a new class Pathfinding.Util.Draw. + +## 4.0.1 (2017-04-15) +- Improved how AIPath and RichAI work with rigidbodies. +- Added option for gravity to AIPath. +- Removed the RichAI.raycastingForGroundPlacement field as it is automatically enabled now if any gravity is used. +- AIPath and RichAI now inherit from the same base class Pathfinding.AIBase. + +## 4.0 (2017-04-10) +- Upgrade Notes + - This release contains some significant changes. It is strongly recommended that you back up your + project before upgrading. + - If you get errors immediately after upgrading, try to delete the AstarPathfindingProject folder + and import the package again. Sometimes UnityPackages will leave old files which can cause issues. + - Moved some things to inside the Pathfinding namespace to avoid naming collisions with other packages. + Make sure you have the line 'using Pathfinding;' at the top of your scripts. + Some example scripts have been moved to the Pathfinding.Examples namespace. + - The RVOController component no longer handles movement as it turned out that was a bad idea. + Having multiple components that handled movement (e.g RichAI and RVOController) didn't turn out well + and it was very hard to configure the settings so that it worked well. + The RVOController now exposes the CalculateMovementDelta method which allows other scripts to + ask it how the local avoidance system thinks the character should move during this frame. + If you use the RichAI or AIPath components for movement, everything should work straight away. + If you use a custom movement script you may need to change your code to use the CalculateMovementDelta + method for movement. Some settings may need to be tweaked, but hopefully it should not be too hard. + - Node connections are now represented using an array of structs (of type \link Pathfinding.Connection Connection\endlink) instead of + one array for target nodes and one array for costs. + - When upgrading an existing project legacy versions of the RVOController, RichAI, AIPath and GraphUpdateScene components + will be used for compatibility reasons. You will have to click a button in the inspector to upgrade them to the latest versions. + I have tried to make sure that the movement scripts behave the same as they did before version 4.0, but it is possible that there are some minor differences. + If you have used a custom movement script which inherits from AIPath or RichAI then the legacy components cannot be used automatically, instead the new versions will be used from the start. +- New Features And Improvements + - Local Avoidance + - The RVO system has been cleaned up a lot. + - Agents will now always avoid walls and obstacles even if that would put them on a collision course with another agent. + This helps with a previous problem of agents being able to be pushed into walls and obstacles (note that RVONavmesh or RVOSquareObstacle still need to be used). + - The RVOSimulator can now be configured for XZ space or XY space (2D). + - The RVOController no longer handles movement itself as this turned out to be a really bad idea (see upgrade notes section). + - The RVOController can now be used to stop at a target much more precisely than before using the SetTarget method. + - Agents are now \link Pathfinding.RVO.RVOSimulator.symmetryBreakingBias biased slightly\endlink towards passing other agents on the right side, this helps resolve some situations + with a lot of symmetry much faster. + - All fuzzy and hard to adjust parameters from the \link Pathfinding.RVO.RVOSimulator RVOSimulator\endlink component have been removed. + It should now be much easier to configure. + - The RichAI movement script now works a lot better with the RVOController. + Previously the movement could be drastically different when the RVOController was used + and local avoidance didn't work well when the agent was at the edge of the navmesh. + - Improved gizmos for the RVOController. + - Added \link Pathfinding.RVO.RVOController.ForceSetVelocity RVOController.ForceSetVelocity\endlink to use when you want agents to avoid a player (or otherwise externally controlled) character. + - RVO agents can now have different priorities, lower priority agents will avoid higher priority agents more. + - The neighbour distance field is now automatically calculated. This makes it easier to configure the agents and it will + also improve performance slightly when the agents are moving slowly (for example in very crowded scenarios). + - Added support for grid graphs to \link Pathfinding.RVO.RVONavmesh RVONavmesh\endlink. + - Added a new example scene for RVO in 2D + \htmlonly \endhtmlonly + - General + - Huge increase in the performance of graph gizmos. + This was accomplished by bypassing the Unity Gizmos and creating a custom gizmo rendererer that is able to retain + the gizmo meshes instead of recreating them every frame (as well as using a lot fewer draw calls than Unity Gizmos). + Therefore the graphs usually only need to check if the nodes have changed, and only if they have changed they will + rebuild the gizmo meshes. This may cause graph updates to seem like they introduce more lag than they actually do + since a graph update will also trigger a gizmo rebuild. So make sure to always profile with gizmos disabled. + For a 1000*1000 graph, which previously almost froze the editor, the time per frame went from over 4200 ms to + around 90 ms when no nodes had changed. + \htmlonly \endhtmlonly + - Improved the style of graph gizmos. A solid surface is now rendered instead of only the connections between the nodes. + The previous mode of rendering only connections is of course still available. + \shadowimage{3vs4/gizmos.png} + - Added a new example scene showing how to configure hexagon graphs. + - Added gizmos for hexagon graphs (grid graphs with certain settings). + \shadowimage{3vs4/hexagon_thin.png} + - Implemented async scanning. \link AstarPath.ScanAsync AstarPath.active.ScanAsync \endlink is an IEnumerable that can be iterated over several frames + so that e.g a progress bar can be shown while calculating the graphs. Note that this does not guarantee + a good framerate, but at least you can show a progress bar. + - Improved behaviour of the AIPath movement script. + - AIPath now works in the XY plane as well. In fact it works with any graph rotation. + The Z axis is always the forward axis for the agent, so for 2D games with sprites you may have to attach the sprite + to a child object which is rotated for it to show up correctly. + - Previously the slowdownDistance had to be smaller than the forwardLook field otherwise the character + could slow down even when it had not reached the end of the path. + - The agent should stop much more precisely at the end of the path now. + - The agent now rotates with a fixed angular speed instead of a varying one as this is often more realistic. + - Reduced the likelihood of the agent spinning around when it reaches the end of the path. + - It no longer uses the forwardLook variable. + It was very tricky to set correctly, now the pickNextWaypointDist variable is used for everything instead + and generally this should give you smoother movement. + - Improved behaviour of the \link Pathfinding.RichAI RichAI \endlink movement script. + - The agent should stop much more precisely at the end of the path now. + - Reduced the likelihood of the agent spinning around when it reaches the end of the path. + - Scanning the graph using AstarPath.Scan will now profile the various parts of the graph scanning + process using the Unity profiler (Profiler.BeginSample and Profiler.EndSample). + - \link Pathfinding.DynamicGridObstacle DynamicGridObstacle \endlink will now update the graph immediately if an object with that component is created during runtime + instead of waiting until it was moved for the first time. + - \link Pathfinding.GraphUpdateScene GraphUpdateScene \endlink and \link Pathfinding.GraphUpdateShape GraphUpdateShape \endlink can now handle rotated graphs a lot better. + The rotation of the object the GraphUpdateScene component is attached to determines the 'up' direction for the shape + and thus which points will be considered to be inside the shape. + The world space option had to be removed from GraphUpdateScene because it didn't really work with rotated graphs. + The lockToY option for GraphUpdateScene has also been removed because it wasn't very useful and after this change it would only have had an impact + in rare cases. + - Improved \link Pathfinding.GraphUpdateScene GraphUpdateScene \endlink editor. When editing the points in the scene view it now shows helper lines + to indicate where a new point is going to be added and which other points it will connect to + as well as several other minor improvements. + \htmlonly \endhtmlonly + - \link Pathfinding.GraphUpdateScene GraphUpdateScene \endlink now supports using the bounds from 2D colliders and the shape from PolygonCollider2D. + - Added opaqueness slider for the gizmos under Inspector -> Settings -> Colors. + - Added \link Pathfinding.Path.BlockUntilCalculated Path.BlockUntilCalculated \endlink which is identical to AstarPath.BlockUntilCalculated. + - Added Seeker.CancelCurrentPathRequest. + - Added \link Pathfinding.NavGraph.GetNodes NavGraph.GetNodes(System.Action) \endlink which calls a delegate with each node in the graph. + Previously NavGraph.GetNodes(GraphNodeDelegateCancelable) existed which did the same thing but required the delegate + to return true if it wanted the graph to continue calling it with more nodes. It turns out this functionality was very rarely needed. + - Individual graphs can now be scanned using #AstarPath.Scan(NavGraph) and other related overloads. + - Improved \link Pathfinding.BinaryHeap priority queue \endlink performance. On average results in about a 2% overall pathfinding performance increase. + - ObjectPool now requires a ref parameter when calling Release with an object to help prevent silly bugs. + - 'Min Area Size' has been removed. The edge cases are now handled automatically. + - Added ObjectPoolSimple as a generic object pool (ObjectPool also exists, but for that T must implement IAstarPooledObject). + - \link Pathfinding.RaycastModifier RaycastModifier \endlink now supports multi editing. + - Added \link Pathfinding.GraphNode.RandomPointOnSurface GraphNode.RandomPointOnSurface \endlink. + - Added \link Pathfinding.GraphNode.SurfaceArea GraphNode.SurfaceArea \endlink. + - \link Pathfinding.Int2 Int2 \endlink and \link Pathfinding.Int3 Int3 \endlink now implement IEquatable for slightly better performance and fewer allocations in some places. + - \link Pathfinding.Examples.LocalSpaceRichAI LocalSpaceRichAI \endlink can now be used with any rotation (even things like moving on an object that is upside down). + - The \link Pathfinding.FunnelModifier funnel modifier \endlink can now handle arbitrary graphs (even graphs in the 2D plane) if the new \link Pathfinding.FunnelModifier.unwrap unwrap \endlink option is enabled. + - The \link Pathfinding.FunnelModifier funnel modifier \endlink can split the resulting path at each portal if the new \link Pathfinding.FunnelModifier.splitAtEveryPortal splitAtEveryPortal \endlink option is enabled. + - Recast/Navmesh Graphs + - Recast graph scanning is now multithreaded which can improve scan times significantly. + - Recast graph scanning now handles large worlds with lots of objects better. This can improve scan times significantly. + \htmlonly \endhtmlonly + - Improved performance of nearest node queries for Recast/navmesh graphs. + - Editing navmesh cut properties in the inspector now forces updates to happen immediately which makes editing easier. + - Long edges in recast graphs are now split at tile borders as well as at obstacle borders. + This can in particular help on terrain maps where the tile borders do not follow the elevation that well + so the max edge length can be reduced to allow the border to follow the elevation of the terrain better. + - Recast graphs can now be rotated arbitrarily. + - Navmesh cutting still works! + - The RichAI script currently does not support movement on rotated graphs, but the AIPath script does. + - Improved performance of navmesh cutting for large worlds with many tiles and NavmeshAdd components. + - Navmesh graphs and recast graphs now share the same base code which means that navmesh graphs + now support everything that previously only recast graphs could be used for, for example + navmesh cutting. + - The NavmeshCut inspector now shows a warning if no TileHandlerHelper component is present in the scene. + A TileHandlerHelper component is necessary for the NavmeshCuts to update the graphs. + - Recast graphs now use less memory due to the BBTree class now using around 70% less memory per node. + - Recast graphs now allocate slightly less memory when recalculating tiles or scanning the graph. + - Cell height on Recast graphs is now automatically set to a good value. + - Navmesh cutting is now a bit better at using object pooling to avoid allocations. + - TileHandlerHelper now updates the tiles properly when one or multiple tiles on the recast graph are recalculated + due to a graph update or because it was rescanned. + - Navmesh cutting now uses more pooling to reduce allocations slightly. + - Improved performance of loading and updating (using navmesh cutting) recast tiles with a large number of nodes. + - Grid Graphs + - Added LevelGridNode.XCoordinateInGrid, LevelGridNode.ZCoordinateInGrid, LevelGridNode.LayerCoordinateInGrid. + - Added GridGraph.GetNodesInRegion(IntRect). + Also works for layered grid graphs. + - Layered grid graphs now have support for 'Erosion Uses Tags'. + - Added GridGraph.CalculateConnections(GridNodeBase) which can be used for both grid graphs and layered grid graphs. + - Grid graphs can now draw the surface and outline of the graph instead of just the connections between the nodes. + The inspector now contains several toggles that can be used to switch between the different rendering modes. + - The ProceduralGridMover component now works with LayerGridGraph as well. + - Added GridGraph.RecalculateCell(x,y) which works both for grid graphs and layered grid graphs. + This replaces the UpdateNodePositionCollision method and that method is now deprecated. + - Improved GridGraph.RelocateNodes which is now a lot more resilient against floating point errors. + - Added dimetric (60°) to the list of default values for the isometric angle field on grid graphs. + - Changing the width/depth of a grid graph will now keep the current pivot point at the same position instead of always keeping the bottom left corner fixed. + (the pivot point can be changed to the center/bottom left/top left/top right/bottom right right next to the position field in the grid graph inspector) + - Improved fluidity and stability when resizing a grid graph in the scene view. + It now snaps to full node increments in size. + - Grid graphs now display a faint grid pattern in the scene view even when the graph is not scanned + to make it easier to position and resize the graph. + - Improved styling of some help boxes in the grid graph inspector when using the dark UI skin. + - The size of the unwalkable node gizmo (red cube) on grid graphs is now based on the node size to avoid the gizmos being much larger or much smaller than the nodes. + - Implemented \link Pathfinding.ABPath.EndPointGridGraphSpecialCase special case for paths on grid graphs \endlink so that if you request a path to an unwalkable node with several + walkable nodes around it, it will now not pick the closest walkable node to the requested target point and find a path to that + but it will find the shortest path which goes to any of the walkable nodes around the unwalkable node. + \htmlonly Before, After \endhtmlonly. + This is a special case of the MultiTargetPath, for more complicated configurations of targets the multi target path needs to be used to be able to handle it correctly. +- Changes + - Node connections are now represented using an array of structs (of type Connection) instead of + one array for target nodes and one array for costs. + - When scanning a graph in the editor, the progress bar is not displayed until at least 200 ms has passed. + Since displaying the progress bar is pretty slow, this makes scanning small graphs feel more snappy. + - GridGraph and LayerGridGraph classes now have a 'transform' field instead of a matrix and inverseMatrix fields. + The GraphTransform class also has various other nice utilities. + - Moved mesh collecting code for Recast graphs to a separate class to improve readability. + - Refactored out large parts of the AstarPath class to separate smaller classes to improve readability and increase encapsulation. + - AstarPath.RegisterSafeUpdate is now implemented using WorkItems. This yields a slightly different behavior (previously callbacks added using RegisterSafeUpdate would + always be executed before work items), but that should rarely be something that you would depend on. + - Replaced AstarPath.BlockUntilPathQueueBlocked with the more robust AstarPath.PausePathfinding method. + - The default radius, height and center for RVOControllers is now 0.5, 2 and 1 respectively. + - To reduce confusion. The second area color is now a greenish color instead of a red one. + The red color would often be mistaken as indicating unwalkable nodes instead of simply a different connected component. + Hopefully green will be a more neutral color. + - Renamed AstarPath.astarData to AstarPath.data. + - Renamed NavmeshCut.useRotation and NavmeshAdd.useRotation to useRotationAndScale (since they have always affected scale too). + - Renamed GridGraph.GenerateMatrix to GridGraph.UpdateTransform to be consistent with recast/navmesh graphs. + The GenerateMatrix method is now deprecated. + - Renamed AstarPath.WaitForPath to AstarPath.BlockUntilCalculated. + - Renamed GridNode.GetConnectionInternal to HasConnectionInDirection. + - Renamed NNInfo.clampedPosition to NNInfo.position. + - Renamed GridGraph.GetNodesInArea to GetNodesInRegion to avoid confusing the word 'area' for what is used to indicate different connected components in graphs. + - Renamed AIPath.turningSpeed to \link Pathfinding.AIPath.rotationSpeed rotationSpeed\endlink. + - Deprecated Seeker.GetNewPath. + - Deprecated NavGraph.matrix, NavGraph.inverseMatrix, NavGraph.SetMatrix and NavGraph.RelocateNodes(Matrix4x4,Matrix4x4). + They have been replaced with a single transform field only available on some graph types as well as a few other overloads of teh RelocateNodes method. + - Changed the signature of NavGraph.GetNodes(GraphNodeDelegateCancelable) to the equivalent NavGraph.GetNodes(System.Func). + - Replaced all instances of GraphNodeDelegate with the equivalent type System.Action. + - Made a large number of previously public methods internal to reduce confusion about which methods one should use in a class and make the documentation easier to read. + In particular the Path class has had its set of public methods reduced a lot. + - Made AstarData.AddGraph(NavGraph) private. Scripts should use AstarData.AddGraph(System.Type) instead. + - Moved internal fields of NNInfo into a new NNInfoInternal struct to make the API easier to use. Previously NNInfo contained some internal fields, but now they are only in NNInfoInternal. + - Moved GetNeighbourAlongDirection to GridNodeBase and made it public. + - An overload of the GridGraph.CalculateConnections method has been made non-static. + - LayerGridGraph.LinkedLevelNode and LayerGridGraph.LinkedLevelCell are now private classes since they are only used by the LayerGridGraph. + - MonoModifier.OnDestroy is now a virtual function. + - AstarPath.IsUsingMultithreading and NumbParallelThreads have been made non-static. + - AstarPath.inGameDebugPath is now private. + - AstarPath.lastScanTime is now read only. + - Removed the 'climb axis' field from grid graphs. The axis is now automatically set to the graph's UP direction (which is + the only direction that makes sense and all other directions can be transformed to this one anyway). + - Removed the 'worldSpace' parameter from RecastGraph.ReplaceTile, it is no longer possible to supply world space vertices to + that method since graph space vertices are required for some things. + - Removed BBTree.QueryCircle and BBTree.Query since they were not used anywhere. + - Removed the Path.searchIterations field because it wasn't very useful even as debug information. + - Removed the Path.maxFrameTime field because it was not used. + - Removed the Path.callTime property because it was not used. + - Removed the ABPath.startHint, ABPath.endHint fields because they were not used. + - Removed the ABPath.recalcStartEndCosts field because it was not used. + - Removed the RecursiveBinary and RecursiveTrinary modes for RichAI.funnelSimplification because the Iterative mode + was usually the best and fastest anyway (also the other two modes had a rare bug where they could get cought in infinite loops). + - Removed the Polygon.Subdivide method because it was not used anywhere. + - Removed the NavGraph.Awake method because it was not used for anything. + - Removed ASTAR_OPTIMIZE_POOLING from Optimization tab. It is now always enabled in standalone builds and always disabled in the Unity editor. + - Removed various unused Recast code. + - Removed support for forcing the inspector skin to be dark or light. The value provided by EditorGUIUtility.isProSkin is always used now. + - Removed multiplication operator for Int3 with a Vector3 because it is a nonstandard operation on vectors (and it is not that useful). + - Removed the since long deprecated example script AIFollow. + - Removed the AdaptiveSampling algorithm for local avoidance. Only GradientDescent is used now. + - Removed empty PostProcess method in NavMeshGraph. +- Fixes + - Fixed RichAI and AIPath trying to use CharacterControllers even if the CharacterController component was disabled. + - Fixed rotated recast/navmesh graphs would ensure each node's vertices were laid out clockwise in XZ space instead of in graph space which could cause parts of the graph to become disconnected from the rest. + - Fixed a bug where graphs could fail to be deserialized correctly if the graph list contained a null element + - Fixed a bug where the json serializer could emit True/False instead of true/false which is the proper json formatting. + - Fixed LayerGridGraphs' "max climb" setting not working properly with rotated graphs. + - Fixed LayerGridGraphs' "character height" setting not working properly with rotated graphs. + - Fixed LayerGridGraphs assuming there were no obstacles nearby if no ground was found. + - Fixed DynamicGridObstacle getting caught in an infinite loop if there was no AstarPath component in the scene when it was created. Thanks MeiChen for finding the bug. + - Fixed NodeLink2 deserialization causing exceptions if the node hadn't linked to anything when it was serialized. Thanks Skalev for finding the bug. + - Fixed the AlternativePath modifier could crash the pathfinding threads if it logged a warning since it used the Debug.Log(message,object) overload which + can only be used from the Unity thread. + - Fixed an issue where layer mask fields in graph editors would show 'Nothing' if they only included layers which had no name set. + - Fixed potential memory leak. + Paths in the path pool would still store the callback which is called when the path has been calculated + which that means it would implicitly hold a reference to the object which had the method that would be called. + Thanks sehee for pointing this out. + - Fixed GridNode.ClosestPointOnNode could sometimes return the wrong y coordinate relative to the graph (in particular when the graph was rotated) and the y coordinate would not snap to the node's surface. + - Fixed AstarData.AddGraph would fill *all* empty slots in the graph array with the graph instead of just the first. Thanks bitwise for finding the bug. + - Improved compatibility with Unity 5.5 which was need due to the newly introduced UnityEngine.Profiling namespace. + - Fixed graph updates on LayeredGridGraphs not respecting GraphUpdateObject.resetPenaltyOnPhysics. + - Fixed potential memory leak when calling RecalculateCell on a layered grid graph. + - LevelGridNode.ContainsConnection now reports correct values (previously it would only check + non-grid connections). + - Fixed not being able to deserialize settings saved with some old versions of the A* Pathfinding Project. + - Tweaked ListPool to avoid returning lists with a very large capacity when a small one was requested + as this could cause performance problems since Clear is O(n) where n is the capacity (not the size of the list). + - Fixed GraphUpdateScene causing 'The Grid Graph is not scanned, cannot update area' to be logged when exiting play mode. + - Fixed scanning a recast graph could in very rare circumstances throw a 'You are trying to pool a list twice' exception due to a multithreading + race condition. + - Fixed recast/navmesh graphs could return the wrong node as the closest one in rare cases, especially near tile borders. + - Fixed another case of recast/navmesh graphs in rare cases returning the wrong node as the closest one. + - Fixed gizmo drawing with 'Show Search Tree' enabled sometimes right after graph updates drawing nodes outside the + search tree as if they were included in it due to leftover data from graph updates. + - Fixed navmesh and recast graphs would unnecessarily be serialized by Unity which would slow down the inspector slightly. + - Fixed AstarEnumFlagDrawer not working with private fields that used the [SerializeField] attribute. + This does not impact anything that the A* Pathfinding Project used, but some users are using the AstarEnumFlagDrawer for + other fields in their projects. Thanks Skalev for the patch. + - Clicking 'Apply' in the Optimizations tab will now always refresh the UI instead of assuming that + a recompilation will happen (it will not happen if only defines for other platforms than the current one were modified). + - Fixed not being able to multi-edit RVOSquareObstacle components. + - Fixed GridNode.ClearConnections(true) not removing all reversed connections and could sometimes remove the wrong ones. + - Fixed TileHandlerHelper regularly checking for if an update needs to be done even if TileHandlerHelper.updateInterval was negative + even though the documentation specifies that it should not do that (it only disabled updates when updateInterval = -1). + - Fixed PathUtilities.GetPointsAroundPointWorld and PathUtilities.GetPointsAroundPoint returning incorrect results sometimes. + - Fixed Path.immediateCallback not being reset to null when using path pooling. + - TileHandlerHelper will now work even if Scan On Awake in A* Inspector -> Settings is false and you are scanning the graph later. + - Fixed AstarWorkItem.init could be called multiple times. + - Fixed some documentation typos. + - Fixed colliders being included twice in the recast rasterization if the GameObject had a RecastMeshObj attached to it which effectively made RecastMeshObj not work well at all with colliders. + - Fixed inspector for RecastMeshObj not updating if changes were done to the fields by a script or when an undo or redo was done. + - Fixed SimpleSmoothModifier custom editor would sometimes set all instances of a field to the same value + when editing multiple objects at the same time. + - Fixed division by zero when the TimeScale was zero in the AstarDebugger class. Thanks Booil Jung for reporting the issue. + - Various other small fixes in the AstarDebugger class. + - Fixed division by zero when generating a recast graph and the cell size was much larger than the bounds of the graph. + - Fixed the recast graph data structures could be invalid while a graph update was running in a separate thread. + This could cause API calls like AstarPath.GetNearest to throw exceptions. Now the affected tiles are recalculated + in a separate thread and then the updates are applied to the existing graph in the Unity thread. + - Fixed some cases where the AlternativePath modifier would apply penalties incorrectly and possibly crash the pathfinding thread. + - Fixed IAgent.NeighbourCount would sometimes not be reset to 0 when the agent was locked and thus takes into account no other agents. + - Fixed RVO threads would sometimes not be terminated which could lead to memory leaks if switching scenes a lot. + - Fixed GridGraph.GetNearest and NavGraph.GetNearest not handling constraint=null. +- Internal changes + - These are changes to the internals of the system and will most likely not have any significant externally visible effects. + - Removed some wrapper methods for the heap in the PathHandler class since they were just unnecessary. Exposed the heap field as readonly instead. + - Renamed BinaryHeapM to BinaryHeap. + - Renamed ExtraMesh to RasterizationMesh. + - Refactored TileHandler.CutPoly to reduce code messiness and also fixed some edge case bugs. +- Documentation + - Among other things: improved the \ref writing-graph-generators guide (among other things it no longer uses hard to understand calculations to get the index of each node). + +## 3.8.8.1 (2017-01-12) +- Fixes + - Fixed the 'Optimization' tab sometimes logging errors when clicking Apply on Unity 5.4 and higher. + - More UWP fixes (pro version only). + +## 3.8.8 (2017-01-11) +- Fixes + - Fixed errors when deploying for the Universal Windows Platform (UWP). + This includes the Hololens platform. + - It is no longer necessary to use the compiler directive ASTAR_NO_ZIP when deploying for UWP. + zipping will be handled by the System.IO.Compression.ZipArchive class on those platforms (ZipArchive is not available on other platforms). + If you have previously enabled ASTAR_NO_ZIP it will stay enabled to ensure compatibility. + - Changed some comments from the ' + +## 3.8.7 (2016-11-26) +- Fixes + - Improved compatibility with Unity 5.5 which was needed due to the newly introduced UnityEngine.Profiling namespace. + +## 3.8.6 (2016-10-31) +- Upgrade Notes + - Note that a few features and some fixes that have been available in the beta releases are not + included in this version because they were either not ready to be released or depended on other + changes that were not ready. + - Dropped support for Unity 5.1. + - Moved some things to inside the Pathfinding namespace to avoid naming collisions with other packages. + Make sure you have the line 'using Pathfinding;' at the top of your scripts. + - Seeker.StartMultiTargetPath will now also set the enabledTags and tagPenalties fields on the path. + Similar to what StartPath has done. This has been the intended behaviour from the start, but bugs happen. + See http://forum.arongranberg.com/t/multitargetpath-doesnt-support-tag-constraints/2561/3 + - The JsonFx library is no longer used, so the Pathfinding.JsonFx.dll file in the plugins folder + may be removed to reduce the build size a bit. UnityPackages cannot delete files, so you have to delete it manually. + - RecastGraph.UpdateArea (along with a few other functions) is now explicitly implemented for the IUpdatableGraph interface + as it is usually a bad idea to try to call those methods directly (use AstarPath.UpdateGraphs instead). + - AstarPath.FlushWorkItems previously had pretty bad default values for the optional parameters. + By default it would not necessarily complete all work items, it would just complete those that + took a single frame. This is pretty much never what you actually want so to avoid + confusion the default value has been changed. +- New Features and Improvements + - The JsonFx library is no longer used. Instead a very tiny json serializer and deserializer has been written. + In addition to reducing code size and being slightly faster, it also means that users using Windows Phone + no longer have to use the ASTAR_NO_JSON compiler directive. I do not have access to a windows phone + however, so I have not tested to build it for that platform. If any issues arise I would appreciate if + you post them in the forum. + - Improved inspector for NavmeshCut. + - NodeLink2 can now be used even when using cached startup or when loading serialized data in other ways just as long as the NodeLink2 components are still in the scene. + - LevelGridNode now has support for custom non-grid connections (just like GridNode has). + - Added GridNode.XCoordinateInGrid and GridNode.ZCoordinateInGrid. + - Improved documentation for GraphUpdateShape a bit. +- Changes + - Removed EditorUtilities.GetMd5Hash since it was not used anywhere. + - Deprecated TileHandler.GetTileType and TileHandler.GetTileTypeCount. + - Seeker.StartPath now properly handles MultiTargetPath objects as well. + - Seeker.StartMultiTargetPath is now deprecated. Note that it will now also set the + enabledTags and tagPenalties fields on the path. Similar to what StartPath has done. + - Removed GridGraph.bounds since it was not used or set anywhere. + - GraphNode.AddConnection will now throw an ArgumentNullException if you try to call it with a null target node. + - Made PointGraph.AddChildren and PointGraph.CountChildren protected since it makes no sense for them to be called by other scripts. + - Changed how the 'Save & Load' tab looks to make it easier to use. + - Renamed 'Path Debug Mode' to 'Graph Coloring' and 'Path Log Mode' to 'Path Logging' in the inspector. + - RecastGraph.UpdateArea (along with a few other functions) is now explicitly implemented for the IUpdatableGraph interface + as it is usually a bad idea to try to call those methods directly (use AstarPath.UpdateGraphs instead). + - Removed ConnectionType enum since it was not used anywhere. + - Removed NodeDelegate and GetNextTargetDelegate since they were not used anywhere. +- Fixes + - Fixed TinyJson not using culture invariant float parsing and printing. + This could cause deserialization errors on systems that formatted floats differently. + - Fixed the EndingCondition example script. + - Fixed speed being multiplied by Time.deltaTime in the AI script in the get started tutorial when it shouldn't have been. + - Fixed FunnelModifier could for some very short paths return a straight line even though a corner should have been inserted. + - Fixed typo. 'Descent' (as in 'Gradient Descent') was spelled as 'Decent' in some cases. Thanks Brad Grimm for finding the typo. + - Fixed some documentation typos. + - Fixed some edge cases in RandomPath and FleePath where a node outside the valid range of G scores could be picked in some cases (when it was not necessary to do so). + - Fixed editor scripts in some cases changing the editor gui styles instead of copying them which could result in headers in unrelated places in the Unity UI had the wrong sizes. Thanks HP for reporting the bug. + - Fixed NavmeshCut causing errors when cutting the navmesh if it was rotated upside down or scaled with a negative scale. + - Fixed TriangleMeshNode.ClosestPointOnNodeXZ could sometimes return the wrong point (still on the node surface however). + This could lead to characters (esp. when using the RichAI component) teleporting in rare cases. Thanks LordCecil for reporting the bug. + - Fixed GridNodes not serializing custom connections. + - Fixed nodes could potentially get incorrect graph indices assigned when additive loading was used. + - Added proper error message when trying to call RecastGraph.ReplaceTile with a vertex count higher than the upper limit. +- Known Bugs + - Calling GetNearest when a recast graph is currently being updated on another thread may in some cases result in a null reference exception + being thrown. This does not impact navmesh cutting. This bug has been present (but not discovered) in previous releases as well. + - Calling GetNearest on point graphs with 'optimizeForSparseGraph' enabled may in some edge cases return the wrong node as being the closest one. + It will not be widely off target though and the issue is pretty rare, so for real world use cases it should be fine. + This bug has been present (but not discovered) in previous releases as well. + +## 3.8.3 through 3.8.5 were beta versions + +## 3.8.2 (2016-02-29) +- Improvements + - DynamicGridObstacle now handles rotation and scaling better. + - Reduced allocations due to coroutines in DynamicGridObstacle. +- Fixes + - Fixed AstarPath.limitGraphUpdates not working properly most of the time. + In order to keep the most common behaviour after the upgrade, the value of this field will be reset to false when upgrading. + - Fixed DynamicGridObstacle not setting the correct bounds at start, so the first move of an object with the DynamicGridObstacle + component could leave some nodes unwalkable even though they should not be. Thanks Dima for reporting the bug. + - Fixed DynamicGridObstacle stopping to work after the GameObject it is attached to is deactivated and then activated again. + - Fixed RVOController not working after reloading the scene due to the C# '??' operator not being equivalent to checking + for '== null' (it doesn't use Unity's special comparison check). Thanks Khan-amil for reporting the bug. + - Fixed typo in documentation for ProceduralGridMover.floodFill. +- Changes + - Renamed 'Max Update Frequency' to 'Max Update Interval' in the editor since it has the unit [second], not [1/second]. + - Renamed AstarPath.limitGraphUpdates to AstarPath.batchGraphUpdates and AstarPath.maxGraphUpdateFreq to AstarPath.graphUpdateBatchingInterval. + Hopefully these new names are more descriptive. The documentation for the fields has also been improved slightly. + +## 3.8.1 (2016-02-17) +- Improvements + - The tag visualization mode for graphs can now use the custom list of colors + that can be configured in the inspector. + Thanks Arakade for the patch. +- Fixes + - Recast graphs now handle meshes and colliders with negative scales correctly. + Thanks bvance and peted for reporting it. + - Fixed GridGraphEditor throwing exceptions when a user had created a custom grid graph class + which inherits from GridGraph. + - Fixed Seeker.postProcessPath not being called properly. + Instead it would throw an exception if the postProcessPath delegate was set to a non-null value. + Thanks CodeSpeaker for finding the bug. + +## 3.8 (2016-02-16) +- The last version released on the Unity Asset Store was 3.7, so if you are upgrading + from that version check out the release notes for 3.7.1 through 3.7.5 as well. +- Breaking Changes + - For the few users that have written their own Path Modifiers. The 'source' parameter to the Apply method has been removed from the IPathModifier interface. + You will need to remove that parameter from your modifiers as well. + - Modifier priorities have been removed and the priorities are now set to sensible hard coded values since at least for the + included modifiers there really is only one ordering that makes sense (hopefully there is no use case I have forgotten). + This may affect your paths if you have used some other modifier order. + Hopefully this change will reduce confusion for new users. +- New Features and Improvements + - Added NodeConnection mode to the StartEndModifier on the Seeker component. + This mode will snap the start/end point to a point on the connections of the start/end node. + Similar to the Interpolate mode, but more often does what you actually want. + - SimpleSmoothModifier now has support for multi editing. + - Added a new movement script called AILerp which uses linear interpolation to follow the path. + This is good for games which want the agent to follow the path exactly and not use any + physics like behaviour. This movement script works in both 2D and 3D. + - Added a new 2D example scene which uses the new AILerp movement script. + - All scripts now have a HelpURLAttribute + so the documentation button at the top left corner of every script inspector now links directly to the documentation. + - Recast graphs can now draw the surface of a navmesh in the scene view instead of only + the node outlines. Enable it by checking the 'Show mesh surface' toggle in the inspector. + Drawing the surface instead of the node outlines is usually faster since it does not use + Unity Gizmos which have to rebuild the mesh every frame. + - Improved GUI for the tag mask field on the Seeker. + - All code is now consistently formatted, utilising the excellent Uncrustify tool. + - Added animated gifs to the \link Pathfinding.RecastGraph.cellSize Recast graph \endlink documentation showing how some parameters change the resulting navmesh. + If users like this, I will probably follow up and add similar gifs for variables in other classes. + \shadowimage{recast/character_radius.gif} +- Fixes + - Fixed objects in recast graphs being rasterized with an 0.5 voxel offset. + Note that this will change how your navmesh is rasterized (but usually for the better), so you may want to make sure it still looks good. + - Fixed graph updates to navmesh and recast graphs not checking against the y coordinate of the bounding box properly (introduced in 3.7.5). + - Fixed potential bug when loading graphs from a file and one or more of the graphs were null. + - Fixed invalid data being saved when calling AstarSerializer.SerializeGraphs with an array that was not equal to the AstarData.graphs array. + The AstarSerializer is mostly used internally (and internally it is always called with the AstarData.graphs array). Thanks munkman for reporting this. + - Fixed incorrect documentation for GridNode.NodeInGridIndex. Thanks mfjk for reporting it! + - Fixed typo in a recast graph log message (where -> were). Thanks bigdaddio for reporting it! + - Fixed not making sure the file is writable before writing graph cache files (Perforce could sometimes make it read-only). Thanks Jørgen Tjernø for the patch. + - Fixed RVOController always using FindObjectOfType during Awake, causing performance issues in large scenes. Thanks Jørgen Tjernø for the patch. + - Removed QuadtreeGraph, AstarParallel, NavMeshRenderer and NavmeshController from the released version. + These were internal dev files but due to typos they had been included in the release. + It will also automatically refresh itself if the graph has been rescanned with a different number of tiles. + - Fixed SimpleSmoothModifier not always including the exact start point of the path. + - Fixed ASTAR_GRID_NO_CUSTOM_CONNECTIONS being stripped out of the final build, so that entry in the Optimizations tab didn't actually do anything. + - Fixed performance issue with path pooling. If many paths were being calculated and pooled, the performance could be + severely reduced unless ASTAR_OPTIMIZE_POOLING was enabled (which it was not by default). + - Fixed 3 compiler warnings about using some deprecated Unity methods. +- Changes + - Recast graphs' 'Snap To Scene' button now snaps to the whole scene instead of the objects that intersect the bounds that are already set. + This has been a widely requested change. Thanks Jørgen Tjernø for the patch. + - Moved various AstarMath functions to the new class VectorMath and renamed some of them to reduce confusion. + - Removed various AstarMath functions because they were either not used or they already exist in e.g Mathf or System.Math. + DistancePointSegment2, ComputeVertexHash, Hermite, MapToRange, FormatBytes, + MagnitudeXZ, Repeat, Abs, Min, Max, Sign, Clamp, Clamp01, Lerp, RoundToInt. + - PathEndingCondition (used with XPath) is now abstract since it doesn't really make any sense to use the default implementation (always returns true). + - A 'Recyle' method is no longer required on path classes (reduced boilerplate). + - Removed old IFunnelGraph interface since it was not used by anything. + - Removed old ConvexMeshNode class since it was not used by anything. + - Removed old script NavmeshController since it has been disabled since a few versions. + - Removed Int3.DivBy2, Int3.unsafeSqrMagnitude and Int3.NormalizeTo since they were not used anywere. + - Removed Int2.sqrMagnitude, Int2.Dot since they were not used anywhere and are prone to overflow (use sqrMagnitudeLong/DotLong instead) + - Deprecated Int2.Rotate since it was not used anywhere. + - Deprecated Int3.worldMagnitude since it was not used anywhere. + +## 3.7.5 (2015-10-05) +- Breaking changes + - Graph updates to navmesh and recast graphs now also check that the nodes are contained in the supplied bounding box on the Y axis. + If the bounds you have been using were very short along the Y axis, you may have to change them so that they cover the nodes they should update. +- Improvements + - Added GridNode.ClosestPointOnNode. + - Optimized GridGraph.CalculateConnections by approximately 20%. + This means slightly faster scans and graph updates. +- Changes + - Graph updates to navmesh and recast graphs now also check that the nodes are contained in the supplied bounding box on the Y axis. + If the bounds you have been using were very short along the Y axis, you may have to change them so that they cover the nodes they should update. +- Fixes + - Fixed stack overflow exception when a pivot root with no children was assigned in the heuristic optimization settings. + - Fixed scanning in the editor could sometimes throw exceptions on new versions of Unity. + Exceptions contained the message "Trying to initialize a node when it is not safe to initialize any node". + This happened because Unity changed the EditorGUIUtility.DisplayProgressBar function to also call + OnSceneGUI and OnDrawGizmos and that interfered with the scanning. + - Fixed paths could be returned with invalid nodes if the path was calculated right + before a call to AstarPath.Scan() was done. This could result in + the funnel modifier becoming really confused and returning a straight line to the + target instead of avoiding obstacles. + - Fixed sometimes not being able to use the Optimizations tab on newer versions of Unity. + +## 3.7.4 (2015-09-13) +- Changes + - AIPath now uses the cached transform field in all cases for slightly better performance. +- Fixes + - Fixed recast/navmesh graphs could in rare cases think that a point on the navmesh was + in fact not on the navmesh which could cause odd paths and agents teleporting short distances. +- Documentation Fixes + - Fixed the Seeker class not appearing in the documentation due to a bug in Doxygen (documentation generator). + +## 3.7.3 (2015-08-18) +- Fixed GridGraph->Unwalkable When No Ground used the negated value (true meant false and false meant true). + This bug was introduced in 3.7 when some code was refactored. Thanks DrowningMonkeys for reporting it. + +## 3.7.2 (2015-08-06) +- Fixed penalties not working on navmesh based graphs (navmesh graphs and recast graphs) due to incorrectly configured compiler directives. +- Removed undocumented compiler directive ASTAR_CONSTANT_PENALTY and replaced with ASTAR_NO_TRAVERSAL_COST which + can strip out code handling penalties to get slightly better pathfinding performance (still not documented though as it is not really a big performance boost). + +## 3.7.1 (2015-08-01) +- Removed a few cases where exceptions where needed to better support WebGL when exception handling is disabled. +- Fixed MultiTargetPath could return the wrong path if the target of the path was the same as the start point. +- Fixed MultiTargetPath could sometimes throw exceptions when using more than one pathfinding thread. +- MultiTargetPath will now set path and vectorPath to the shortest path even if pathsForAll is true. +- The log output for MultiTargetPath now contains the length (in nodes) of the shortest path. +- Fixed RecastGraph throwing exceptions when trying to rasterize trees with missing (null) prefabs. Now they will simply be ignored. +- Removed RecastGraph.bbTree since it was not used for anything (bbTrees are stored inside each tile since a few versions) +- Improved performance of loading and updating large recast graph tiles (improved performance of internal AABB tree). +- Removed support for the compiler directive ASTAR_OLD_BBTREE. + +## 3.7 (2015-07-22) +- The last version that was released on the Unity Asset Store + was version 3.6 so if you are upgrading from that version also check out the release + notes for 3.6.1 through 3.6.7. +- Upgrade notes + - ProceduralGridMover.updateDistance is now in nodes instead of world units since this value + is a lot less world scale dependant. So the defaults should fit more cases. + You may have to adjust it slightly. + - Some old parts of the API that has been marked as deprecated long ago have been removed (see below). + Some other unused parts of the API that mostly lead to confusion have been removed as well. +- Improvements + - Rewrote several documentation pages to try to explain concepts better and fixed some old code. + - \ref accessing-data + - \ref graph-updates + - \ref writing-graph-generators + - Pathfinding.NavmeshCut + - And some other smaller changes. + - Added an overload of Pathfinding.PathUtilities.IsPathPossible which takes a tag mask. + - \link Pathfinding.XPath XPath \endlink now works again. + - The ProceduralGridMover component now supports rotated graphs (and all other ways you can transform it, e.g isometric angle and aspect ratio). + - Rewrote GridGraph.Linecast to be more accurate and more performant. + Previously it used a sampling approach which could cut corners of obstacles slightly and was pretty inefficient. + - Linted lots of files to remove trailing whitespace, fix imports, use 'var' when relevant and various other small tweaks. + - Added AstarData.layerGridGraph shortcut. +- Fixes + - Fixed compilation errors for Windows Store. + The errors mentioned ThreadPriority and VolatileRead. + - Fixed LayerGridGraph.GetNearest sometimes returning the wrong node inside a cell (e.g sometimes it would always return the node with the highest y coordinate).\n + This did not happen when the node size was close to 1 and the grid was positioned close to the origin. + Which it of course was in all my tests (tests are improved now). + - Fixed GridGraph.Linecast always returning false (no obstacles) when the start point and end point was the same. + Now it returns true (obstacle) if the start point was inside an obstacle which makes more sense. + - Linecasts on layered grid graphs now use the same implementation as the normal grid graph.\n + This fixed a TON of bugs. If you relied on the old (buggy) behaviour you might have to change your algorithms a bit. + It will now report more accurate hit information as well. + - Fixed documentation on LayerGridGraph.Linecast saying that it would return false if there was an obstacle in the way + when in fact exactly the opposite was true. + - Fixed inspector GUI throwing exceptions when two or more grid graphs or layered grid graphs were visible and thickRaycast was enabled on only one of them. + - Fixed a few options only relevant for grid graphs were visible in the layered grid graph inspector as well. + - Fixed GridGraph.CheckConnection returned the wrong result when neighbours was Four and dir was less than 4. + - All compiler directives in the Optimizations tab are now tested during the package build phase. So hopefully none of them should give compiler errors now. + - Improved accuracy of intellisense by changing the start of some documentation comments to + but apparently not so well by MonoDevelop and VS. + - Fixed the editor sometimes incorrectly comparing versions which could cause the 'New Update' window to appear even though no new version was available. +- Changes + - Removed code only necessary for compatibility with Unity 4.5 and lower. + - Removed a lot of internal unused old code. + - Renamed GridGraph.GetNodePosition to GridGraph.GraphPointToWorld to avoid confusion. + - Renamed 3rd party plugin license files to prevent the Unity Asset Store + from detecting those as the license for the whole package. + - Changed Seeker.traversableTags to be a simple int instead of a class. + - GridNode and LevelGridNode now inherit from a shared base class called GridNodeBase. + - Removed support for the compiler directive ConfigureTagsAsMultiple since it was not supported by the whole codebase + and it was pretty old. + - Marked a few methods in AstarData as deprecated since they used strings instead of types. + If string to type conversion is needed it should be done elsewhere. + - Removed some methods which have been marked as obsolete for a very long time. + - AstarData.GetNode + - PathModifier and MonoModifier.ApplyOriginal + - Some old variants of PathModifier.Apply + - GridGeneratorEditor.ResourcesField + - Int3.safeMagnitude and safeSqrMagnitude + - GraphUpdateUtilities.IsPathPossible (this has been since long been moved to the PathUtilities class) + - All constructors on path classes. The static Construct method should be used instead since that can handle path pooling. + - GraphNode.Position, walkable, tags, graphIndex. These had small changes made to their names (if they use upper- or lowercase letters) a long time ago. + (for better or for worse, but I want to avoid changing the names now again to avoid breaking peoples' code) + - GridNode.GetIndex. + - Removed the Node class which has been marked as obsolete a very long time. This class has been renamed to GraphNode to avoid name conflicts. + - Removed LocalAvoidanceMover which has been marked as obsolete a very long time. The RVO system has replaced it. + - Removed Seeker.ModifierPass.PostProcessOriginal since it was not used. This also caused Seeker.postProcessOriginalPath to be removed. + - Removed support for ASTAR_MORE_PATH_IDS because it wasn't really useful, it only increased the memory usage. + - Removed Path.height, radius, turnRadius, walkabilityMask and speed since they were dummy variables that have not been used and are + better implemented using inheritance anyway. This is also done to reduce confusion for users. + - Removed the old local avoidance system which has long since been marked as obsolete and replaced by the RVO based system. + +## 3.6.7 (2015-06-08) +- Fixes + - Fixed a race condition when OnPathPreSearch and OnPathPostSearch were called. + When the AlternativePath modifier was used, this could cause the pathfinding threads to crash with a null reference exception. + +## 3.6.6 (2015-05-27) +- Improvements + - Point Graphs are now supported when using ASTAR_NO_JSON. + - The Optimizations tab now modifies the player settings instead of changing the source files. + This is more stable and your settings are now preserved even when you upgrade the system. + - The Optimizations tab now works regardless of the directory you have installed the package in. + Hopefully the whole project is now directory agnostic, but you never know. +- Changes + - Switched out OnVoidDelegate for System.Action. + You might get a compiler error because of this (for the few that use it) + but then just rename your delegate to System.Action. +- Fixes + - Fixed recast graphs not saving all fields when using ASTAR_NO_JSON. + +## 3.6.5 (2015-05-19) +- Fixes + - Fixed recast graphs generating odd navmeshes on non-square terrains. + - Fixed serialization sometimes failing with the error 'Argument cannot be null' when ASTAR_NO_JSON was enabled. + - The 'Walkable Climb' setting on recast graphs is now clamped to be at most equal to 'Walkable Height' because + otherwise the navmesh generation can fail in some rare cases. +- Changes + - Recast graphs now show unwalkable nodes with a red outline instead of their normal colors. + +## 3.6.4 (2015-04-19) +- Fixes + - Improved compatibility with WIIU and other big-endian platforms. + +## 3.6.3 (2015-04-19) +- Fixes + - Fixed RVONavmesh not adding obstacles correctly (they were added added, but all agents ignored them). + +## 3.6.2 (2015-04-14) +- Fixes + - Fixed null reference exception in the PointGraph OnDrawGizmos method. + - Fixed a few example scene errors in Unity 5. + +## 3.6.1 (2015-04-06) +- Upgrade notes: + - The behaviour of NavGraph.RelocateNodes has changed. + The oldMatrix was previously treated as the newMatrix and vice versa so you might + need to switch the order of your parameters if you are calling it. +- Highlights: + - Works in WebGL/IL2CPP (Unity 5.0.0p3). + At least according to my limited tests. + - Implemented RelocateNodes for recast graphs (however it cannot be used on tiled recast graphs). + - Added support for hexagon graphs. + Enable it by changing the 'Connections' field on a grid graph to 'Six'. + - Fixed AstarData.DeserializeGraphsAdditive (thanks tmcsweeney). + - Fixed pathfinding threads sometimes not terminating correctly. + This would show up as a 'Could not terminate pathfinding thread...' error message. + - Added a version of GridGraph.RelocateNodes which takes grid settings instead of a matrix for ease of use. +- Changes: + - Removed NavGraph.SafeOnDestroy + - Removed GridGraph.scans because it is a pretty useless variable. + - Removed NavGraph.CreateNodes (and overriden methods) since they were not used. + - Made GridGraph.RemoveGridGraphFromStatic private. + - Removed NavMeshGraph.DeserializeMeshNodes since it was not used. + - Made Seeker.lastCompletedVectorPath, lastCompletedNodePath, OnPathComplete, OnMultiPathComplete, OnPartialPathComplete + private since they really shouldn't be used by other scripts. + - Removed Seeker.saveGetNearestHints, Seeker.startHint, Seeker.endHint, Seeker.DelayPathStart since they were not used. + - Removed unused methods of little use: AstarData.GuidToIndex and AstarData.GuidToGraph. + - Removed RecastGraph.vertices and RecastGraph.vectorVertices since they were obsolete and not used. + - Removed some old Unity 4.3 and Unity 3 compatibility code. + - Recast graphs' 'Snap to scene' button now takes into account the layer mask and the tag mask when snapping, it now also checks terrains and colliders instead of just meshes (thanks Kieran). +- Fixes: + - Fixed RecastGraph bounds gizmos could sometimes be drawn with the wrong color. + - Fixed a rare data race which would cause an exception with the message + 'Trying to initialize a node when it is not safe to initialize any nodes' to be thrown + - Tweaked Undo behaviour, should be more stable now. + - Fixed grid graph editor changing the center field very little every frame (floating point errors) + causing an excessive amount of undo items to be created. + - Reduced unecessary dirtying of the scene (thanks Ben Hymers). + - Fixed RVOCoreSimulator.WallThickness (thanks tmcsweeney). + - Fixed recast graph not properly checking for the case where an object had a MeshFilter but no Renderer (thanks 3rinJax). + - Fixed disabling ASTAR_RECAST_ARRAY_BASED_LINKED_LIST (now ASTAR_RECAST_CLASS_BASED_LINKED_LIST) would cause compiler errors. + - Fixed recast graphs could sometimes voxelize the world incorrectly and the resulting navmesh would have artifacts. + - Fixed graphMask code having been removed from the free version in some cases + due to old code which treated it as a pro only feature. + - Improved compatibility with Xbox One. + - Fixed RVOController layer field not working when multiple agents were selected. + - Fixed grid nodes not being able to have custom connections in the free version. + - Fixed runtime error on PS4. + +## 3.6 (2015-02-02) +- Upgrade notes: + - Cache data for faster startup is now stored in a separate file.\n + This reduces the huge lag some users have been experiencing since Unity changed their Undo system.\n + You will need to open the AstarPath components which used cached startup, go to the save and load tab + and press a button labeled "Transfer cache data to a separate file". +- Highlights: + - Added support for the Jump Point Search algorithm on grid graphs (pro only).\n + The JPS algorithm can be used to speed up pathfinding on grid graphs *without any penalties or tag weights applied* (it only works on uniformly weighted graphs). + It can be several times faster than normal A*. + It works best on open areas. + - Added support for heuristic optimizations (pro only).\n + This can be applied on any static graph, i.e any graph which does not change. + It requires a rather slow preprocessing step so graph updates will be really slow when using this. + However when the preprocessing is done, it can speed up pathfinding with an order of magnitude. + It works especially well in mazes with lots of options and dead ends.\n + Combined with JPS (mentioned above) I have seen it perform up to 20x better than regular A* with no heuristic optimizations. + - Added PointNode.gameObject which will contain the GameObject each node was created from. + - Added support for RVO obstacles.\n + It is by no means perfect at this point, but at least it works. + - Undo works reasonably well again.\n + It took a lot of time working around weird Unity behaviours. + For example Unity seems to send undo events when dragging items to object fields (why? no idea). + - Dragging meshes to the NavmeshGraph.SourceMesh field works again.\n + See fix about undo above. + - Extended the max number of possible areas (connected components) to 2^17 = 131072 up from 2^10 = 1024.\n + No memory usage increase, just shuffling bits around.\n + Deprecated compiler directive ASTAR_MORE_AREAS + - Extended the max number of graphs in the inspector to 256 up from 4 or 32 depending on settings.\n + No memory usage increase, just shuffling bits around. + I still don't recommend that you actually use this many graphs. + - Added RecastTileUpdate and RecastTileUpdateHandler scripts for easier recast tile updating with good performance. + - When using A* Inspector -> Settings -> Debug -> Path Debug Mode = {G,F,H,Penalties} + you previously had to set the limits for what should be displayed as "red" in the scene view yourself, this is now + optionally automatically calculated. The UI for it has also been improved. +- Improvements: + - Added penaltyAnglePower to Grid Graph -> Extra -> Penalty from Angle.\n + This can be used to increase the penalty even more for large angles than for small angles (more than it already does, that is). + - ASTAR_NO_JSON now works for recast graphs as well. + - Added custom inspector for RecastMeshObj, hopefully it will not be as confusing anymore. +- Changes: + - FleePath now has a default flee strength of 1 to avoid confusion when the FleePath doesn't seem to flee from anything. + - Removed some irrelevant defines from the Optimizations tab. + - IAgent.Position cannot be changed anymore, instead use the Teleport and SetYPosition methods. + - Exposed GraphUpdateObject.changedNodes. + - Deprecated the threadSafe paremeter on RegisterSafeUpdate, it is always treated as true now. + - The default value for AstarPath.minAreaSize is now 0 since the number of areas (connected component) indices has been greatly increased (see highlights). + - Tweaked ProceduralWorld script (used for the "Procedural" example scene) to reduce FPS drops. +- Fixes: + - AstarPath.FlushGraphUpdates will now complete all graph updates instead of just making sure they have started.\n + In addition to avoiding confusion, this fixes a rare null reference exception which could happen when using + the GraphUpdateUtilities.UpdateGraphsNoBlock method. + - Fixed some cases where updating recast graphs could throw exceptions. (message begun with "No Voxelizer object. UpdateAreaInit...") + - Fixed typo in RVOSimulator. desiredSimulatonFPS -> desiredSimulationFPS. + - RVO agents move smoother now (previously their velocity could change widely depending on the fps, the average velocity was correct however) + - Fixed an exception which could, with some graph settings, be thrown when deserializing on iPhone when bytecode stripping was enabled. + - Fixed a NullReferenceException in MultiTargetPath which was thrown if the path debug mode was set to "Heavy". + - Fixed PathUtilies.BFS always returning zero nodes (thanks Ajveach). + - Made reverting GraphUpdateObjects work. The GraphUpdateUtilities.UpdateGraphsNoBlock was also fixed by this change. + - Fixed compile error with monodevelop. + - Fixed a bug which caused scanning to fail if more than one NavmeshGraph existed. + - Fixed the lightweight local avoidance example scene which didn't work previously. + - Fixed SimpleSmoothModifier not exposing Roundness Factor in the editor for the Curved Nonuniform mode. + - Fixed an exception when updating RecastGraphs and using RelevantGraphSurfaces and multithreading. + - Fixed exceptions caused by starting paths from other threads than the Unity thread. + - Fixed an infinite loop/out of memory exception that could occur sometimes when graph updates were being done at the start of the game (I hate multithreading race conditions). + - Fixed the Optimizations tab not working when JS Support was enabled. + - Fixed graph updating not working on navmesh graphs (it was broken before due to a missing line of code). + - Fixed some misspelled words in the documentation. + - Removed some unused and/or redundant variables. + - Fixed a case where graphs added using code might not always be configured correctly (and would throw exceptions when scanning). + - Improved Windows Store compatibility. + - Fixed a typo in the GridGraph which could cause compilation to fail when building for Windows Phone or Windows Store (thanks MariuszP) + - Lots of code cleanups and comments added to various scripts. + - Fixed some cases where MonoDevelop would pick up the wrong documention for fields since it doesn't support all features that Doxygen supports. + - Fixed a bug which caused the points field on GraphUpdateScene to sometimes not be editable. + - Fixed a bug which could cause RVO agents not to move if the fps was low and Interpolation and Double Buffering was used. + - Set the execution order for RVOController and RVOSimulator to make sure that other scripts will + get the latest position in their Update method. + - Fixed a bug which could cause some nearest point on line methods in AstarMath to return NaN. + This could happen when Seeker->Start End Modifier->StartPoint and EndPoint was set to Interpolate. + - Fixed a runtime error on PS Vita. + - Fixed an index out of range exception which could occur when scanning LayeredGridGraphs. + - Fixed an index out of range exception which could occur when drawing gizmos for a LayeredGridGraph. + - Fixed a bug which could cause ProduralGridMover to update the graph every frame regardless + of if the target moved or not (thanks Makak for finding the bug). + - Fixed a number of warnings in Unity 5. + +## 3.5.9.7 (3.6 beta 6, 2015-01-28) +## 3.5.9.6 (3.6 beta 5, 2015-01-28) +## 3.5.9.5 (3.6 beta 4, 2015-01-27) +## 3.5.9.1 (3.6 beta 3, 2014-10-14) +## 3.5.9 (3.6 beta 2, 2014-10-13) +## 3.5.8 (3.6 beta 1) + - See release notes for 3.6 + +## 3.5.2 (2013-09-01) (tiny bugfix and small feature release) +- Added isometric angle option for grid graphs to help with isometric 2D games. +- Fixed a bug with the RVOAgent class which caused the LightweightRVO example scene to not work as intended (no agents were avoiding each other). +- Fixed some documentation typos. +- Fixed some compilations errors some people were having with other compilers than Unity's. + +## 3.5.1 (2014-06-15) +- Added avoidance masks to local avoidance. + Each agent now has a layer and each agent can specify which layers it will avoid. + +## 3.5 (2014-06-12) +- Added back local avoidance!! + The new system uses a sampling based algorithm instead of a geometric one. + The API is almost exactly the same so if you used the previous system this will be a drop in replacement. + As for performance, it is roughly the same, maybe slightly worse in high density situations and slightly better + in less dense situations. It can handle several thousand agents on an i7 processor. + Obstacles are not yet supported, but they will be added in a future update. + +- Binary heap switched out for a 4-ary heap. + This improves pathfinding performances by about 5%. +- Optimized scanning of navmesh graphs (not the recast graphs) + Large meshes should be much faster to scan now. +- Optimized BBTree (nearest node lookup for navmesh/recast graphs, pro version only) + Nearest node queries on navmesh/recast graphs should be slightly faster now. +- Minor updates to the documentation, esp. to the GraphNode class. + +## 3.4.0.7 +- Vuforia test build + +## 3.4.0.6 +- Fixed an issue where serialization could on some machines sometimes cause an exception to get thrown. +- Fixed an issue where the recast graph would not rasterize terrains properly near the edges of it. +- Added PathUtilities.BFS. +- Added PathUtilities.GetPointsAroundPointWorld. + +## 3.4.0.5 +- Added offline documentation (Documentation.zip) +- Misc fixes for namespace conflicts people have been having. This should improve compatibility with other packages. + You might need to delete the AstarPathfindingProject folder and reimport the package for everything to work. + +## 3.4.0.4 +- Removed RVOSimulatorEditor from the free version, it was causing compiler errors. +- Made PointGraph.nodes public. + +## 3.4.0.3 +- Removed Local Avoidance due to licensing issues. + Agents will fall back to not avoiding each other. + I am working to get the local avoidance back as soon as possible. + +## 3.4.0.2 +- Unity Asset Store forced me to increase version number. + +## 3.4.0.1 +- Fixed an ArrayIndexOutOfBounds exception which could be thrown by the ProceduralGridMover script in the Procedural example scene if the target was moved too quickly. +- The project no longer references assets from the Standard Assets folder (the package on the Unity Asset Store did so by mistake before). + +## 3.4 +- Fixed a null reference exception when scanning recast graphs and rasterizing colliders. +- Removed duplicate clipper_library.dll which was causing compiler errors. +- Support for 2D Physics collision testing when using Grid Graphs. +- Better warnings when using odd settings for Grid Graphs. +- Minor cleanups. +- Queued graph updates are no longer being performed when the AstarPath object is destroyed, this just took time. +- Fixed a bug introduced in 3.3.11 which forced grid graphs to be square in Unity versions earlier than 4.3. +- Fixed a null reference in BBTree ( used by RecastGraph). +- Fixed NavmeshGraph not rebuilding BBTree on cached start (causing performance issues on larger graphs). + +- Includes all changes from the beta releases below + +## Beta 3.3.14 ( available for everyone! ) +- All dlls are now in namespaces (e.g Pathfinding.Ionic.Zip instead of just Ionic.Zip ) to avoid conflicts with other packages. +- Most scripts are now in namespaces to avoid conflicts with other packages. +- GridNodes now support custom connections. +- Cleanups, preparing for release. +- Reverted to using an Int3 for GraphNode.position instead of an abstract Position property, the tiny memory gains were not worth it. + +## Beta 3.3.13 ( 4.3 compatible only ) +- Fixed an issue where deleting a NavmeshCut component would not update the underlaying graph. +- Better update checking. + +## Beta 3.3.12 ( 4.3 compatible only ) +- Fixed an infinite loop which could happen when scanning graphs during runtime ( not the first scan ). +- NodeLink component is now working correctly. +- Added options for optimizations to the PointGraph. +- Improved TileHandler and navmesh cutting. +- Fixed rare bug which could mess up navmeshes when using navmesh cutting. + +## Beta 3.3.11 ( 4.3 compatible only ) +- Fixed update checking. A bug has caused update checking not to run unless you had been running a previous version in which the bug did not exist. + I am not sure how long this bug has been here, but potentially for a very long time. +- Added an update notification window which pops up when there is a new version of the A* Pathfinding Project. +- Lots of UI fixes for Unity 4.3 +- Lots of other UI fixes and imprements. +- Fixed gravity for RichAI. +- Fixed Undo for Unity 4.3 +- Added a new example scene showing a procedural environment. + +## Beta 3.3.10 +- Removed RecastGraph.includeOutOfBounds. +- Fixed a few bugs when updating Layered Grid Graphs causing incorrect connections to be created, and valid ones to be left out. +- Fixed a null reference bug when removing RVO agents. +- Fixed memory leaks when deserializing graphs or reloading scenes. + +## Beta 3.3.9 +- Added new tutorial page about recast graphs. +- Recast Graph: Fixed a bug which could cause vertical surfaces to be ignored. +- Removed support for C++ Recast. +- Fixed rare bug which could mess up navmeshes when using navmesh cutting. +- Improved TileHandler and navmesh cutting. +- GraphModifiers now take O(n) (linear) time to destroy at end of game instead of O(n^2) (quadratic). +- RecastGraph now has a toggle for using tiles or not. +- Added RelevantGraphSurface which can be used with RecastGraphs to prune away non-relevant surfaces. +- Removed RecastGraph.accurateNearestNode since it was not used anymore. +- Added RecastGraph.nearestSearchOnlyXZ. +- RecastGraph now has support for removing small areas. +- Added toggle to show or hide connections between nodes on a recast graph. +- PointNode has some graph searching methods overloaded specially. This increases performance and reduces alloacations when searching + point graphs. +- Reduced allocations when searching on RecastGraph. +- Reduced allocations in RichAI and RichPath. Everything is pooled now, so for most requests no allocations will be done. +- Reduced allocations in general by using "yield return null" instead of "yield return 0" +- Fixed teleport for local avoidance agents. Previously moving an agent from one position to another + could cause it to interpolate between those two positions for a brief amount of time instead of staying at the second position. + +## Beta 3.3.8 +- Nicer RichAI gizmo colors. +- Fixed RichAI not using raycast when no path has been calculated. + +## Beta 3.3.7 +- Fixed stack overflow exception in RichPath +- Fixed RichPath could sometimes generate invalid paths +- Added gizmos to RichAI + +## Beta 3.3.6 +- Fixed node positions being off by half a node size. GetNearest node queries on grid graphs would be slightly inexact. +- Fixed grid graph updating could get messed up when using erosion. +- ... among other things, see below + +## Beta 3.3.5 and 3.3.6 +- Highlights + - Rewritten graph nodes. Nodes can now be created more easily (less overhead when creating nodes). + - Graphs may use their custom optimized memory structure for storing nodes. + - Performance improvements for scanning recast graphs. + - Added a whole new AI script. RichAI (and the class RichPath for some things): + This script is intended for navmesh based graphs and has features such as: + - Guarantees that the character stays on the navmesh + - Minor deviations from the path can be fixed without a path recalculation. + - Very exact stop at endpoint (seriously, precision with something like 7 decimals). + No more circling around the target point as with AIPath. + - Does not use path modifiers at all (for good reasons). It has an internal funnel modifier however. + - Simple wall avoidance to avoid too much wall hugging. + - Basic support for off-mesh links (see example scene). + - Improved randomness for RandomPath and FleePath, all nodes considered now have an equal chance of being selected. + - Recast now has support for tiles. This enabled much larger worlds to be rasterized (without OutOfMemory errors) and allows for dynamic graph updates. Still slow, but much faster than + a complete recalculation of the graph. + - Navmesh Cutting can now be done on recast graphs. This is a kind of (relatively) cheap graph updating which punches a hole in the navmesh to make place for obstacles. + So it only supports removing geometry, not adding it (like bridges). This update is comparitively fast, and it makes real time navmesh updating possible. + See video: http://youtu.be/qXi5qhhGNIw. + - Added RecastMeshObj which can be attached to any GameObject to include that object in recast rasterization. It exposes more options and is also + faster for graph updates with logarithmic lookup complexity instead of linear (good for larger worlds when doing graph updating). + - Reintroducing special connection costs for start and end nodes. + Before multithreading was introduced, pathfinding on navmesh graphs could recalculate + the connection costs for the start and end nodes to take into account that the start point is not actually exactly at the start node's position + (triangles are usually quite a larger than the player/npc/whatever). + This didn't work with multithreading however and could mess up pathfinding, so it was removed. + Now it has been reintroduced, working with multithreading! This means more accurate paths + on navmeshes. + - Added several methods to pick random points (e.g for group movement) to Pathfinding.PathUtlitilies. + - Added RadiusModifier. A new modifier which can offset the path based on the character radius. Intended for navmesh graphs + which are not shrinked by the character radius at start but can be used for other purposes as well. + - Improved GraphUpdateScene gizmos. Convex gizmos are now correctly placed. It also shows a bounding box when selected (not showing this has confused a lot of people). + - AIPath has gotten some cleanups. Among other things it now behaves correctly when disabled and then enabled again + making it easy to pool and reuse (should that need arise). + - Funnel modifier on grid graphs will create wider funnels for diagonals which results in nicer paths. + - If an exception is thrown during pathfinding, the program does no longer hang at quit. + - Split Automatic thread count into Automatic High Load and Automatic Low Load. The former one using a higher number of thread. + - Thread count used is now shown in the editor. + - GridGraph now supports ClosestOnNode (StartEndModifier) properly. SnapToNode gives the previous behaviour on GridGraphs (they were identical before). + - New example scene Door2 which uses the NavmeshCut component. +- Fixes + - Fixed spelling error in GridGraph.uniformWidthDepthGrid. + - Erosion radius (character radius, recast graphs) could become half of what it really should be in many cases. + - RecastGraph will not rasterize triggers. + - Fixed recast not being able to handle multiple terrains. + - Fixed recast generating an incorrect mesh for terrains in some cases (not the whole terrain was included). + - Linecast on many graph types had incorrect descriptions saying that the function returns true when the line does not intersect any obstacles, + it is actually the other way around. Descriptions corrected. + - The list of nodes returned by a ConstantPath is now guaranteed to have no duplicates. + - Many recast constants are now proper constants instead of static variables. + - Fixed bug in GridNode.RemoveGridGraph which caused graphs not being cleaned up correctly. Could cause problems later on. + - Fixed an ArgumentOutOfRange exception in ListPool class. + - RelocateNodes on NavMeshGraph now correctly recalculates connection costs and rebuilds the internal query tree (thanks peted on the forums). + - Much better member documentation for RVOController. + - Exposed MaxNeighbours from IAgent to RVOController. + - Fixed AstarData.UpdateShortcuts not being called when caching was enabled. This caused graph shortcuts such as AstarPath.astarData.gridGraph not being set + when loaded from a cache. + - RVOCoreSimulator/RVOSimulator now cleans up the worker threads correctly. + - Tiled recast graphs can now be serialized. +- Changes + - Renamed Modifier class to PathModifier to avoid naming conflicts with user scripts and other packages. + - Cleaned up recast, put inside namespace and split into multiple files. + - ListPool and friends are now threadsafe. + - Removed Polygon.Dot since the Vector3 class already contains such a method. + - The Scan functions now use callbacks for progress info instead of IEnumerators. Graphs can now output progress info as well. + - Added Pathfinding.NavGraph.CountNodes function. + - Removed GraphHitInfo.success field since it was not used. + - GraphUpdateScene will now fall back to collider.bounds or renderer.bounds (depending on what is available) if no points are + defined for the shape. + - AstarPath.StartPath now has an option to put the path in the front of the queue to prioritize its calculation over other paths. + - Time.fixedDeltaTime by Time.deltaTime in AIPath.RotateTowards() to work with both FixedUpdate and Update. (Thanks Pat_AfterMoon) + You might have to configure the turn speed variable after updating since the actual rotation speed might have changed a bit depending on your settings. + - Fixed maxNeighbourDistance not being used correctly by the RVOController script. It would stay at the default value. If you + have had trouble getting local avoidance working on world with a large scale, this could have been the problem. (Thanks to Edgar Sun for providing a reproducible example case) + - Graphs loaded using DeserializeGraphsAdditive will get their graphIndex variables on the nodes set to the correct values. (thanks peted for noticing the bug). + - Fixed a null reference exception in MultiTargetPath (thanks Dave for informing me about the bug). + - GraphUpdateScene.useWorldSpace is now false per default. + - If no log output is disabled and we are not running in the editor, log output will be discarded as early as possible for performance. + Even though in theory log output could be enabled between writing to internal log strings and deciding if log output should be written. + - NavGraph.inverseMatrix is now a field, not a property (for performance). All writes to matrix should be through the SetMatrix method. + - StartEndModifier now uses ClosestOnNode for both startPoint and endPoint by default. +- Known bugs + - Linecasting on graphs is broken at the moment. (working for recast/navmesh graph atm. Except in very special cases) + - RVONavmesh does not work with tiled recast graphs. + + + +## 3.2.5.1 +- Fixes + - Pooling of paths had been accidentally disabled in AIPath. + +## 3.2.5 +- Changes + - Added support for serializing dictionaries with integer keys via a Json Converter. + - If drawGizmos is disabled on the seeker, paths will be recycled instantly. + This will show up so that if you had a seeker with drawGizmos=false, and then enable + drawGizmos, it will not draw gizmos until the next path request is issued. +- Fixes + - Fixed UNITY_4_0 preprocesor directives which were indented for UNITY 4 and not only 4.0. + Now they will be enabled for all 4.x versions of unity instead of only 4.0. + - Fixed a path pool leak in the Seeker which could cause paths not to be released if a seeker + was destroyed. + - When using a non-positive maxDistance for point graphs less processing power will be used. + - Removed unused 'recyclePaths' variable in the AIPath class. + - NullReferenceException could occur if the Pathfinding.Node.connections array was null. + - Fixed NullReferenceException which could occur sometimes when using a MultiTargetPath (Issue #16) + - Changed Ctrl to Alt when recalcing path continously in the Path Types example scene to avoid + clearing the points for the MultiTargetPath at the same time (it was also using Ctrl). + - Fixed strange looking movement artifacts during the first few frames when using RVO and interpolation was enabled. + - AlternativePath modifier will no longer cause underflows if penalties have been reset during the time it was active. It will now + only log a warning message and zero the penalty. + - Added Pathfinding.GraphUpdateObject.resetPenaltyOnPhysics (and similar in GraphUpdateScene) to force grid graphs not to reset penalties when + updating graphs. + - Fixed a bug which could cause pathfinding to crash if using the preprocessor directive ASTAR_NoTagPenalty. + - Fixed a case where StartEndModifier.exactEndPoint would incorrectly be used instead of exactStartPoint. + - AlternativePath modifier now correctly resets penalties if it is destroyed. + +## 3.2.4.1 +- Unity Asset Store guys complained about the wrong key image. + I had to update the version number to submit again. + +## 3.2.4 +- Highlights + - RecastGraph can now rasterize colliders as well! + - RecastGraph can rasterize colliders added to trees on unity terrains! + - RecastGraph will use Graphics.DrawMeshNow functions in Unity 4 instead of creating a dummy GameObject. + This will remove the annoying "cleaning up leaked mesh object" debug message which unity would log sometimes. + The debug mesh is now also only visible in the Scene View when the A* object is selected as that seemed + most logical to me (don't like this? post something in the forum saying you want a toggle for it and I will implement + one). + - GraphUpdateObject now has a \link Pathfinding.GraphUpdateObject.updateErosion toggle \endlink specifying if erosion (on grid graphs) should be recalculated after applying the guo. + This enables one to add walkable nodes which should have been made unwalkable by erosion. + - Made it a bit easier (and added more correct documentation) to add custom graph types when building for iPhone with Fast But No Exceptions (see iPhone page). +- Changes + - RecastGraph now only rasterizes enabled MeshRenderers. Previously even disabled ones would be included. + - Renamed RecastGraph.includeTerrain to RecastGraph.rasterizeTerrain to better match other variable naming. +- Fixes + - AIPath now resumes path calculation when the component or GameObject has been disabled and then reenabled. + +## 3.2.3 (free version mostly) +- Fixes + - A UNITY_IPHONE directive was not included in the free version. This caused compilation errors when building for iPhone. +- Changes + - Some documentation updates + +## 3.2.2 +- Changes + - Max Slope in grid graphs is now relative to the graph's up direction instead of world up (makes more sense I hope) +- Note + - Update really too small to be an update by itself, but I was updating the build scripts I use for the project and had to upload a new version because of technical reasons. + +## 3.2.1 +- Fixes + - Fixed bug which caused compiler errors on build (player, not in editor). + - Version number was by mistake set to 3.1 instead of 3.2 in the previous version. + +## 3.2 +- Highlights + - A complete Local Avoidance system is now included in the pro version! + - Almost every allocation can now be pooled. Which means a drastically lower allocation rate (GC get's called less often). + - Initial node penalty per graph can now be set. + Custom graph types implementing CreateNodes must update their implementations to properly assign this value. + - GraphUpdateScene has now many more tools and options which can be used. + - Added Pathfinding.PathUtilities which contains some usefull functions for working with paths and nodes. + - Added Pathfinding.Node.GetConnections to enable easy getting of all connections of a node. + The Node.connections array does not include custom connections which for example grid graphs use. + - Seeker.PostProcess function was added for easy postprocessing of paths calculated without a seeker. + - AstarPath.WaitForPath. Wait (block) until a specific path has been calculated. + - Path.WaitForPath. Wait using a coroutine until a specific path has been calculated. + - LayeredGridGraph now has support for up to 65535 layers (theoretically, but don't try it as you would probably run out of memory) + - Recast graph generation is now up to twice as fast! + - Fixed some UI glitches in Unity 4. + - Debugger component has more features and a slightly better layout. +- Fixes + - Fixed a bug which caused the SimpleSmoothModifier with uniformSegmentLength enabled to skip points sometimes. + - Fixed a bug where importing graphs additively which had the same GUID as a graph already loaded could cause bugs in the inspector. + - Fixed a bug where updating a GridGraph loaded from file would throw a NullReferenceException. + - Fixed a bug which could cause error messages for paths not to be logged + - Fixed a number of small bugs related to updating grid graphs (especially when using erosion as well). + - Overflows could occur in some navmesh/polygon math related functions when working with Int3s. This was because the precision of them had recently been increased. + Further down the line this could cause incorrect answers to GetNearest queries. + Fixed by casting to long when necessary. + - Navmesh2.shader defined "Cull Off" twice. + - Pathfinding threads are now background threads. This will prevent them from blocking the process to terminate if they of some reason are still alive (hopefully at least). + - When really high penalties are applied (which could be underflowed negative penalties) a warning message is logged. + Really high penalties (close to max uint value) can otherwise cause overflows and in some cases infinity loops because of that. + - ClosestPointOnTriangle is now spelled correctly. + - MineBotAI now uses Update instead of FixedUpdate. + - Use Dark Skin option is now exposed again since it could be incorrectly set sometimes. Now you can force it to light or dark, or set it to auto. + - Fixed recast graph bug when using multiple terrains. Previously only one terrain would be used. + - Fixed some UI glitches in Unity 4. +- Changes + - Removed Pathfinding.NNInfo.priority. + - Removed Pathfinding.NearestNodePriority. + - Conversions between NNInfo and Node are now explicit to comply with the rule of "if information might be lost: use explicit casts". + - NNInfo is now a struct. + - GraphHitInfo is now a struct. + - Path.vectorPath and Path.path are now List and List respectively. This is done to enable pooling of resources more efficiently. + - Added Pathfinding.Node.RecalculateConnectionCosts. + - Moved IsPathPossible from GraphUpdateUtilities to PathUtilities. + - Pathfinding.Path.processed was replaced with Pathfinding.Path.state. The new variable will have much more information about where + the path is in the pathfinding pipeline. + - Paths should not be created with constructors anymore, instead use the PathPool class and then call some Setup() method + - When the AstarPath object is destroyed, calculated paths in the return queue are not returned with errors anymore, but just returned. + - Removed depracated methods AstarPath.AddToPathPool, RecyclePath, GetFromPathPool. +- Bugs + - C++ Version of Recast does not work on Windows. + - GraphUpdateScene does in some cases not draw correctly positioned gizmos. + - Starting two webplayers and closing down the first might cause the other one's pathfinding threads to crash (unity bug?) (confirmed on osx) + +## 3.1.4 (iOS fixes) +- Fixes + - More fixes for the iOS platform. + - The "JsonFx.Json.dll" file is now correctly named. +- Changes + - Removed unused code from DotNetZip which reduced the size of it with about 20 KB. + +## 3.1.3 (free version only) +- Fixes + - Some of the fixes which were said to have been made in 3.1.2 were actually not included in the free version of the project. Sorry about that. + - Also includes a new JsonFx and Ionic.Zip dll. This should make it possible to build with the .Net 2.0 Subset again see: + http://www.arongranberg.com/forums/topic/ios-problem/page/1/ + +## 3.1.2 (small bugfix release) +- Fixes + - Fixed a bug which caused builds for iPhone to fail. + - Fixed a bug which caused runtime errors on the iPhone platform. + - Fixed a bug which caused huge lag in the editor for some users when using grid graphs. + - ListGraphs are now correctly loaded as PointGraphs when loading data from older versions of the system. +- Changes + - Moved JsonFx into the namespace Pathfinding.Serialization.JsonFx to avoid conflicts with users own JsonFx libraries (if they used JsonFx). + +- Known bugs + - Recast graph does not work when using static batching on any objects included. + +## 3.1.1 (small bugfix release) +- Fixes + - Fixed a bug which would cause Pathfinding.GraphUpdateUtilities.UpdateGraphsNoBlock to throw an exception when using multithreading + - Fixed a bug which caused an error to be logged and no pathfinding working when not using multithreading in the free version of the project + - Fixed some example scene bugs due to downgrading the project from Unity 3.5 to Unity 3.4 + +## 3.1 +- Fixed bug which caused LayerMask fields (GridGraph inspector for example) to behave weirdly for custom layers on Unity 3.5 and up. +- The color setting "Node Connection" now actually sets the colors of the node connections when no other information should be shown using the connection colors or when no data is available. +- Put the Int3 class in a separate file. +- Casting between Int3 and Vector3 is no longer implicit. This follows the rule of "if information might be lost: use explicit casts". +- Renamed ListGraph to PointGraph. "ListGraph" has previously been used for historical reasons. PointGraph is a more suitable name. +- Graph can now have names in the editor (just click the name in the graph list) +- Graph Gizmos can now be selectively shown or hidden per graph (small "eye" icon to the right of the graph's name) +- Added GraphUpdateUtilities with many useful functions for updating graphs. +- Erosion for grid graphs can now use tags instead of walkability +- Fixed a bug where using One Way links could in some cases result in a NullReferenceException being thrown. +- Vector3 fields in the graph editors now look a bit better in Unity 3.5+. EditorGUILayout.Vector3Field didn't show the XYZ labels in a good way (no idea why) +- GridGraph.useRaycastNormal is now enabled only if the Max Slope is less than 90 degrees. Previously it was a manual setting. +- The keyboard shortcut to scan all graphs does now also work even when the graphs are not deserialized yet (which happens a lot in the editor) +- Added NodeLink script, which can be attached to GameObjects to add manual links. This system will eventually replace the links system in the A* editor. +- Added keyboard shortcuts for adding and removing links. See Menubar -> Edit -> Pathfinding + \note Some features are restricted to Unity 3.5 and newer because of technical limitations in earlier versions (especially multi-object editing related features). + + +## 3.1 beta (version number 3.0.9.9 in Unity due to technical limitations of the System.Versions class) +- Multithreading is now enabled in the free version of the A* Pathfinding Project! +- Better support for graph updates called during e.g OnPostScan. +- PathID is now used as a short everywhere in the project +- G,H and penalty is now used as unsigned integers everywhere in the project instead of signed integers. +- There is now only one tag per node (if not the \#define ConfigureTagsAsMultiple is set). +- Fixed a bug which could make connections between graphs invalid when loading from file (would also log annoying error messages). +- Erosion (GridGraph) can now be used even when updating the graph during runtime. +- Fixed a bug where the GridGraph could return null from it's GetNearestForce calls which ended up later throwing a NullReferenceException. +- FunnelModifier no longer warns if any graph in the path does not implement the IFunnelGraph interface (i.e have no support for the funnel algorithm) + and instead falls back to add node positions to the path. +- Added a new graph type : LayerGridGraph which works like a GridGraph, but has support for multiple layers of nodes (e.g multiple floors in a building). +- ScanOnStartup is now exposed in the editor. +- Separated temporary path data and connectivity data. +- Rewritten multithreading. You can now run any number of threads in parallel. +- To avoid possible infinite loops, paths are no longer returned with just an error when requested at times they should not (e.g right when destroying the pathfinding object) +- Cleaned up code in AstarPath.cs, members are now structured and many obsolete members have been removed. +- Rewritten serialization. Now uses Json for settings along with a small part hardcoded binary data (for performance and memory). + This is a lot more stable and will be more forwards and backwards compatible. + Data is now saved as zip files(in memory, but can be saved to file) which means you can actually edit them by hand if you want! +- Added dependency JsonFx (modified for smaller code size and better compatibility). +- Added dependency DotNetZip (reduced version and a bit modified) for zip compression. +- Graph types wanting to serialize members must add the JsonOptIn attribute to the class and JsonMember to any members to serialize (in the JsonFx.Json namespace) +- Graph types wanting to serialize a bit more data (custom), will have to override some new functions from the NavGraph class to do that instead of the old serialization functions. +- Changed from using System.Guid to a custom written Guid implementation placed in Pathfinding.Util.Guid. This was done to improve compabitility with iOS and other platforms. + Previously it could crash when trying to create one because System.Guid was not included in the runtime. +- Renamed callback AstarPath.OnSafeNodeUpdate to AstarPath.OnSafeCallback (also added AstarPath.OnThreadSafeCallback) +- MultiTargetPath would throw NullReferenceException if no valid start node was found, fixed now. +- Binary heaps are now automatically expanded if needed, no annoying warning messages. +- Fixed a bug where grid graphs would not update the correct area (using GraphUpdateObject) if it was rotated. +- Node position precision increased from 100 steps per world unit to 1000 steps per world unit (if 1 world unit = 1m, that is mm precision). + This also means that all costs and penalties in graphs will need to be multiplied by 10 to match the new scale. + It also means the max range of node positions is reduced a bit... but it is still quite large (about 2 150 000 world units in either direction, that should be enough). +- If Unity 3.5 is used, the EditorGUIUtility.isProSkin field is used to toggle between light and dark skin. +- Added LayeredGridGraph which works almost the same as grid graphs, but support multiple layers of nodes. +- \note Dropped Unity 3.3 support. + + Known Bugs: The C++ version of Recast does not work on Windows + +## Documentation Update +- Changed from FixedUpdate to Update in the Get Started Guide. CharacterController.SimpleMove should not be called more than once per frame, + so this might have lowered performance when using many agents, sorry about this typo. +## 3.0.9 +- The List Graph's "raycast" variable is now serialized correctly, so it will be saved. +- List graphs do not generate connections from nodes to themselves anymore (yielding slightly faster searches) +- List graphs previously calculated cost values for connections which were very low (they should have been 100 times larger), + this can have caused searches which were not very accurate on small scales since the values were rounded to the nearest integer. +- Added Pathfinding.Path.recalcStartEndCosts to specify if the start and end nodes connection costs should be recalculated when searching to reflect + small differences between the node's position and the actual used start point. It is on by default but if you change node connection costs you might want to switch it off to get more accurate paths. +- Fixed a compile time warning in the free version from referecing obsolete variables in the project. +- Added AstarPath.threadTimeoutFrames which specifies how long the pathfinding thread will wait for new work to turn up before aborting (due to request). This variable is not exposed in the inspector yet. +- Fixed typo, either there are eight (8) or four (4) max connections per node in a GridGraph, never six (6). +- AlternativePath will no longer cause errors when using multithreading! +- Added Pathfinding.ConstantPath, a path type which finds all nodes in a specific distance (cost) from a start node. +- Added Pathfinding.FloodPath and Pathfinding.FloodPathTracer as an extreamly fast way to generate paths to a single point in for example TD games. +- Fixed a bug in MultiTargetPath which could make it extreamly slow to process. It would not use much CPU power, but it could take half a second for it to complete due to excessive yielding +- Fixed a bug in FleePath, it now returns the correct path. It had previously sometimes returned the last node searched, but which was not necessarily the best end node (though it was often close) +- Using \#defines, the pathfinder can now be better profiled (see Optimizations tab -> Profile Astar) +- Added example scene Path Types (mainly useful for A* Pro users, so I have only included it for them) +- Added many more tooltips in the editor +- Fixed a bug which would double the Y coordinate of nodes in grid graphs when loading from saved data (or caching startup) +- Graph saving to file will now work better for users of the Free version, I had forgot to include a segment of code for Grid Graphs (sorry about that) +- Some other bugfixes +## 3.0.8.2 +- Fixed a critical bug which could render the A* inspector unusable on Windows due to problems with backslashes and forward slashes in paths. +## 3.0.8.1 +- Fixed critical crash bug. When building, a preprocessor-directive had messed up serialization so the game would probably crash from an OutOfMemoryException. +## 3.0.8 +- Graph saving to file is now exposed for users of the Free version +- Fixed a bug where penalties added using a GraphUpdateObject would be overriden if updatePhysics was turned on in the GraphUpdateObject +- Fixed a bug where list graphs could ignore some children nodes, especially common if the hierarchy was deep +- Fixed the case where empty messages would spam the log (instead of spamming somewhat meaningful messages) when path logging was set to Only Errors +- Changed the NNConstraint used as default when calling NavGraph.GetNearest from NNConstraint.Default to NNConstraint.None, this is now the same as the default for AstarPath.GetNearest. +- You can now set the size of the red cubes shown in place of unwalkable nodes (Settings-->Show Unwalkable Nodes-->Size) +- Dynamic search of where the EditorAssets folder is, so now you can place it anywhere in the project. +- Minor A* inspector enhancements. +- Fixed a very rare bug which could, when using multithreading cause the pathfinding thread not to start after it has been terminated due to a long delay +- Modifiers can now be enabled or disabled in the editor +- Added custom inspector for the Simple Smooth Modifier. Hopefully it will now be easier to use (or at least get the hang on which fields you should change). +- Added AIFollow.canSearch to disable or enable searching for paths due to popular request. +- Added AIFollow.canMove to disable or enable moving due to popular request. +- Changed behaviour of AIFollow.Stop, it will now set AIFollow.ccanSearch and AIFollow.ccanMove to false thus making it completely stop and stop searching for paths. +- Removed Path.customData since it is a much better solution to create a new path class which inherits from Path. +- Seeker.StartPath is now implemented with overloads instead of optional parameters to simplify usage for Javascript users +- Added Curved Nonuniform spline as a smoothing option for the Simple Smooth modifier. +- Added Pathfinding.WillBlockPath as function for checking if a GraphUpdateObject would block pathfinding between two nodes (useful in TD games). +- Unity References (GameObject's, Transforms and similar) are now serialized in another way, hopefully this will make it more stable as people have been having problems with the previous one, especially on the iPhone. +- Added shortcuts to specific types of graphs, AstarData.navmesh, AstarData.gridGraph, AstarData.listGraph +- Known Bugs: The C++ version of Recast does not work on Windows +## 3.0.7 +- Grid Graphs can now be scaled to allow non-square nodes, good for isometric games. +- Added more options for custom links. For example individual nodes or connections can be either enabled or disabled. And penalty can be added to individual nodes +- Placed the Scan keyboard shortcut code in a different place, hopefully it will work more often now +- Disabled GUILayout in the AstarPath script for a possible small speed boost +- Some debug variables (such as AstarPath.PathsCompleted) are now only updated if the ProfileAstar define is enabled +- DynamicGridObstacle will now update nodes correctly when the object is destroyed +- Unwalkable nodes no longer shows when Show Graphs is not toggled +- Removed Path.multithreaded since it was not used +- Removed Path.preCallback since it was obsolate +- Added Pathfinding.XPath as a more customizable path +- Added example of how to use MultiTargetPaths to the documentation as it was seriously lacking info on that area +- The viewing mesh scaling for recast graphs is now correct also for the C# version +- The StartEndModifier now changes the path length to 2 for correct applying if a path length of 1 was passed. +- The progressbar is now removed even if an exception was thrown during scanning +- Two new example scenes have been added, one for list graphs which includes sample links, and another one for recast graphs +- Reverted back to manually setting the dark skin option, since it didn't work in all cases, however if a dark skin is detected, the user will be asked if he/she wants to enable the dark skin +- Added gizmos for the AIFollow script which shows the current waypoint and a circle around it illustrating the distance required for it to be considered "reached". +- The C# version of Recast does now use Character Radius instead of Erosion Radius (world units instead of voxels) +- Fixed an IndexOutOfRange exception which could occur when saving a graph with no nodes to file +- Known Bugs: The C++ version of Recast does not work on Windows +## 3.0.6 +- Added support for a C++ version of Recast which means faster scanning times and more features (though almost no are available at the moment since I haven't added support for them yet). +- Removed the overload AstarData.AddGraph (string type, NavGraph graph) since it was obsolete. AstarData.AddGraph (Pathfinding.NavGraph) should be used now. +- Fixed a few bugs in the FunnelModifier which could cause it to return invalid paths +- A reference image can now be generated for the Use Texture option for Grid Graphs +- Fixed an editor bug with graphs which had no editors +- Graphs with no editors now show up in the Add New Graph list to show that they have been found, but they cannot be used +- Deleted the \a graphIndex parameter in the Pathfinding.NavGraph.Scan function. If you need to use it in your graph's Scan function, get it using Pathfinding.AstarData.GetGraphIndex +- Javascript support! At last you can use Js code with the A* Pathfinding Project! Go to A* Inspector-->Settings-->Editor-->Enable Js Support to enable it +- The Dark Skin is now automatically used if the rest of Unity uses the dark skin(hopefully) +- Fixed a bug which could cause Unity to crash when using multithreading and creating a new AstarPath object during runtime +## 3.0.5 +- \link Pathfinding.PointGraph List Graphs \endlink now support UpdateGraphs. This means that they for example can be used with the DynamicObstacle script. +- List Graphs can now gather nodes based on GameObject tags instead of all nodes as childs of a specific GameObject. +- List Graphs can now search recursively for childs to the 'root' GameObject instead of just searching through the top-level children. +- Added custom area colors which can be edited in the inspector (A* inspector --> Settings --> Color Settings --> Custom Area Colors) +- Fixed a NullReference bug which could occur when loading a Unity Reference with the AstarSerializer. +- Fixed some bugs with the FleePath and RandomPath which could cause the StartEndModifier to assign the wrong endpoint to the path. +- Documentation is now more clear on what is A* Pathfinding Project Pro only features. +- Pathfinding.NNConstraint now has a variable to constrain which graphs to search (A* Pro only).\n + This is also available for Pathfinding.GraphUpdateObject which now have a field for an NNConstraint where it can constrain which graphs to update. +- StartPath calls on the Seeker can now take a parameter specifying which graphs to search for close nodes on (A* Pro only) +- Added the delegate AstarPath.OnAwakeSettings which is called as the first thing in the Awake function, can be used to set up settings. +- Pathfinding.UserConnection.doOverrideCost is now serialized correctly. This represents the toggle to the right of the "Cost" field when editing a link. +- Fixed some bugs with the RecastGraph when spans were partially out-of-bounds, this could generate seemingly random holes in the mesh +## 3.0.4 (only pro version affected) +- Added a Dark Skin for Unity Pro users (though it is available to Unity Free users too, even though it doesn't look very good). + It can be enabled through A* Inspector --> Settings --> Editor Settings --> Use Dark Skin +- Added option to include or not include out of bounds voxels (Y axis below the graph only) for Recast graphs. +## 3.0.3 (only pro version affected) +- Fixed a NullReferenceException caused by Voxelize.cs which could surface if there were MeshFilters with no Renderers on GameObjects (Only Pro version affected) +## 3.0.2 +- Textures can now be used to add penalty, height or change walkability of a Grid Graph (A* Pro only) +- Slope can now be used to add penalty to nodes +- Height (Y position) can now be usd to add penalty to nodes +- Prioritized graphs can be used to enable prioritizing some graphs before others when they are overlapping +- Several bug fixes +- Included a new DynamicGridObstacle.cs script which can be attached to any obstacle with a collider and it will update grids around it to account for changed position +## 3.0.1 +- Fixed Unity 3.3 compability +## 3.0 +- Rewrote the system from scratch +- Funnel modifier +- Easier to extend the system + + +## x. releases are major rewrites or updates to the system. +## .x releases are quite big feature updates +## ..x releases are the most common updates, fix bugs, add some features etc. +## ...x releases are quickfixes, most common when there was a really bad bug which needed fixing ASAP. diff --git a/Final Platformer/Assets/AstarPathfindingProject/CHANGELOG.md.meta b/Final Platformer/Assets/AstarPathfindingProject/CHANGELOG.md.meta new file mode 100644 index 0000000..015a9ba --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e271255bdfe8941f9ab0acccbb14dd82 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core.meta b/Final Platformer/Assets/AstarPathfindingProject/Core.meta new file mode 100644 index 0000000..e1e64a7 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b6b8abb917bca4ce0ad1b26452b3c58d diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AI.meta new file mode 100644 index 0000000..38e138b --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5ab9be352d07b44e68ad7c1a03eef3a5 diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIBase.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIBase.cs new file mode 100644 index 0000000..ff6c5b2 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIBase.cs @@ -0,0 +1,729 @@ +using UnityEngine; +using System.Collections; +using UnityEngine.Serialization; + +namespace Pathfinding { + using Pathfinding.RVO; + using Pathfinding.Util; + + /// + /// Base class for AIPath and RichAI. + /// This class holds various methods and fields that are common to both AIPath and RichAI. + /// + /// See: + /// See: + /// See: (all movement scripts implement this interface) + /// + [RequireComponent(typeof(Seeker))] + public abstract class AIBase : VersionedMonoBehaviour { + /// \copydoc Pathfinding::IAstarAI::radius + public float radius = 0.5f; + + /// \copydoc Pathfinding::IAstarAI::height + public float height = 2; + + /// + /// Determines how often the agent will search for new paths (in seconds). + /// The agent will plan a new path to the target every N seconds. + /// + /// If you have fast moving targets or AIs, you might want to set it to a lower value. + /// + /// See: + /// See: + /// + public float repathRate = 0.5f; + + /// \copydoc Pathfinding::IAstarAI::canSearch + [UnityEngine.Serialization.FormerlySerializedAs("repeatedlySearchPaths")] + public bool canSearch = true; + + /// \copydoc Pathfinding::IAstarAI::canMove + public bool canMove = true; + + /// Max speed in world units per second + [UnityEngine.Serialization.FormerlySerializedAs("speed")] + public float maxSpeed = 1; + + /// + /// Gravity to use. + /// If set to (NaN,NaN,NaN) then Physics.Gravity (configured in the Unity project settings) will be used. + /// If set to (0,0,0) then no gravity will be used and no raycast to check for ground penetration will be performed. + /// + public Vector3 gravity = new Vector3(float.NaN, float.NaN, float.NaN); + + /// + /// Layer mask to use for ground placement. + /// Make sure this does not include the layer of any colliders attached to this gameobject. + /// + /// See: + /// See: https://docs.unity3d.com/Manual/Layers.html + /// + public LayerMask groundMask = -1; + + /// + /// Offset along the Y coordinate for the ground raycast start position. + /// Normally the pivot of the character is at the character's feet, but you usually want to fire the raycast + /// from the character's center, so this value should be half of the character's height. + /// + /// A green gizmo line will be drawn upwards from the pivot point of the character to indicate where the raycast will start. + /// + /// See: + /// Deprecated: Use the property instead (2x this value) + /// + [System.Obsolete("Use the height property instead (2x this value)")] + public float centerOffset { + get { return height * 0.5f; } set { height = value * 2; } + } + + [SerializeField] + [HideInInspector] + [FormerlySerializedAs("centerOffset")] + float centerOffsetCompatibility = float.NaN; + + /// + /// Determines which direction the agent moves in. + /// For 3D games you most likely want the ZAxisIsForward option as that is the convention for 3D games. + /// For 2D games you most likely want the YAxisIsForward option as that is the convention for 2D games. + /// + /// Using the YAxisForward option will also allow the agent to assume that the movement will happen in the 2D (XY) plane instead of the XZ plane + /// if it does not know. This is important only for the point graph which does not have a well defined up direction. The other built-in graphs (e.g the grid graph) + /// will all tell the agent which movement plane it is supposed to use. + /// + /// [Open online documentation to see images] + /// + [UnityEngine.Serialization.FormerlySerializedAs("rotationIn2D")] + public OrientationMode orientation = OrientationMode.ZAxisForward; + + /// + /// If true, the forward axis of the character will be along the Y axis instead of the Z axis. + /// + /// Deprecated: Use instead + /// + [System.Obsolete("Use orientation instead")] + public bool rotationIn2D { + get { return orientation == OrientationMode.YAxisForward; } + set { orientation = value ? OrientationMode.YAxisForward : OrientationMode.ZAxisForward; } + } + + /// + /// If true, the AI will rotate to face the movement direction. + /// See: + /// + public bool enableRotation = true; + + /// + /// Position of the agent. + /// If is true then this value will be synchronized every frame with Transform.position. + /// + protected Vector3 simulatedPosition; + + /// + /// Rotation of the agent. + /// If is true then this value will be synchronized every frame with Transform.rotation. + /// + protected Quaternion simulatedRotation; + + /// + /// Position of the agent. + /// In world space. + /// If is true then this value is idential to transform.position. + /// See: + /// See: + /// + public Vector3 position { get { return updatePosition ? tr.position : simulatedPosition; } } + + /// + /// Rotation of the agent. + /// If is true then this value is identical to transform.rotation. + /// + public Quaternion rotation { get { return updateRotation ? tr.rotation : simulatedRotation; } } + + /// Accumulated movement deltas from the method + Vector3 accumulatedMovementDelta = Vector3.zero; + + /// + /// Current desired velocity of the agent (does not include local avoidance and physics). + /// Lies in the movement plane. + /// + protected Vector2 velocity2D; + + /// + /// Velocity due to gravity. + /// Perpendicular to the movement plane. + /// + /// When the agent is grounded this may not accurately reflect the velocity of the agent. + /// It may be non-zero even though the agent is not moving. + /// + protected float verticalVelocity; + + /// Cached Seeker component + protected Seeker seeker; + + /// Cached Transform component + protected Transform tr; + + /// Cached Rigidbody component + protected Rigidbody rigid; + + /// Cached Rigidbody component + protected Rigidbody2D rigid2D; + + /// Cached CharacterController component + protected CharacterController controller; + + + /// + /// Plane which this agent is moving in. + /// This is used to convert between world space and a movement plane to make it possible to use this script in + /// both 2D games and 3D games. + /// + public IMovementPlane movementPlane = GraphTransform.identityTransform; + + /// + /// Determines if the character's position should be coupled to the Transform's position. + /// If false then all movement calculations will happen as usual, but the object that this component is attached to will not move + /// instead only the property will change. + /// + /// This is useful if you want to control the movement of the character using some other means such + /// as for example root motion but still want the AI to move freely. + /// See: Combined with calling from a separate script instead of it being called automatically one can take a similar approach to what is documented here: https://docs.unity3d.com/Manual/nav-CouplingAnimationAndNavigation.html + /// + /// See: which in contrast to this field will disable all movement calculations. + /// See: + /// + [System.NonSerialized] + public bool updatePosition = true; + + /// + /// Determines if the character's rotation should be coupled to the Transform's rotation. + /// If false then all movement calculations will happen as usual, but the object that this component is attached to will not rotate + /// instead only the property will change. + /// + /// See: + /// + [System.NonSerialized] + public bool updateRotation = true; + + /// Indicates if gravity is used during this frame + protected bool usingGravity { get; set; } + + /// Delta time used for movement during the last frame + protected float lastDeltaTime; + + /// Last frame index when was updated + protected int prevFrame; + + /// Position of the character at the end of the last frame + protected Vector3 prevPosition1; + + /// Position of the character at the end of the frame before the last frame + protected Vector3 prevPosition2; + + /// Amount which the character wants or tried to move with during the last frame + protected Vector2 lastDeltaPosition; + + /// Only when the previous path has been calculated should the script consider searching for a new path + protected bool waitingForPathCalculation = false; + + /// Time when the last path request was started + protected float lastRepath = float.NegativeInfinity; + + [UnityEngine.Serialization.FormerlySerializedAs("target")][SerializeField][HideInInspector] + Transform targetCompatibility; + + /// + /// True if the Start method has been executed. + /// Used to test if coroutines should be started in OnEnable to prevent calculating paths + /// in the awake stage (or rather before start on frame 0). + /// + bool startHasRun = false; + + /// + /// Target to move towards. + /// The AI will try to follow/move towards this target. + /// It can be a point on the ground where the player has clicked in an RTS for example, or it can be the player object in a zombie game. + /// + /// Deprecated: In 4.1 this will automatically add a component and set the target on that component. + /// Try instead to use the property which does not require a transform to be created as the target or use + /// the AIDestinationSetter component directly. + /// + [System.Obsolete("Use the destination property or the AIDestinationSetter component instead")] + public Transform target { + get { + var setter = GetComponent(); + return setter != null ? setter.target : null; + } + set { + targetCompatibility = null; + var setter = GetComponent(); + if (setter == null) setter = gameObject.AddComponent(); + setter.target = value; + destination = value != null ? value.position : new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); + } + } + + /// \copydoc Pathfinding::IAstarAI::destination + public Vector3 destination { get; set; } + + /// \copydoc Pathfinding::IAstarAI::velocity + public Vector3 velocity { + get { + return lastDeltaTime > 0.000001f ? (prevPosition1 - prevPosition2) / lastDeltaTime : Vector3.zero; + } + } + + /// + /// Velocity that this agent wants to move with. + /// Includes gravity and local avoidance if applicable. + /// + public Vector3 desiredVelocity { get { return lastDeltaTime > 0.00001f ? movementPlane.ToWorld(lastDeltaPosition / lastDeltaTime, verticalVelocity) : Vector3.zero; } } + + /// \copydoc Pathfinding::IAstarAI::isStopped + public bool isStopped { get; set; } + + /// \copydoc Pathfinding::IAstarAI::onSearchPath + public System.Action onSearchPath { get; set; } + + /// True if the path should be automatically recalculated as soon as possible + protected virtual bool shouldRecalculatePath { + get { + return Time.time - lastRepath >= repathRate && !waitingForPathCalculation && canSearch && !float.IsPositiveInfinity(destination.x); + } + } + + protected AIBase () { + // Note that this needs to be set here in the constructor and not in e.g Awake + // because it is possible that other code runs and sets the destination property + // before the Awake method on this script runs. + destination = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); + } + + /// + /// Looks for any attached components like RVOController and CharacterController etc. + /// + /// This is done during . If you are adding/removing components during runtime you may want to call this function + /// to make sure that this script finds them. It is unfortunately prohibitive from a performance standpoint to look for components every frame. + /// + public virtual void FindComponents () { + tr = transform; + seeker = GetComponent(); + // Find attached movement components + controller = GetComponent(); + rigid = GetComponent(); + rigid2D = GetComponent(); + } + + /// Called when the component is enabled + protected virtual void OnEnable () { + FindComponents(); + // Make sure we receive callbacks when paths are calculated + seeker.pathCallback += OnPathComplete; + Init(); + } + + /// + /// Starts searching for paths. + /// If you override this method you should in most cases call base.Start () at the start of it. + /// See: + /// + protected virtual void Start () { + startHasRun = true; + Init(); + } + + void Init () { + if (startHasRun) { + // Clamp the agent to the navmesh (which is what the Teleport call will do essentially. Though only some movement scripts require this, like RichAI). + // The Teleport call will also make sure some variables are properly initialized (like #prevPosition1 and #prevPosition2) + Teleport(position, false); + lastRepath = float.NegativeInfinity; + if (shouldRecalculatePath) SearchPath(); + } + } + + /// \copydoc Pathfinding::IAstarAI::Teleport + public virtual void Teleport (Vector3 newPosition, bool clearPath = true) { + if (clearPath) ClearPath(); + prevPosition1 = prevPosition2 = simulatedPosition = newPosition; + if (updatePosition) tr.position = newPosition; + if (clearPath) SearchPath(); + } + + protected void CancelCurrentPathRequest () { + waitingForPathCalculation = false; + // Abort calculation of the current path + if (seeker != null) seeker.CancelCurrentPathRequest(); + } + + protected virtual void OnDisable () { + ClearPath(); + + // Make sure we no longer receive callbacks when paths complete + seeker.pathCallback -= OnPathComplete; + + velocity2D = Vector3.zero; + accumulatedMovementDelta = Vector3.zero; + verticalVelocity = 0f; + lastDeltaTime = 0; + } + + /// + /// Called every frame. + /// If no rigidbodies are used then all movement happens here. + /// + protected virtual void Update () { + if (shouldRecalculatePath) SearchPath(); + + // If gravity is used depends on a lot of things. + // For example when a non-kinematic rigidbody is used then the rigidbody will apply the gravity itself + // Note that the gravity can contain NaN's, which is why the comparison uses !(a==b) instead of just a!=b. + usingGravity = !(gravity == Vector3.zero) && (!updatePosition || ((rigid == null || rigid.isKinematic) && (rigid2D == null || rigid2D.isKinematic))); + if (rigid == null && rigid2D == null && canMove) { + Vector3 nextPosition; + Quaternion nextRotation; + MovementUpdate(Time.deltaTime, out nextPosition, out nextRotation); + FinalizeMovement(nextPosition, nextRotation); + } + } + + /// + /// Called every physics update. + /// If rigidbodies are used then all movement happens here. + /// + protected virtual void FixedUpdate () { + if (!(rigid == null && rigid2D == null) && canMove) { + Vector3 nextPosition; + Quaternion nextRotation; + MovementUpdate(Time.fixedDeltaTime, out nextPosition, out nextRotation); + FinalizeMovement(nextPosition, nextRotation); + } + } + + /// \copydoc Pathfinding::IAstarAI::MovementUpdate + public void MovementUpdate (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) { + lastDeltaTime = deltaTime; + MovementUpdateInternal(deltaTime, out nextPosition, out nextRotation); + } + + /// Called during either Update or FixedUpdate depending on if rigidbodies are used for movement or not + protected abstract void MovementUpdateInternal (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation); + + /// + /// Outputs the start point and end point of the next automatic path request. + /// This is a separate method to make it easy for subclasses to swap out the endpoints + /// of path requests. For example the script which requires the endpoints + /// to be transformed to graph space first. + /// + protected virtual void CalculatePathRequestEndpoints (out Vector3 start, out Vector3 end) { + start = GetFeetPosition(); + end = destination; + } + + /// \copydoc Pathfinding::IAstarAI::SearchPath + public virtual void SearchPath () { + if (float.IsPositiveInfinity(destination.x)) return; + if (onSearchPath != null) onSearchPath(); + + lastRepath = Time.time; + waitingForPathCalculation = true; + + seeker.CancelCurrentPathRequest(); + + Vector3 start, end; + CalculatePathRequestEndpoints(out start, out end); + + // Alternative way of requesting the path + //ABPath p = ABPath.Construct(start, end, null); + //seeker.StartPath(p); + + // This is where we should search to + // Request a path to be calculated from our current position to the destination + seeker.StartPath(start, end); + } + + /// + /// Position of the base of the character. + /// This is used for pathfinding as the character's pivot point is sometimes placed + /// at the center of the character instead of near the feet. In a building with multiple floors + /// the center of a character may in some scenarios be closer to the navmesh on the floor above + /// than to the floor below which could cause an incorrect path to be calculated. + /// To solve this the start point of the requested paths is always at the base of the character. + /// + public virtual Vector3 GetFeetPosition () { + return position; + } + + /// Called when a requested path has been calculated + protected abstract void OnPathComplete (Path newPath); + + /// + /// Clears the current path of the agent. + /// + /// Usually invoked using + /// + /// See: + /// See: + /// + protected abstract void ClearPath (); + + /// \copydoc Pathfinding::IAstarAI::SetPath + public void SetPath (Path path) { + if (path == null) { + CancelCurrentPathRequest(); + ClearPath(); + } else if (path.PipelineState == PathState.Created) { + // Path has not started calculation yet + lastRepath = Time.time; + waitingForPathCalculation = true; + seeker.CancelCurrentPathRequest(); + seeker.StartPath(path); + } else if (path.PipelineState == PathState.Returned) { + // Path has already been calculated + + // We might be calculating another path at the same time, and we don't want that path to override this one. So cancel it. + if (seeker.GetCurrentPath() != path) seeker.CancelCurrentPathRequest(); + else throw new System.ArgumentException("If you calculate the path using seeker.StartPath then this script will pick up the calculated path anyway as it listens for all paths the Seeker finishes calculating. You should not call SetPath in that case."); + + OnPathComplete(path); + } else { + // Path calculation has been started, but it is not yet complete. Cannot really handle this. + throw new System.ArgumentException("You must call the SetPath method with a path that either has been completely calculated or one whose path calculation has not been started at all. It looks like the path calculation for the path you tried to use has been started, but is not yet finished."); + } + } + + /// + /// Accelerates the agent downwards. + /// See: + /// See: + /// + protected void ApplyGravity (float deltaTime) { + // Apply gravity + if (usingGravity) { + float verticalGravity; + velocity2D += movementPlane.ToPlane(deltaTime * (float.IsNaN(gravity.x) ? Physics.gravity : gravity), out verticalGravity); + verticalVelocity += verticalGravity; + } else { + verticalVelocity = 0; + } + } + + /// Calculates how far to move during a single frame + protected Vector2 CalculateDeltaToMoveThisFrame (Vector2 position, float distanceToEndOfPath, float deltaTime) { + // Direction and distance to move during this frame + return Vector2.ClampMagnitude(velocity2D * deltaTime, distanceToEndOfPath); + } + + /// + /// Simulates rotating the agent towards the specified direction and returns the new rotation. + /// + /// Note that this only calculates a new rotation, it does not change the actual rotation of the agent. + /// Useful when you are handling movement externally using but you want to use the built-in rotation code. + /// + /// See: + /// + /// Direction in world space to rotate towards. + /// Maximum number of degrees to rotate this frame. + public Quaternion SimulateRotationTowards (Vector3 direction, float maxDegrees) { + return SimulateRotationTowards(movementPlane.ToPlane(direction), maxDegrees); + } + + /// + /// Simulates rotating the agent towards the specified direction and returns the new rotation. + /// + /// Note that this only calculates a new rotation, it does not change the actual rotation of the agent. + /// + /// See: + /// See: + /// + /// Direction in the movement plane to rotate towards. + /// Maximum number of degrees to rotate this frame. + protected Quaternion SimulateRotationTowards (Vector2 direction, float maxDegrees) { + if (direction != Vector2.zero) { + Quaternion targetRotation = Quaternion.LookRotation(movementPlane.ToWorld(direction, 0), movementPlane.ToWorld(Vector2.zero, 1)); + // This causes the character to only rotate around the Z axis + if (orientation == OrientationMode.YAxisForward) targetRotation *= Quaternion.Euler(90, 0, 0); + return Quaternion.RotateTowards(simulatedRotation, targetRotation, maxDegrees); + } + return simulatedRotation; + } + + /// \copydoc Pathfinding::IAstarAI::Move + public virtual void Move (Vector3 deltaPosition) { + accumulatedMovementDelta += deltaPosition; + } + + /// + /// Moves the agent to a position. + /// + /// This is used if you want to override how the agent moves. For example if you are using + /// root motion with Mecanim. + /// + /// This will use a CharacterController, Rigidbody, Rigidbody2D or the Transform component depending on what options + /// are available. + /// + /// The agent will be clamped to the navmesh after the movement (if such information is available, generally this is only done by the RichAI component). + /// + /// See: for some example code. + /// See: , , + /// + /// New position of the agent. + /// New rotation of the agent. If #enableRotation is false then this parameter will be ignored. + public virtual void FinalizeMovement (Vector3 nextPosition, Quaternion nextRotation) { + if (enableRotation) FinalizeRotation(nextRotation); + FinalizePosition(nextPosition); + } + + void FinalizeRotation (Quaternion nextRotation) { + simulatedRotation = nextRotation; + if (updateRotation) { + if (rigid != null) rigid.MoveRotation(nextRotation); + else if (rigid2D != null) rigid2D.MoveRotation(nextRotation.eulerAngles.z); + else tr.rotation = nextRotation; + } + } + + void FinalizePosition (Vector3 nextPosition) { + // Use a local variable, it is significantly faster + Vector3 currentPosition = simulatedPosition; + bool positionDirty1 = false; + + if (controller != null && controller.enabled && updatePosition) { + // Use CharacterController + // The Transform may not be at #position if it was outside the navmesh and had to be moved to the closest valid position + tr.position = currentPosition; + controller.Move((nextPosition - currentPosition) + accumulatedMovementDelta); + // Grab the position after the movement to be able to take physics into account + // TODO: Add this into the clampedPosition calculation below to make RVO better respond to physics + currentPosition = tr.position; + if (controller.isGrounded) verticalVelocity = 0; + } else { + // Use Transform, Rigidbody, Rigidbody2D or nothing at all (if updatePosition = false) + float lastElevation; + movementPlane.ToPlane(currentPosition, out lastElevation); + currentPosition = nextPosition + accumulatedMovementDelta; + + // Position the character on the ground + if (usingGravity) currentPosition = RaycastPosition(currentPosition, lastElevation); + positionDirty1 = true; + } + + // Clamp the position to the navmesh after movement is done + bool positionDirty2 = false; + currentPosition = ClampToNavmesh(currentPosition, out positionDirty2); + + // Assign the final position to the character if we haven't already set it (mostly for performance, setting the position can be slow) + if ((positionDirty1 || positionDirty2) && updatePosition) { + // Note that rigid.MovePosition may or may not move the character immediately. + // Check the Unity documentation for the special cases. + if (rigid != null) rigid.MovePosition(currentPosition); + else if (rigid2D != null) rigid2D.MovePosition(currentPosition); + else tr.position = currentPosition; + } + + accumulatedMovementDelta = Vector3.zero; + simulatedPosition = currentPosition; + UpdateVelocity(); + } + + protected void UpdateVelocity () { + var currentFrame = Time.frameCount; + + if (currentFrame != prevFrame) prevPosition2 = prevPosition1; + prevPosition1 = position; + prevFrame = currentFrame; + } + + /// + /// Constrains the character's position to lie on the navmesh. + /// Not all movement scripts have support for this. + /// + /// Returns: New position of the character that has been clamped to the navmesh. + /// + /// Current position of the character. + /// True if the character's position was modified by this method. + protected virtual Vector3 ClampToNavmesh (Vector3 position, out bool positionChanged) { + positionChanged = false; + return position; + } + + /// + /// Checks if the character is grounded and prevents ground penetration. + /// + /// Sets to zero if the character is grounded. + /// + /// Returns: The new position of the character. + /// + /// Position of the character in the world. + /// Elevation coordinate before the agent was moved. This is along the 'up' axis of the #movementPlane. + protected Vector3 RaycastPosition (Vector3 position, float lastElevation) { + RaycastHit hit; + float elevation; + + movementPlane.ToPlane(position, out elevation); + float rayLength = tr.localScale.y * height * 0.5f + Mathf.Max(0, lastElevation-elevation); + Vector3 rayOffset = movementPlane.ToWorld(Vector2.zero, rayLength); + + if (Physics.Raycast(position + rayOffset, -rayOffset, out hit, rayLength, groundMask, QueryTriggerInteraction.Ignore)) { + // Grounded + // Make the vertical velocity fall off exponentially. This is reasonable from a physical standpoint as characters + // are not completely stiff and touching the ground will not immediately negate all velocity downwards. The AI will + // stop moving completely due to the raycast penetration test but it will still *try* to move downwards. This helps + // significantly when moving down along slopes as if the vertical velocity would be set to zero when the character + // was grounded it would lead to a kind of 'bouncing' behavior (try it, it's hard to explain). Ideally this should + // use a more physically correct formula but this is a good approximation and is much more performant. The constant + // '5' in the expression below determines how quickly it converges but high values can lead to too much noise. + verticalVelocity *= System.Math.Max(0, 1 - 5 * lastDeltaTime); + return hit.point; + } + return position; + } + + protected virtual void OnDrawGizmosSelected () { + // When selected in the Unity inspector it's nice to make the component react instantly if + // any other components are attached/detached or enabled/disabled. + // We don't want to do this normally every frame because that would be expensive. + if (Application.isPlaying) FindComponents(); + } + + public static readonly Color ShapeGizmoColor = new Color(240/255f, 213/255f, 30/255f); + + protected virtual void OnDrawGizmos () { + if (!Application.isPlaying || !enabled) FindComponents(); + + var color = ShapeGizmoColor; + if (orientation == OrientationMode.YAxisForward) { + Draw.Gizmos.Cylinder(position, Vector3.forward, 0, radius * tr.localScale.x, color); + } else { + Draw.Gizmos.Cylinder(position, rotation * Vector3.up, tr.localScale.y * height, radius * tr.localScale.x, color); + } + + if (!float.IsPositiveInfinity(destination.x) && Application.isPlaying) Draw.Gizmos.CircleXZ(destination, 0.2f, Color.blue); + } + + protected override void Reset () { + ResetShape(); + base.Reset(); + } + + void ResetShape () { + var cc = GetComponent(); + + if (cc != null) { + radius = cc.radius; + height = Mathf.Max(radius*2, cc.height); + } + } + + protected override int OnUpgradeSerializedData (int version, bool unityThread) { + if (unityThread && !float.IsNaN(centerOffsetCompatibility)) { + height = centerOffsetCompatibility*2; + ResetShape(); + centerOffsetCompatibility = float.NaN; + } + #pragma warning disable 618 + if (unityThread && targetCompatibility != null) target = targetCompatibility; + #pragma warning restore 618 + return 1; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIBase.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIBase.cs.meta new file mode 100644 index 0000000..7c735c0 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIBase.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a08f67bbe580e4ddfaebd06363c9cc97 +timeCreated: 1496932372 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 100 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AILerp.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AILerp.cs new file mode 100644 index 0000000..ddea438 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AILerp.cs @@ -0,0 +1,654 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace Pathfinding { + using Pathfinding.Util; + + /// + /// Linearly interpolating movement script. + /// This movement script will follow the path exactly, it uses linear interpolation to move between the waypoints in the path. + /// This is desirable for some types of games. + /// It also works in 2D. + /// + /// See: You can see an example of this script in action in the example scene called Example15_2D. + /// + /// \section rec Configuration + /// \subsection rec-snapped Recommended setup for movement along connections + /// + /// This depends on what type of movement you are aiming for. + /// If you are aiming for movement where the unit follows the path exactly and move only along the graph connections on a grid/point graph. + /// I recommend that you adjust the StartEndModifier on the Seeker component: set the 'Start Point Snapping' field to 'NodeConnection' and the 'End Point Snapping' field to 'SnapToNode'. + /// [Open online documentation to see images] + /// [Open online documentation to see images] + /// + /// \subsection rec-smooth Recommended setup for smooth movement + /// If you on the other hand want smoother movement I recommend setting 'Start Point Snapping' and 'End Point Snapping' to 'ClosestOnNode' and to add the Simple Smooth Modifier to the GameObject as well. + /// Alternatively you can use the which works better on navmesh/recast graphs or the . + /// + /// You should not combine the Simple Smooth Modifier or the Funnel Modifier with the NodeConnection snapping mode. This may lead to very odd behavior. + /// + /// [Open online documentation to see images] + /// [Open online documentation to see images] + /// You may also want to tweak the . + /// + /// \ingroup movementscripts + /// + [RequireComponent(typeof(Seeker))] + [AddComponentMenu("Pathfinding/AI/AILerp (2D,3D)")] + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_a_i_lerp.php")] + public class AILerp : VersionedMonoBehaviour, IAstarAI { + /// + /// Determines how often it will search for new paths. + /// If you have fast moving targets or AIs, you might want to set it to a lower value. + /// The value is in seconds between path requests. + /// + public float repathRate = 0.5F; + + /// \copydoc Pathfinding::IAstarAI::canSearch + public bool canSearch = true; + + /// \copydoc Pathfinding::IAstarAI::canMove + public bool canMove = true; + + /// Speed in world units + public float speed = 3; + + /// + /// Determines which direction the agent moves in. + /// For 3D games you most likely want the ZAxisIsForward option as that is the convention for 3D games. + /// For 2D games you most likely want the YAxisIsForward option as that is the convention for 2D games. + /// + /// Using the YAxisForward option will also allow the agent to assume that the movement will happen in the 2D (XY) plane instead of the XZ plane + /// if it does not know. This is important only for the point graph which does not have a well defined up direction. The other built-in graphs (e.g the grid graph) + /// will all tell the agent which movement plane it is supposed to use. + /// + /// [Open online documentation to see images] + /// + [UnityEngine.Serialization.FormerlySerializedAs("rotationIn2D")] + public OrientationMode orientation = OrientationMode.ZAxisForward; + + /// + /// If true, the forward axis of the character will be along the Y axis instead of the Z axis. + /// + /// Deprecated: Use instead + /// + [System.Obsolete("Use orientation instead")] + public bool rotationIn2D { + get { return orientation == OrientationMode.YAxisForward; } + set { orientation = value ? OrientationMode.YAxisForward : OrientationMode.ZAxisForward; } + } + + /// + /// If true, the AI will rotate to face the movement direction. + /// See: + /// + public bool enableRotation = true; + + /// How quickly to rotate + public float rotationSpeed = 10; + + /// + /// If true, some interpolation will be done when a new path has been calculated. + /// This is used to avoid short distance teleportation. + /// See: + /// + public bool interpolatePathSwitches = true; + + /// + /// How quickly to interpolate to the new path. + /// See: + /// + public float switchPathInterpolationSpeed = 5; + + /// True if the end of the current path has been reached + public bool reachedEndOfPath { get; private set; } + + /// \copydoc Pathfinding::IAstarAI::reachedDestination + public bool reachedDestination { + get { + if (!reachedEndOfPath || !interpolator.valid) return false; + // Note: distanceToSteeringTarget is the distance to the end of the path when approachingPathEndpoint is true + var dir = destination - interpolator.endPoint; + // Ignore either the y or z coordinate depending on if we are using 2D mode or not + if (orientation == OrientationMode.YAxisForward) dir.z = 0; + else dir.y = 0; + + // Check against using a very small margin + // In theory a check against 0 should be done, but this will be a bit more resilient against targets that move slowly or maybe jitter around due to floating point errors. + if (remainingDistance + dir.magnitude >= 0.05f) return false; + + return true; + } + } + + public Vector3 destination { get; set; } + + /// + /// Determines if the character's position should be coupled to the Transform's position. + /// If false then all movement calculations will happen as usual, but the object that this component is attached to will not move + /// instead only the property will change. + /// + /// See: which in contrast to this field will disable all movement calculations. + /// See: + /// + [System.NonSerialized] + public bool updatePosition = true; + + /// + /// Determines if the character's rotation should be coupled to the Transform's rotation. + /// If false then all movement calculations will happen as usual, but the object that this component is attached to will not rotate + /// instead only the property will change. + /// + /// See: + /// + [System.NonSerialized] + public bool updateRotation = true; + + /// + /// Target to move towards. + /// The AI will try to follow/move towards this target. + /// It can be a point on the ground where the player has clicked in an RTS for example, or it can be the player object in a zombie game. + /// + /// Deprecated: In 4.0 this will automatically add a component and set the target on that component. + /// Try instead to use the property which does not require a transform to be created as the target or use + /// the AIDestinationSetter component directly. + /// + [System.Obsolete("Use the destination property or the AIDestinationSetter component instead")] + public Transform target { + get { + var setter = GetComponent(); + return setter != null ? setter.target : null; + } + set { + targetCompatibility = null; + var setter = GetComponent(); + if (setter == null) setter = gameObject.AddComponent(); + setter.target = value; + destination = value != null ? value.position : new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); + } + } + + /// \copydoc Pathfinding::IAstarAI::position + public Vector3 position { get { return updatePosition ? tr.position : simulatedPosition; } } + + /// \copydoc Pathfinding::IAstarAI::rotation + public Quaternion rotation { get { return updateRotation ? tr.rotation : simulatedRotation; } } + + #region IAstarAI implementation + + /// \copydoc Pathfinding::IAstarAI::Move + void IAstarAI.Move (Vector3 deltaPosition) { + // This script does not know the concept of being away from the path that it is following + // so this call will be ignored (as is also mentioned in the documentation). + } + + /// \copydoc Pathfinding::IAstarAI::radius + float IAstarAI.radius { get { return 0; } set {} } + + /// \copydoc Pathfinding::IAstarAI::height + float IAstarAI.height { get { return 0; } set {} } + + /// \copydoc Pathfinding::IAstarAI::maxSpeed + float IAstarAI.maxSpeed { get { return speed; } set { speed = value; } } + + /// \copydoc Pathfinding::IAstarAI::canSearch + bool IAstarAI.canSearch { get { return canSearch; } set { canSearch = value; } } + + /// \copydoc Pathfinding::IAstarAI::canMove + bool IAstarAI.canMove { get { return canMove; } set { canMove = value; } } + + Vector3 IAstarAI.velocity { + get { + return Time.deltaTime > 0.00001f ? (previousPosition1 - previousPosition2) / Time.deltaTime : Vector3.zero; + } + } + + Vector3 IAstarAI.desiredVelocity { + get { + // The AILerp script sets the position every frame. It does not take into account physics + // or other things. So the velocity should always be the same as the desired velocity. + return (this as IAstarAI).velocity; + } + } + + /// \copydoc Pathfinding::IAstarAI::steeringTarget + Vector3 IAstarAI.steeringTarget { + get { + // AILerp doesn't use steering at all, so we will just return a point ahead of the agent in the direction it is moving. + return interpolator.valid ? interpolator.position + interpolator.tangent : simulatedPosition; + } + } + #endregion + + /// \copydoc Pathfinding::IAstarAI::remainingDistance + public float remainingDistance { + get { + return Mathf.Max(interpolator.remainingDistance, 0); + } + set { + interpolator.remainingDistance = Mathf.Max(value, 0); + } + } + + /// \copydoc Pathfinding::IAstarAI::hasPath + public bool hasPath { + get { + return interpolator.valid; + } + } + + /// \copydoc Pathfinding::IAstarAI::pathPending + public bool pathPending { + get { + return !canSearchAgain; + } + } + + /// \copydoc Pathfinding::IAstarAI::isStopped + public bool isStopped { get; set; } + + /// \copydoc Pathfinding::IAstarAI::onSearchPath + public System.Action onSearchPath { get; set; } + + /// Cached Seeker component + protected Seeker seeker; + + /// Cached Transform component + protected Transform tr; + + /// Time when the last path request was sent + protected float lastRepath = -9999; + + /// Current path which is followed + protected ABPath path; + + /// Only when the previous path has been returned should a search for a new path be done + protected bool canSearchAgain = true; + + /// + /// When a new path was returned, the AI was moving along this ray. + /// Used to smoothly interpolate between the previous movement and the movement along the new path. + /// The speed is equal to movement direction. + /// + protected Vector3 previousMovementOrigin; + protected Vector3 previousMovementDirection; + + /// + /// Time since the path was replaced by a new path. + /// See: + /// + protected float pathSwitchInterpolationTime = 0; + + protected PathInterpolator interpolator = new PathInterpolator(); + + + /// + /// Holds if the Start function has been run. + /// Used to test if coroutines should be started in OnEnable to prevent calculating paths + /// in the awake stage (or rather before start on frame 0). + /// + bool startHasRun = false; + + Vector3 previousPosition1, previousPosition2, simulatedPosition; + Quaternion simulatedRotation; + + /// Required for serialization backward compatibility + [UnityEngine.Serialization.FormerlySerializedAs("target")][SerializeField][HideInInspector] + Transform targetCompatibility; + + protected AILerp () { + // Note that this needs to be set here in the constructor and not in e.g Awake + // because it is possible that other code runs and sets the destination property + // before the Awake method on this script runs. + destination = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); + } + + /// + /// Initializes reference variables. + /// If you override this function you should in most cases call base.Awake () at the start of it. + /// + protected override void Awake () { + base.Awake(); + //This is a simple optimization, cache the transform component lookup + tr = transform; + + seeker = GetComponent(); + + // Tell the StartEndModifier to ask for our exact position when post processing the path This + // is important if we are using prediction and requesting a path from some point slightly ahead + // of us since then the start point in the path request may be far from our position when the + // path has been calculated. This is also good because if a long path is requested, it may take + // a few frames for it to be calculated so we could have moved some distance during that time + seeker.startEndModifier.adjustStartPoint = () => simulatedPosition; + } + + /// + /// Starts searching for paths. + /// If you override this function you should in most cases call base.Start () at the start of it. + /// See: + /// See: + /// + protected virtual void Start () { + startHasRun = true; + Init(); + } + + /// Called when the component is enabled + protected virtual void OnEnable () { + // Make sure we receive callbacks when paths complete + seeker.pathCallback += OnPathComplete; + Init(); + } + + void Init () { + if (startHasRun) { + // The Teleport call will make sure some variables are properly initialized (like #prevPosition1 and #prevPosition2) + Teleport(position, false); + lastRepath = float.NegativeInfinity; + if (shouldRecalculatePath) SearchPath(); + } + } + + public void OnDisable () { + ClearPath(); + // Make sure we no longer receive callbacks when paths complete + seeker.pathCallback -= OnPathComplete; + } + + /// \copydoc Pathfinding::IAstarAI::GetRemainingPath + public void GetRemainingPath (List buffer, out bool stale) { + buffer.Clear(); + if (!interpolator.valid) { + buffer.Add(position); + stale = true; + return; + } + + stale = false; + interpolator.GetRemainingPath(buffer); + // The agent is almost always at interpolation.position (which is buffer[0]) + // but sometimes - in particular when interpolating between two paths - the agent might at a slightly different position. + // So we replace the first point with the actual position of the agent. + buffer[0] = position; + } + + public void Teleport (Vector3 position, bool clearPath = true) { + if (clearPath) ClearPath(); + simulatedPosition = previousPosition1 = previousPosition2 = position; + if (updatePosition) tr.position = position; + reachedEndOfPath = false; + if (clearPath) SearchPath(); + } + + /// True if the path should be automatically recalculated as soon as possible + protected virtual bool shouldRecalculatePath { + get { + return Time.time - lastRepath >= repathRate && canSearchAgain && canSearch && !float.IsPositiveInfinity(destination.x); + } + } + + /// + /// Requests a path to the target. + /// Deprecated: Use instead. + /// + [System.Obsolete("Use SearchPath instead")] + public virtual void ForceSearchPath () { + SearchPath(); + } + + /// Requests a path to the target. + public virtual void SearchPath () { + if (float.IsPositiveInfinity(destination.x)) return; + if (onSearchPath != null) onSearchPath(); + + lastRepath = Time.time; + + // This is where the path should start to search from + var currentPosition = GetFeetPosition(); + + // If we are following a path, start searching from the node we will + // reach next this can prevent odd turns right at the start of the path + /*if (interpolator.valid) { + var prevDist = interpolator.distance; + // Move to the end of the current segment + interpolator.MoveToSegment(interpolator.segmentIndex, 1); + currentPosition = interpolator.position; + // Move back to the original position + interpolator.distance = prevDist; + }*/ + + canSearchAgain = false; + + // Alternative way of creating a path request + //ABPath p = ABPath.Construct(currentPosition, targetPoint, null); + //seeker.StartPath(p); + + // Create a new path request + // The OnPathComplete method will later be called with the result + seeker.StartPath(currentPosition, destination); + } + + /// + /// The end of the path has been reached. + /// If you want custom logic for when the AI has reached it's destination + /// add it here. + /// You can also create a new script which inherits from this one + /// and override the function in that script. + /// + public virtual void OnTargetReached () { + } + + /// + /// Called when a requested path has finished calculation. + /// A path is first requested by , it is then calculated, probably in the same or the next frame. + /// Finally it is returned to the seeker which forwards it to this function. + /// + protected virtual void OnPathComplete (Path _p) { + ABPath p = _p as ABPath; + + if (p == null) throw new System.Exception("This function only handles ABPaths, do not use special path types"); + + canSearchAgain = true; + + // Increase the reference count on the path. + // This is used for path pooling + p.Claim(this); + + // Path couldn't be calculated of some reason. + // More info in p.errorLog (debug string) + if (p.error) { + p.Release(this); + return; + } + + if (interpolatePathSwitches) { + ConfigurePathSwitchInterpolation(); + } + + + // Replace the old path + var oldPath = path; + path = p; + reachedEndOfPath = false; + + // Just for the rest of the code to work, if there + // is only one waypoint in the path add another one + if (path.vectorPath != null && path.vectorPath.Count == 1) { + path.vectorPath.Insert(0, GetFeetPosition()); + } + + // Reset some variables + ConfigureNewPath(); + + // Release the previous path + // This is used for path pooling. + // This is done after the interpolator has been configured in the ConfigureNewPath method + // as this method would otherwise invalidate the interpolator + // since the vectorPath list (which the interpolator uses) will be pooled. + if (oldPath != null) oldPath.Release(this); + + if (interpolator.remainingDistance < 0.0001f && !reachedEndOfPath) { + reachedEndOfPath = true; + OnTargetReached(); + } + } + + /// + /// Clears the current path of the agent. + /// + /// Usually invoked using + /// + /// See: + /// See: + /// + protected virtual void ClearPath () { + // Abort any calculations in progress + if (seeker != null) seeker.CancelCurrentPathRequest(); + canSearchAgain = true; + reachedEndOfPath = false; + + // Release current path so that it can be pooled + if (path != null) path.Release(this); + path = null; + interpolator.SetPath(null); + } + + /// \copydoc Pathfinding::IAstarAI::SetPath + public void SetPath (Path path) { + if (path == null) { + ClearPath(); + } else if (path.PipelineState == PathState.Created) { + // Path has not started calculation yet + lastRepath = Time.time; + canSearchAgain = false; + seeker.CancelCurrentPathRequest(); + seeker.StartPath(path); + } else if (path.PipelineState == PathState.Returned) { + // Path has already been calculated + + // We might be calculating another path at the same time, and we don't want that path to override this one. So cancel it. + if (seeker.GetCurrentPath() != path) seeker.CancelCurrentPathRequest(); + else throw new System.ArgumentException("If you calculate the path using seeker.StartPath then this script will pick up the calculated path anyway as it listens for all paths the Seeker finishes calculating. You should not call SetPath in that case."); + + OnPathComplete(path); + } else { + // Path calculation has been started, but it is not yet complete. Cannot really handle this. + throw new System.ArgumentException("You must call the SetPath method with a path that either has been completely calculated or one whose path calculation has not been started at all. It looks like the path calculation for the path you tried to use has been started, but is not yet finished."); + } + } + + protected virtual void ConfigurePathSwitchInterpolation () { + bool reachedEndOfPreviousPath = interpolator.valid && interpolator.remainingDistance < 0.0001f; + + if (interpolator.valid && !reachedEndOfPreviousPath) { + previousMovementOrigin = interpolator.position; + previousMovementDirection = interpolator.tangent.normalized * interpolator.remainingDistance; + pathSwitchInterpolationTime = 0; + } else { + previousMovementOrigin = Vector3.zero; + previousMovementDirection = Vector3.zero; + pathSwitchInterpolationTime = float.PositiveInfinity; + } + } + + public virtual Vector3 GetFeetPosition () { + return position; + } + + /// Finds the closest point on the current path and configures the + protected virtual void ConfigureNewPath () { + var hadValidPath = interpolator.valid; + var prevTangent = hadValidPath ? interpolator.tangent : Vector3.zero; + + interpolator.SetPath(path.vectorPath); + interpolator.MoveToClosestPoint(GetFeetPosition()); + + if (interpolatePathSwitches && switchPathInterpolationSpeed > 0.01f && hadValidPath) { + var correctionFactor = Mathf.Max(-Vector3.Dot(prevTangent.normalized, interpolator.tangent.normalized), 0); + interpolator.distance -= speed*correctionFactor*(1f/switchPathInterpolationSpeed); + } + } + + protected virtual void Update () { + if (shouldRecalculatePath) SearchPath(); + if (canMove) { + Vector3 nextPosition; + Quaternion nextRotation; + MovementUpdate(Time.deltaTime, out nextPosition, out nextRotation); + FinalizeMovement(nextPosition, nextRotation); + } + } + + /// \copydoc Pathfinding::IAstarAI::MovementUpdate + public void MovementUpdate (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) { + if (updatePosition) simulatedPosition = tr.position; + if (updateRotation) simulatedRotation = tr.rotation; + + Vector3 direction; + + nextPosition = CalculateNextPosition(out direction, isStopped ? 0f : deltaTime); + + if (enableRotation) nextRotation = SimulateRotationTowards(direction, deltaTime); + else nextRotation = simulatedRotation; + } + + /// \copydoc Pathfinding::IAstarAI::FinalizeMovement + public void FinalizeMovement (Vector3 nextPosition, Quaternion nextRotation) { + previousPosition2 = previousPosition1; + previousPosition1 = simulatedPosition = nextPosition; + simulatedRotation = nextRotation; + if (updatePosition) tr.position = nextPosition; + if (updateRotation) tr.rotation = nextRotation; + } + + Quaternion SimulateRotationTowards (Vector3 direction, float deltaTime) { + // Rotate unless we are really close to the target + if (direction != Vector3.zero) { + Quaternion targetRotation = Quaternion.LookRotation(direction, orientation == OrientationMode.YAxisForward ? Vector3.back : Vector3.up); + // This causes the character to only rotate around the Z axis + if (orientation == OrientationMode.YAxisForward) targetRotation *= Quaternion.Euler(90, 0, 0); + return Quaternion.Slerp(simulatedRotation, targetRotation, deltaTime * rotationSpeed); + } + return simulatedRotation; + } + + /// Calculate the AI's next position (one frame in the future). + /// The tangent of the segment the AI is currently traversing. Not normalized. + protected virtual Vector3 CalculateNextPosition (out Vector3 direction, float deltaTime) { + if (!interpolator.valid) { + direction = Vector3.zero; + return simulatedPosition; + } + + interpolator.distance += deltaTime * speed; + + if (interpolator.remainingDistance < 0.0001f && !reachedEndOfPath) { + reachedEndOfPath = true; + OnTargetReached(); + } + + direction = interpolator.tangent; + pathSwitchInterpolationTime += deltaTime; + var alpha = switchPathInterpolationSpeed * pathSwitchInterpolationTime; + if (interpolatePathSwitches && alpha < 1f) { + // Find the approximate position we would be at if we + // would have continued to follow the previous path + Vector3 positionAlongPreviousPath = previousMovementOrigin + Vector3.ClampMagnitude(previousMovementDirection, speed * pathSwitchInterpolationTime); + + // Interpolate between the position on the current path and the position + // we would have had if we would have continued along the previous path. + return Vector3.Lerp(positionAlongPreviousPath, interpolator.position, alpha); + } else { + return interpolator.position; + } + } + + protected override int OnUpgradeSerializedData (int version, bool unityThread) { + #pragma warning disable 618 + if (unityThread && targetCompatibility != null) target = targetCompatibility; + #pragma warning restore 618 + return 2; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AILerp.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AILerp.cs.meta new file mode 100644 index 0000000..c5f7ba3 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AILerp.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 847a14d4dc9cc43679ab34fc78e0182f +timeCreated: 1454879612 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIPath.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIPath.cs new file mode 100644 index 0000000..46c3ac0 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIPath.cs @@ -0,0 +1,487 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace Pathfinding { + using Pathfinding.RVO; + using Pathfinding.Util; + + /// + /// AI for following paths. + /// This AI is the default movement script which comes with the A* Pathfinding Project. + /// It is in no way required by the rest of the system, so feel free to write your own. But I hope this script will make it easier + /// to set up movement for the characters in your game. + /// This script works well for many types of units, but if you need the highest performance (for example if you are moving hundreds of characters) you + /// may want to customize this script or write a custom movement script to be able to optimize it specifically for your game. + /// + /// This script will try to move to a given . At , the path to the destination will be recalculated. + /// If you want to make the AI to follow a particular object you can attach the component. + /// Take a look at the getstarted (view in online documentation for working links) tutorial for more instructions on how to configure this script. + /// + /// Here is a video of this script being used move an agent around (technically it uses the script that inherits from this one but adds a bit of animation support for the example scenes): + /// [Open online documentation to see videos] + /// + /// \section variables Quick overview of the variables + /// In the inspector in Unity, you will see a bunch of variables. You can view detailed information further down, but here's a quick overview. + /// + /// The determines how often it will search for new paths, if you have fast moving targets, you might want to set it to a lower value. + /// The field is where the AI will try to move, it can be a point on the ground where the player has clicked in an RTS for example. + /// Or it can be the player object in a zombie game. + /// The is self-explanatory, as is . however might require some explanation: + /// It is the approximate distance from the target where the AI will start to slow down. Setting it to a large value will make the AI slow down very gradually. + /// determines the distance to the point the AI will move to (see image below). + /// + /// Below is an image illustrating several variables that are exposed by this class (, , + /// [Open online documentation to see images] + /// + /// This script has many movement fallbacks. + /// If it finds an RVOController attached to the same GameObject as this component, it will use that. If it finds a character controller it will also use that. + /// If it finds a rigidbody it will use that. Lastly it will fall back to simply modifying Transform.position which is guaranteed to always work and is also the most performant option. + /// + /// \section how-aipath-works How it works + /// In this section I'm going to go over how this script is structured and how information flows. + /// This is useful if you want to make changes to this script or if you just want to understand how it works a bit more deeply. + /// However you do not need to read this section if you are just going to use the script as-is. + /// + /// This script inherits from the class. The movement happens either in Unity's standard or method. + /// They are both defined in the AIBase class. Which one is actually used depends on if a rigidbody is used for movement or not. + /// Rigidbody movement has to be done inside the FixedUpdate method while otherwise it is better to do it in Update. + /// + /// From there a call is made to the method (which in turn calls . + /// This method contains the main bulk of the code and calculates how the AI *wants* to move. However it doesn't do any movement itself. + /// Instead it returns the position and rotation it wants the AI to move to have at the end of the frame. + /// The (or method then passes these values to the method which is responsible for actually moving the character. + /// That method also handles things like making sure the AI doesn't fall through the ground using raycasting. + /// + /// The AI recalculates its path regularly. This happens in the Update method which checks and if that returns true it will call . + /// The method will prepare a path request and send it to the component which should be attached to the same GameObject as this script. + /// Since this script will when waking up register to the delegate this script will be notified every time a new path is calculated by the method being called. + /// It may take one or sometimes multiple frames for the path to be calculated, but finally the method will be called and the current path that the AI is following will be replaced. + /// + [AddComponentMenu("Pathfinding/AI/AIPath (2D,3D)")] + public partial class AIPath : AIBase, IAstarAI { + /// + /// How quickly the agent accelerates. + /// Positive values represent an acceleration in world units per second squared. + /// Negative values are interpreted as an inverse time of how long it should take for the agent to reach its max speed. + /// For example if it should take roughly 0.4 seconds for the agent to reach its max speed then this field should be set to -1/0.4 = -2.5. + /// For a negative value the final acceleration will be: -acceleration*maxSpeed. + /// This behaviour exists mostly for compatibility reasons. + /// + /// In the Unity inspector there are two modes: Default and Custom. In the Default mode this field is set to -2.5 which means that it takes about 0.4 seconds for the agent to reach its top speed. + /// In the Custom mode you can set the acceleration to any positive value. + /// + public float maxAcceleration = -2.5f; + + /// + /// Rotation speed in degrees per second. + /// Rotation is calculated using Quaternion.RotateTowards. This variable represents the rotation speed in degrees per second. + /// The higher it is, the faster the character will be able to rotate. + /// + [UnityEngine.Serialization.FormerlySerializedAs("turningSpeed")] + public float rotationSpeed = 360; + + /// Distance from the end of the path where the AI will start to slow down + public float slowdownDistance = 0.6F; + + /// + /// How far the AI looks ahead along the path to determine the point it moves to. + /// In world units. + /// If you enable the toggle this value will be visualized in the scene view as a blue circle around the agent. + /// [Open online documentation to see images] + /// + /// Here are a few example videos showing some typical outcomes with good values as well as how it looks when this value is too low and too high. + /// + /// + /// + /// + /// + /// + ///
[Open online documentation to see videos]\xmlonly Too low
\endxmlonly A too low value and a too low acceleration will result in the agent overshooting a lot and not managing to follow the path well.
[Open online documentation to see videos]\xmlonly Ok
\endxmlonly A low value but a high acceleration works decently to make the AI follow the path more closely. Note that the component is better suited if you want the agent to follow the path without any deviations.
[Open online documentation to see videos]\xmlonly Ok
\endxmlonly A reasonable value in this example.
[Open online documentation to see videos]\xmlonly Ok
\endxmlonly A reasonable value in this example, but the path is followed slightly more loosely than in the previous video.
[Open online documentation to see videos]\xmlonly Too high
\endxmlonly A too high value will make the agent follow the path too loosely and may cause it to try to move through obstacles.
+ ///
+ public float pickNextWaypointDist = 2; + + /// + /// Distance to the end point to consider the end of path to be reached. + /// When the end is within this distance then will be called and will return true. + /// + public float endReachedDistance = 0.2F; + + /// Draws detailed gizmos constantly in the scene view instead of only when the agent is selected and settings are being modified + public bool alwaysDrawGizmos; + + /// + /// Slow down when not facing the target direction. + /// Incurs at a small performance overhead. + /// + public bool slowWhenNotFacingTarget = true; + + /// + /// What to do when within units from the destination. + /// The character can either stop immediately when it comes within that distance, which is useful for e.g archers + /// or other ranged units that want to fire on a target. Or the character can continue to try to reach the exact + /// destination point and come to a full stop there. This is useful if you want the character to reach the exact + /// point that you specified. + /// + /// Note: will become true when the character is within units from the destination + /// regardless of what this field is set to. + /// + public CloseToDestinationMode whenCloseToDestination = CloseToDestinationMode.Stop; + + /// + /// Ensure that the character is always on the traversable surface of the navmesh. + /// When this option is enabled a query will be done every frame to find the closest node that the agent can walk on + /// and if the agent is not inside that node, then the agent will be moved to it. + /// + /// This is especially useful together with local avoidance in order to avoid agents pushing each other into walls. + /// See: local-avoidance (view in online documentation for working links) for more info about this. + /// + /// This option also integrates with local avoidance so that if the agent is say forced into a wall by other agents the local avoidance + /// system will be informed about that wall and can take that into account. + /// + /// Enabling this has some performance impact depending on the graph type (pretty fast for grid graphs, slightly slower for navmesh/recast graphs). + /// If you are using a navmesh/recast graph you may want to switch to the movement script which is specifically written for navmesh/recast graphs and + /// does this kind of clamping out of the box. In many cases it can also follow the path more smoothly around sharp bends in the path. + /// + /// It is not recommended that you use this option together with the funnel modifier on grid graphs because the funnel modifier will make the path + /// go very close to the border of the graph and this script has a tendency to try to cut corners a bit. This may cause it to try to go slightly outside the + /// traversable surface near corners and that will look bad if this option is enabled. + /// + /// Warning: This option makes no sense to use on point graphs because point graphs do not have a surface. + /// Enabling this option when using a point graph will lead to the agent being snapped to the closest node every frame which is likely not what you want. + /// + /// Below you can see an image where several agents using local avoidance were ordered to go to the same point in a corner. + /// When not constraining the agents to the graph they are easily pushed inside obstacles. + /// [Open online documentation to see images] + /// + public bool constrainInsideGraph = false; + + /// Current path which is followed + protected Path path; + + /// Helper which calculates points along the current path + protected PathInterpolator interpolator = new PathInterpolator(); + + #region IAstarAI implementation + + /// \copydoc Pathfinding::IAstarAI::Teleport + public override void Teleport (Vector3 newPosition, bool clearPath = true) { + reachedEndOfPath = false; + base.Teleport(newPosition, clearPath); + } + + /// \copydoc Pathfinding::IAstarAI::remainingDistance + public float remainingDistance { + get { + return interpolator.valid ? interpolator.remainingDistance + movementPlane.ToPlane(interpolator.position - position).magnitude : float.PositiveInfinity; + } + } + + /// \copydoc Pathfinding::IAstarAI::reachedDestination + public bool reachedDestination { + get { + if (!reachedEndOfPath) return false; + if (remainingDistance + movementPlane.ToPlane(destination - interpolator.endPoint).magnitude > endReachedDistance) return false; + + // Don't do height checks in 2D mode + if (orientation != OrientationMode.YAxisForward) { + // Check if the destination is above the head of the character or far below the feet of it + float yDifference; + movementPlane.ToPlane(destination - position, out yDifference); + var h = tr.localScale.y * height; + if (yDifference > h || yDifference < -h*0.5) return false; + } + + return true; + } + } + + /// \copydoc Pathfinding::IAstarAI::reachedEndOfPath + public bool reachedEndOfPath { get; protected set; } + + /// \copydoc Pathfinding::IAstarAI::hasPath + public bool hasPath { + get { + return interpolator.valid; + } + } + + /// \copydoc Pathfinding::IAstarAI::pathPending + public bool pathPending { + get { + return waitingForPathCalculation; + } + } + + /// \copydoc Pathfinding::IAstarAI::steeringTarget + public Vector3 steeringTarget { + get { + return interpolator.valid ? interpolator.position : position; + } + } + + /// \copydoc Pathfinding::IAstarAI::radius + float IAstarAI.radius { get { return radius; } set { radius = value; } } + + /// \copydoc Pathfinding::IAstarAI::height + float IAstarAI.height { get { return height; } set { height = value; } } + + /// \copydoc Pathfinding::IAstarAI::maxSpeed + float IAstarAI.maxSpeed { get { return maxSpeed; } set { maxSpeed = value; } } + + /// \copydoc Pathfinding::IAstarAI::canSearch + bool IAstarAI.canSearch { get { return canSearch; } set { canSearch = value; } } + + /// \copydoc Pathfinding::IAstarAI::canMove + bool IAstarAI.canMove { get { return canMove; } set { canMove = value; } } + + #endregion + + /// \copydoc Pathfinding::IAstarAI::GetRemainingPath + public void GetRemainingPath (List buffer, out bool stale) { + buffer.Clear(); + buffer.Add(position); + if (!interpolator.valid) { + stale = true; + return; + } + + stale = false; + interpolator.GetRemainingPath(buffer); + } + + protected override void OnDisable () { + base.OnDisable(); + + // Release current path so that it can be pooled + if (path != null) path.Release(this); + path = null; + interpolator.SetPath(null); + } + + /// + /// The end of the path has been reached. + /// If you want custom logic for when the AI has reached it's destination add it here. You can + /// also create a new script which inherits from this one and override the function in that script. + /// + /// This method will be called again if a new path is calculated as the destination may have changed. + /// So when the agent is close to the destination this method will typically be called every seconds. + /// + public virtual void OnTargetReached () { + } + + /// + /// Called when a requested path has been calculated. + /// A path is first requested by , it is then calculated, probably in the same or the next frame. + /// Finally it is returned to the seeker which forwards it to this function. + /// + protected override void OnPathComplete (Path newPath) { + ABPath p = newPath as ABPath; + + if (p == null) throw new System.Exception("This function only handles ABPaths, do not use special path types"); + + waitingForPathCalculation = false; + + // Increase the reference count on the new path. + // This is used for object pooling to reduce allocations. + p.Claim(this); + + // Path couldn't be calculated of some reason. + // More info in p.errorLog (debug string) + if (p.error) { + p.Release(this); + return; + } + + // Release the previous path. + if (path != null) path.Release(this); + + // Replace the old path + path = p; + + // Make sure the path contains at least 2 points + if (path.vectorPath.Count == 1) path.vectorPath.Add(path.vectorPath[0]); + interpolator.SetPath(path.vectorPath); + + var graph = path.path.Count > 0 ? AstarData.GetGraph(path.path[0]) as ITransformedGraph : null; + movementPlane = graph != null ? graph.transform : (orientation == OrientationMode.YAxisForward ? new GraphTransform(Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(-90, 270, 90), Vector3.one)) : GraphTransform.identityTransform); + + // Reset some variables + reachedEndOfPath = false; + + // Simulate movement from the point where the path was requested + // to where we are right now. This reduces the risk that the agent + // gets confused because the first point in the path is far away + // from the current position (possibly behind it which could cause + // the agent to turn around, and that looks pretty bad). + interpolator.MoveToLocallyClosestPoint((GetFeetPosition() + p.originalStartPoint) * 0.5f); + interpolator.MoveToLocallyClosestPoint(GetFeetPosition()); + + // Update which point we are moving towards. + // Note that we need to do this here because otherwise the remainingDistance field might be incorrect for 1 frame. + // (due to interpolator.remainingDistance being incorrect). + interpolator.MoveToCircleIntersection2D(position, pickNextWaypointDist, movementPlane); + + var distanceToEnd = remainingDistance; + if (distanceToEnd <= endReachedDistance) { + reachedEndOfPath = true; + OnTargetReached(); + } + } + + protected override void ClearPath () { + CancelCurrentPathRequest(); + interpolator.SetPath(null); + reachedEndOfPath = false; + } + + /// Called during either Update or FixedUpdate depending on if rigidbodies are used for movement or not + protected override void MovementUpdateInternal (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) { + float currentAcceleration = maxAcceleration; + + // If negative, calculate the acceleration from the max speed + if (currentAcceleration < 0) currentAcceleration *= -maxSpeed; + + if (updatePosition) { + // Get our current position. We read from transform.position as few times as possible as it is relatively slow + // (at least compared to a local variable) + simulatedPosition = tr.position; + } + if (updateRotation) simulatedRotation = tr.rotation; + + var currentPosition = simulatedPosition; + + // Update which point we are moving towards + interpolator.MoveToCircleIntersection2D(currentPosition, pickNextWaypointDist, movementPlane); + var dir = movementPlane.ToPlane(steeringTarget - currentPosition); + + // Calculate the distance to the end of the path + float distanceToEnd = dir.magnitude + Mathf.Max(0, interpolator.remainingDistance); + + // Check if we have reached the target + var prevTargetReached = reachedEndOfPath; + reachedEndOfPath = distanceToEnd <= endReachedDistance && interpolator.valid; + if (!prevTargetReached && reachedEndOfPath) OnTargetReached(); + float slowdown; + + // Normalized direction of where the agent is looking + var forwards = movementPlane.ToPlane(simulatedRotation * (orientation == OrientationMode.YAxisForward ? Vector3.up : Vector3.forward)); + + // Check if we have a valid path to follow and some other script has not stopped the character + if (interpolator.valid && !isStopped) { + // How fast to move depending on the distance to the destination. + // Move slower as the character gets closer to the destination. + // This is always a value between 0 and 1. + slowdown = distanceToEnd < slowdownDistance? Mathf.Sqrt (distanceToEnd / slowdownDistance) : 1; + + if (reachedEndOfPath && whenCloseToDestination == CloseToDestinationMode.Stop) { + // Slow down as quickly as possible + velocity2D -= Vector2.ClampMagnitude(velocity2D, currentAcceleration * deltaTime); + } else { + velocity2D += MovementUtilities.CalculateAccelerationToReachPoint(dir, dir.normalized*maxSpeed, velocity2D, currentAcceleration, rotationSpeed, maxSpeed, forwards) * deltaTime; + } + } else { + slowdown = 1; + // Slow down as quickly as possible + velocity2D -= Vector2.ClampMagnitude(velocity2D, currentAcceleration * deltaTime); + } + + velocity2D = MovementUtilities.ClampVelocity(velocity2D, maxSpeed, slowdown, slowWhenNotFacingTarget && enableRotation, forwards); + + ApplyGravity(deltaTime); + + + // Set how much the agent wants to move during this frame + var delta2D = lastDeltaPosition = CalculateDeltaToMoveThisFrame(movementPlane.ToPlane(currentPosition), distanceToEnd, deltaTime); + nextPosition = currentPosition + movementPlane.ToWorld(delta2D, verticalVelocity * lastDeltaTime); + CalculateNextRotation(slowdown, out nextRotation); + } + + protected virtual void CalculateNextRotation (float slowdown, out Quaternion nextRotation) { + if (lastDeltaTime > 0.00001f && enableRotation) { + Vector2 desiredRotationDirection; + desiredRotationDirection = velocity2D; + + // Rotate towards the direction we are moving in. + // Don't rotate when we are very close to the target. + var currentRotationSpeed = rotationSpeed * Mathf.Max(0, (slowdown - 0.3f) / 0.7f); + nextRotation = SimulateRotationTowards(desiredRotationDirection, currentRotationSpeed * lastDeltaTime); + } else { + // TODO: simulatedRotation + nextRotation = rotation; + } + } + + static NNConstraint cachedNNConstraint = NNConstraint.Default; + protected override Vector3 ClampToNavmesh (Vector3 position, out bool positionChanged) { + if (constrainInsideGraph) { + cachedNNConstraint.tags = seeker.traversableTags; + cachedNNConstraint.graphMask = seeker.graphMask; + cachedNNConstraint.distanceXZ = true; + var clampedPosition = AstarPath.active.GetNearest(position, cachedNNConstraint).position; + + // We cannot simply check for equality because some precision may be lost + // if any coordinate transformations are used. + var difference = movementPlane.ToPlane(clampedPosition - position); + float sqrDifference = difference.sqrMagnitude; + if (sqrDifference > 0.001f*0.001f) { + // The agent was outside the navmesh. Remove that component of the velocity + // so that the velocity only goes along the direction of the wall, not into it + velocity2D -= difference * Vector2.Dot(difference, velocity2D) / sqrDifference; + + positionChanged = true; + // Return the new position, but ignore any changes in the y coordinate from the ClampToNavmesh method as the y coordinates in the navmesh are rarely very accurate + return position + movementPlane.ToWorld(difference); + } + } + + positionChanged = false; + return position; + } + +#if UNITY_EDITOR + [System.NonSerialized] + int gizmoHash = 0; + + [System.NonSerialized] + float lastChangedTime = float.NegativeInfinity; + + protected static readonly Color GizmoColor = new Color(46.0f/255, 104.0f/255, 201.0f/255); + + protected override void OnDrawGizmos () { + base.OnDrawGizmos(); + if (alwaysDrawGizmos) OnDrawGizmosInternal(); + } + + protected override void OnDrawGizmosSelected () { + base.OnDrawGizmosSelected(); + if (!alwaysDrawGizmos) OnDrawGizmosInternal(); + } + + void OnDrawGizmosInternal () { + var newGizmoHash = pickNextWaypointDist.GetHashCode() ^ slowdownDistance.GetHashCode() ^ endReachedDistance.GetHashCode(); + + if (newGizmoHash != gizmoHash && gizmoHash != 0) lastChangedTime = Time.realtimeSinceStartup; + gizmoHash = newGizmoHash; + float alpha = alwaysDrawGizmos ? 1 : Mathf.SmoothStep(1, 0, (Time.realtimeSinceStartup - lastChangedTime - 5f)/0.5f) * (UnityEditor.Selection.gameObjects.Length == 1 ? 1 : 0); + + if (alpha > 0) { + // Make sure the scene view is repainted while the gizmos are visible + if (!alwaysDrawGizmos) UnityEditor.SceneView.RepaintAll(); + Draw.Gizmos.Line(position, steeringTarget, GizmoColor * new Color(1, 1, 1, alpha)); + Gizmos.matrix = Matrix4x4.TRS(position, transform.rotation * (orientation == OrientationMode.YAxisForward ? Quaternion.Euler(-90, 0, 0) : Quaternion.identity), Vector3.one); + Draw.Gizmos.CircleXZ(Vector3.zero, pickNextWaypointDist, GizmoColor * new Color(1, 1, 1, alpha)); + Draw.Gizmos.CircleXZ(Vector3.zero, slowdownDistance, Color.Lerp(GizmoColor, Color.red, 0.5f) * new Color(1, 1, 1, alpha)); + Draw.Gizmos.CircleXZ(Vector3.zero, endReachedDistance, Color.Lerp(GizmoColor, Color.red, 0.8f) * new Color(1, 1, 1, alpha)); + } + } +#endif + + protected override int OnUpgradeSerializedData (int version, bool unityThread) { + base.OnUpgradeSerializedData(version, unityThread); + // Approximately convert from a damping value to a degrees per second value. + if (version < 1) rotationSpeed *= 90; + return 2; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIPath.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIPath.cs.meta new file mode 100644 index 0000000..5cb0534 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/AIPath.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f6eb1402c17e84a9282a7f0f62eb584f +timeCreated: 1491225739 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/IAstarAI.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/IAstarAI.cs new file mode 100644 index 0000000..81c50d9 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/IAstarAI.cs @@ -0,0 +1,386 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Pathfinding { + /// + /// Common interface for all movement scripts in the A* Pathfinding Project. + /// See: + /// See: + /// See: + /// + public interface IAstarAI { + /// + /// Height of the agent in world units. + /// This is visualized in the scene view as a yellow cylinder around the character. + /// + /// This value is currently only used if an RVOController is attached to the same GameObject, otherwise it is only used for drawing nice gizmos in the scene view. + /// However since the height value is used for some things, the radius field is always visible for consistency and easier visualization of the character. + /// That said, it may be used for something in a future release. + /// + /// Note: The script doesn't really have any use of knowing the radius or the height of the character, so this property will always return 0 in that script. + /// + float radius { get; set; } + + /// + /// Radius of the agent in world units. + /// This is visualized in the scene view as a yellow cylinder around the character. + /// + /// Note: The script doesn't really have any use of knowing the radius or the height of the character, so this property will always return 0 in that script. + /// + float height { get; set; } + + /// + /// Position of the agent. + /// In world space. + /// See: + /// + Vector3 position { get; } + + /// + /// Rotation of the agent. + /// In world space. + /// See: + /// + Quaternion rotation { get; } + + /// Max speed in world units per second + float maxSpeed { get; set; } + + /// + /// Actual velocity that the agent is moving with. + /// In world units per second. + /// + /// See: + /// + Vector3 velocity { get; } + + /// + /// Velocity that this agent wants to move with. + /// Includes gravity and local avoidance if applicable. + /// In world units per second. + /// + /// See: + /// + Vector3 desiredVelocity { get; } + + /// + /// Remaining distance along the current path to the end of the path. + /// For the RichAI movement script this may not always be precisely known, especially when + /// far away from the destination. In those cases an approximate distance will be returned. + /// + /// If the agent does not currently have a path, then positive infinity will be returned. + /// + /// Note: This is the distance to the end of the path, which may or may not be at the . If the character cannot reach the destination it will try to move as close as possible to it. + /// + /// Warning: Since path requests are asynchronous, there is a small delay between a path request being sent and this value being updated with the new calculated path. + /// + /// See: + /// See: + /// See: + /// + float remainingDistance { get; } + + /// + /// True if the ai has reached the . + /// This is a best effort calculation to see if the has been reached. + /// For the AIPath/RichAI scripts, this is when the character is within world units from the . + /// For the AILerp script it is when the character is at the destination (±a very small margin). + /// + /// This value will be updated immediately when the is changed (in contrast to , however since path requests are asynchronous + /// it will use an approximation until it sees the real path result. What this property does is to check the distance to the end of the current path, and add to that the distance + /// from the end of the path to the (i.e. is assumes it is possible to move in a straight line between the end of the current path to the destination) and then checks if that total + /// distance is less than . This property is therefore only a best effort, but it will work well for almost all use cases. + /// + /// Furthermore it will not report that the destination is reached if the destination is above the head of the character or more than half the of the character below its feet + /// (so if you have a multilevel building, it is important that you configure the of the character correctly). + /// + /// The cases which could be problematic are if an agent is standing next to a very thin wall and the destination suddenly changes to the other side of that thin wall. + /// During the time that it takes for the path to be calculated the agent may see itself as alredy having reached the destination because the destination only moved a very small distance (the wall was thin), + /// even though it may actually be quite a long way around the wall to the other side. + /// + /// In contrast to , this property is immediately updated when the is changed. + /// + /// + /// IEnumerator Start () { + /// ai.destination = somePoint; + /// // Start to search for a path to the destination immediately + /// ai.SearchPath(); + /// // Wait until the agent has reached the destination + /// while (!ai.reachedDestination) { + /// yield return null; + /// } + /// // The agent has reached the destination now + /// } + /// + /// + /// See: + /// See: + /// See: + /// + bool reachedDestination { get; } + + /// + /// True if the agent has reached the end of the current path. + /// + /// Note that setting the does not immediately update the path, nor is there any guarantee that the + /// AI will actually be able to reach the destination that you set. The AI will try to get as close as possible. + /// Often you want to use instead which is easier to work with. + /// + /// It is very hard to provide a method for detecting if the AI has reached the that works across all different games + /// because the destination may not even lie on the navmesh and how that is handled differs from game to game (see also the code snippet in the docs for . + /// + /// See: + /// See: + /// + bool reachedEndOfPath { get; } + + /// + /// Position in the world that this agent should move to. + /// + /// If no destination has been set yet, then (+infinity, +infinity, +infinity) will be returned. + /// + /// Note that setting this property does not immediately cause the agent to recalculate its path. + /// So it may take some time before the agent starts to move towards this point. + /// Most movement scripts have a repathRate field which indicates how often the agent looks + /// for a new path. You can also call the method to immediately + /// start to search for a new path. Paths are calculated asynchronously so when an agent starts to + /// search for path it may take a few frames (usually 1 or 2) until the result is available. + /// During this time the property will return true. + /// + /// If you are setting a destination and then want to know when the agent has reached that destination + /// then you could either use (recommended) or check both and . + /// Check the documentation for the respective fields to learn about their differences. + /// + /// + /// IEnumerator Start () { + /// ai.destination = somePoint; + /// // Start to search for a path to the destination immediately + /// ai.SearchPath(); + /// // Wait until the agent has reached the destination + /// while (!ai.reachedDestination) { + /// yield return null; + /// } + /// // The agent has reached the destination now + /// } + /// + /// + /// IEnumerator Start () { + /// ai.destination = somePoint; + /// // Start to search for a path to the destination immediately + /// // Note that the result may not become available until after a few frames + /// // ai.pathPending will be true while the path is being calculated + /// ai.SearchPath(); + /// // Wait until we know for sure that the agent has calculated a path to the destination we set above + /// while (ai.pathPending || !ai.reachedEndOfPath) { + /// yield return null; + /// } + /// // The agent has reached the destination now + /// } + /// + /// + Vector3 destination { get; set; } + + /// + /// Enables or disables recalculating the path at regular intervals. + /// Setting this to false does not stop any active path requests from being calculated or stop it from continuing to follow the current path. + /// + /// Note that this only disables automatic path recalculations. If you call the method a path will still be calculated. + /// + /// See: + /// See: + /// + bool canSearch { get; set; } + + /// + /// Enables or disables movement completely. + /// If you want the agent to stand still, but still react to local avoidance and use gravity: use instead. + /// + /// This is also useful if you want to have full control over when the movement calculations run. + /// Take a look at + /// + /// See: + /// See: + /// + bool canMove { get; set; } + + /// True if this agent currently has a path that it follows + bool hasPath { get; } + + /// True if a path is currently being calculated + bool pathPending { get; } + + /// + /// Gets or sets if the agent should stop moving. + /// If this is set to true the agent will immediately start to slow down as quickly as it can to come to a full stop. + /// The agent will still react to local avoidance and gravity (if applicable), but it will not try to move in any particular direction. + /// + /// The current path of the agent will not be cleared, so when this is set + /// to false again the agent will continue moving along the previous path. + /// + /// This is a purely user-controlled parameter, so for example it is not set automatically when the agent stops + /// moving because it has reached the target. Use for that. + /// + /// If this property is set to true while the agent is traversing an off-mesh link (RichAI script only), then the agent will + /// continue traversing the link and stop once it has completed it. + /// + /// Note: This is not the same as the setting which some movement scripts have. The setting + /// disables movement calculations completely (which among other things makes it not be affected by local avoidance or gravity). + /// For the AILerp movement script which doesn't use gravity or local avoidance anyway changing this property is very similar to + /// changing . + /// + /// The property will continue to indicate the point which the agent would move towards if it would not be stopped. + /// + bool isStopped { get; set; } + + /// + /// Point on the path which the agent is currently moving towards. + /// This is usually a point a small distance ahead of the agent + /// or the end of the path. + /// + /// If the agent does not have a path at the moment, then the agent's current position will be returned. + /// + Vector3 steeringTarget { get; } + + /// + /// Called when the agent recalculates its path. + /// This is called both for automatic path recalculations (see and manual ones (see . + /// + /// See: Take a look at the source code for an example of how it can be used. + /// + System.Action onSearchPath { get; set; } + + /// + /// Fills buffer with the remaining path. + /// + /// + /// var buffer = new List(); + /// ai.GetRemainingPath(buffer, out bool stale); + /// for (int i = 0; i < buffer.Count - 1; i++) { + /// Debug.DrawLine(buffer[i], buffer[i+1], Color.red); + /// } + /// + /// [Open online documentation to see images] + /// + /// The buffer will be cleared and replaced with the path. The first point is the current position of the agent. + /// May be true if the path is invalid in some way. For example if the agent has no path or (for the RichAI script only) if the agent has detected that some nodes in the path have been destroyed. + void GetRemainingPath (List buffer, out bool stale); + + /// + /// Recalculate the current path. + /// You can for example use this if you want very quick reaction times when you have changed the + /// so that the agent does not have to wait until the next automatic path recalculation (see . + /// + /// If there is an ongoing path calculation, it will be canceled, so make sure you leave time for the paths to get calculated before calling this function again. + /// A canceled path will show up in the log with the message "Canceled by script" (see . + /// + /// If no has been set yet then nothing will be done. + /// + /// Note: The path result may not become available until after a few frames. + /// During the calculation time the property will return true. + /// + /// See: + /// + void SearchPath (); + + /// + /// Make the AI follow the specified path. + /// In case the path has not been calculated, the script will call seeker.StartPath to calculate it. + /// This means the AI may not actually start to follow the path until in a few frames when the path has been calculated. + /// The field will as usual return true while the path is being calculated. + /// + /// In case the path has already been calculated it will immediately replace the current path the AI is following. + /// This is useful if you want to replace how the AI calculates its paths. + /// Note that if you calculate the path using seeker.StartPath then this script will already pick it up because it is listening for + /// all paths that the Seeker finishes calculating. In that case you do not need to call this function. + /// + /// If you pass null as a parameter then the current path will be cleared and the agent will stop moving. + /// Note than unless you have also disabled then the agent will soon recalculate its path and start moving again. + /// + /// You can disable the automatic path recalculation by setting the field to false. + /// + /// + /// // Disable the automatic path recalculation + /// ai.canSearch = false; + /// var pointToAvoid = enemy.position; + /// // Make the AI flee from the enemy. + /// // The path will be about 20 world units long (the default cost of moving 1 world unit is 1000). + /// var path = FleePath.Construct(ai.position, pointToAvoid, 1000 * 20); + /// ai.SetPath(path); + /// + /// // If you want to make use of properties like ai.reachedDestination or ai.remainingDistance or similar + /// // you should also set the destination property to something reasonable. + /// // Since the agent's own path recalculation is disabled, setting this will not affect how the paths are calculated. + /// // ai.destination = ... + /// + /// + void SetPath (Path path); + + /// + /// Instantly move the agent to a new position. + /// This will trigger a path recalculation (if clearPath is true, which is the default) so if you want to teleport the agent and change its + /// it is recommended that you set the before calling this method. + /// + /// The current path will be cleared by default. + /// + /// See: Works similarly to Unity's NavmeshAgent.Warp. + /// See: + /// + void Teleport (Vector3 newPosition, bool clearPath = true); + + /// + /// Move the agent. + /// + /// This is intended for external movement forces such as those applied by wind, conveyor belts, knockbacks etc. + /// + /// Some movement scripts may ignore this completely (notably the AILerp script) if it does not have + /// any concept of being moved externally. + /// + /// The agent will not be moved immediately when calling this method. Instead this offset will be stored and then + /// applied the next time the agent runs its movement calculations (which is usually later this frame or the next frame). + /// If you want to move the agent immediately then call: + /// + /// ai.Move(someVector); + /// ai.FinalizeMovement(ai.position, ai.rotation); + /// + /// + /// Direction and distance to move the agent in world space. + void Move (Vector3 deltaPosition); + + /// + /// Calculate how the character wants to move during this frame. + /// + /// Note that this does not actually move the character. You need to call for that. + /// This is called automatically unless is false. + /// + /// To handle movement yourself you can disable and call this method manually. + /// This code will replicate the normal behavior of the component: + /// + /// void Update () { + /// // Disable the AIs own movement code + /// ai.canMove = false; + /// Vector3 nextPosition; + /// Quaternion nextRotation; + /// // Calculate how the AI wants to move + /// ai.MovementUpdate(Time.deltaTime, out nextPosition, out nextRotation); + /// // Modify nextPosition and nextRotation in any way you wish + /// // Actually move the AI + /// ai.FinalizeMovement(nextPosition, nextRotation); + /// } + /// + /// + /// time to simulate movement for. Usually set to Time.deltaTime. + /// the position that the agent wants to move to during this frame. + /// the rotation that the agent wants to rotate to during this frame. + void MovementUpdate (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation); + + /// + /// Move the agent. + /// To be called as the last step when you are handling movement manually. + /// + /// The movement will be clamped to the navmesh if applicable (this is done for the RichAI movement script). + /// + /// See: for a code example. + /// + void FinalizeMovement (Vector3 nextPosition, Quaternion nextRotation); + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/IAstarAI.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/IAstarAI.cs.meta new file mode 100644 index 0000000..d415d1d --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/IAstarAI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b7438f3f6b9404f05ab7f584f92aa7d5 +timeCreated: 1495013922 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/NavmeshController.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/NavmeshController.cs new file mode 100644 index 0000000..3b6ed49 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/NavmeshController.cs @@ -0,0 +1,4 @@ + +// This file has been removed from the project. Since UnityPackages cannot +// delete files, only replace them, this message is left here to prevent old +// files from causing compiler errors diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/NavmeshController.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/NavmeshController.cs.meta new file mode 100644 index 0000000..379b815 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/NavmeshController.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 20549335d45df4a329ece093b865221b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/Seeker.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/Seeker.cs new file mode 100644 index 0000000..ae74e51 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/Seeker.cs @@ -0,0 +1,507 @@ +using UnityEngine; +using System.Collections.Generic; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +namespace Pathfinding { + /// + /// Handles path calls for a single unit. + /// \ingroup relevant + /// This is a component which is meant to be attached to a single unit (AI, Robot, Player, whatever) to handle its pathfinding calls. + /// It also handles post-processing of paths using modifiers. + /// + /// [Open online documentation to see images] + /// + /// See: calling-pathfinding (view in online documentation for working links) + /// See: modifiers (view in online documentation for working links) + /// + [AddComponentMenu("Pathfinding/Seeker")] + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_seeker.php")] + public class Seeker : VersionedMonoBehaviour { + /// + /// Enables drawing of the last calculated path using Gizmos. + /// The path will show up in green. + /// + /// See: OnDrawGizmos + /// + public bool drawGizmos = true; + + /// + /// Enables drawing of the non-postprocessed path using Gizmos. + /// The path will show up in orange. + /// + /// Requires that is true. + /// + /// This will show the path before any post processing such as smoothing is applied. + /// + /// See: drawGizmos + /// See: OnDrawGizmos + /// + public bool detailedGizmos; + + /// Path modifier which tweaks the start and end points of a path + [HideInInspector] + public StartEndModifier startEndModifier = new StartEndModifier(); + + /// + /// The tags which the Seeker can traverse. + /// + /// Note: This field is a bitmask. + /// See: bitmasks (view in online documentation for working links) + /// + [HideInInspector] + public int traversableTags = -1; + + /// + /// Penalties for each tag. + /// Tag 0 which is the default tag, will have added a penalty of tagPenalties[0]. + /// These should only be positive values since the A* algorithm cannot handle negative penalties. + /// + /// Note: This array should always have a length of 32 otherwise the system will ignore it. + /// + /// See: Pathfinding.Path.tagPenalties + /// + [HideInInspector] + public int[] tagPenalties = new int[32]; + + /// + /// Graphs that this Seeker can use. + /// This field determines which graphs will be considered when searching for the start and end nodes of a path. + /// It is useful in numerous situations, for example if you want to make one graph for small units and one graph for large units. + /// + /// This is a bitmask so if you for example want to make the agent only use graph index 3 then you can set this to: + /// seeker.graphMask = 1 << 3; + /// + /// See: bitmasks (view in online documentation for working links) + /// + /// Note that this field only stores which graph indices that are allowed. This means that if the graphs change their ordering + /// then this mask may no longer be correct. + /// + /// If you know the name of the graph you can use the method: + /// + /// GraphMask mask1 = GraphMask.FromGraphName("My Grid Graph"); + /// GraphMask mask2 = GraphMask.FromGraphName("My Other Grid Graph"); + /// + /// NNConstraint nn = NNConstraint.Default; + /// + /// nn.graphMask = mask1 | mask2; + /// + /// // Find the node closest to somePoint which is either in 'My Grid Graph' OR in 'My Other Grid Graph' + /// var info = AstarPath.active.GetNearest(somePoint, nn); + /// + /// + /// Some overloads of the methods take a graphMask parameter. If those overloads are used then they + /// will override the graph mask for that path request. + /// + /// [Open online documentation to see images] + /// + /// See: multiple-agent-types (view in online documentation for working links) + /// + [HideInInspector] + public GraphMask graphMask = GraphMask.everything; + + /// Used for serialization backwards compatibility + [UnityEngine.Serialization.FormerlySerializedAs("graphMask")] + int graphMaskCompatibility = -1; + + /// + /// Callback for when a path is completed. + /// Movement scripts should register to this delegate.\n + /// A temporary callback can also be set when calling StartPath, but that delegate will only be called for that path + /// + public OnPathDelegate pathCallback; + + /// Called before pathfinding is started + public OnPathDelegate preProcessPath; + + /// Called after a path has been calculated, right before modifiers are executed. + public OnPathDelegate postProcessPath; + + /// Used for drawing gizmos + [System.NonSerialized] + List lastCompletedVectorPath; + + /// Used for drawing gizmos + [System.NonSerialized] + List lastCompletedNodePath; + + /// The current path + [System.NonSerialized] + protected Path path; + + /// Previous path. Used to draw gizmos + [System.NonSerialized] + private Path prevPath; + + /// Cached delegate to avoid allocating one every time a path is started + private readonly OnPathDelegate onPathDelegate; + + /// Temporary callback only called for the current path. This value is set by the StartPath functions + private OnPathDelegate tmpPathCallback; + + /// The path ID of the last path queried + protected uint lastPathID; + + /// Internal list of all modifiers + readonly List modifiers = new List(); + + public enum ModifierPass { + PreProcess, + // An obsolete item occupied index 1 previously + PostProcess = 2, + } + + public Seeker () { + onPathDelegate = OnPathComplete; + } + + /// Initializes a few variables + protected override void Awake () { + base.Awake(); + startEndModifier.Awake(this); + } + + /// + /// Path that is currently being calculated or was last calculated. + /// You should rarely have to use this. Instead get the path when the path callback is called. + /// + /// See: pathCallback + /// + public Path GetCurrentPath () { + return path; + } + + /// + /// Stop calculating the current path request. + /// If this Seeker is currently calculating a path it will be canceled. + /// The callback (usually to a method named OnPathComplete) will soon be called + /// with a path that has the 'error' field set to true. + /// + /// This does not stop the character from moving, it just aborts + /// the path calculation. + /// + /// If true then the path will be pooled when the pathfinding system is done with it. + public void CancelCurrentPathRequest (bool pool = true) { + if (!IsDone()) { + path.FailWithError("Canceled by script (Seeker.CancelCurrentPathRequest)"); + if (pool) { + // Make sure the path has had its reference count incremented and decremented once. + // If this is not done the system will think no pooling is used at all and will not pool the path. + // The particular object that is used as the parameter (in this case 'path') doesn't matter at all + // it just has to be *some* object. + path.Claim(path); + path.Release(path); + } + } + } + + /// + /// Cleans up some variables. + /// Releases any eventually claimed paths. + /// Calls OnDestroy on the . + /// + /// See: + /// See: + /// + public void OnDestroy () { + ReleaseClaimedPath(); + startEndModifier.OnDestroy(this); + } + + /// + /// Releases the path used for gizmos (if any). + /// The seeker keeps the latest path claimed so it can draw gizmos. + /// In some cases this might not be desireable and you want it released. + /// In that case, you can call this method to release it (not that path gizmos will then not be drawn). + /// + /// If you didn't understand anything from the description above, you probably don't need to use this method. + /// + /// See: pooling (view in online documentation for working links) + /// + void ReleaseClaimedPath () { + if (prevPath != null) { + prevPath.Release(this, true); + prevPath = null; + } + } + + /// Called by modifiers to register themselves + public void RegisterModifier (IPathModifier modifier) { + modifiers.Add(modifier); + + // Sort the modifiers based on their specified order + modifiers.Sort((a, b) => a.Order.CompareTo(b.Order)); + } + + /// Called by modifiers when they are disabled or destroyed + public void DeregisterModifier (IPathModifier modifier) { + modifiers.Remove(modifier); + } + + /// + /// Post Processes the path. + /// This will run any modifiers attached to this GameObject on the path. + /// This is identical to calling RunModifiers(ModifierPass.PostProcess, path) + /// See: RunModifiers + /// \since Added in 3.2 + /// + public void PostProcess (Path path) { + RunModifiers(ModifierPass.PostProcess, path); + } + + /// Runs modifiers on a path + public void RunModifiers (ModifierPass pass, Path path) { + if (pass == ModifierPass.PreProcess) { + if (preProcessPath != null) preProcessPath(path); + + for (int i = 0; i < modifiers.Count; i++) modifiers[i].PreProcess(path); + } else if (pass == ModifierPass.PostProcess) { + Profiler.BeginSample("Running Path Modifiers"); + // Call delegates if they exist + if (postProcessPath != null) postProcessPath(path); + + // Loop through all modifiers and apply post processing + for (int i = 0; i < modifiers.Count; i++) modifiers[i].Apply(path); + Profiler.EndSample(); + } + } + + /// + /// Is the current path done calculating. + /// Returns true if the current has been returned or if the is null. + /// + /// Note: Do not confuse this with Pathfinding.Path.IsDone. They usually return the same value, but not always + /// since the path might be completely calculated, but it has not yet been processed by the Seeker. + /// + /// \since Added in 3.0.8 + /// Version: Behaviour changed in 3.2 + /// + public bool IsDone () { + return path == null || path.PipelineState >= PathState.Returned; + } + + /// + /// Called when a path has completed. + /// This should have been implemented as optional parameter values, but that didn't seem to work very well with delegates (the values weren't the default ones) + /// See: OnPathComplete(Path,bool,bool) + /// + void OnPathComplete (Path path) { + OnPathComplete(path, true, true); + } + + /// + /// Called when a path has completed. + /// Will post process it and return it by calling and + /// + void OnPathComplete (Path p, bool runModifiers, bool sendCallbacks) { + if (p != null && p != path && sendCallbacks) { + return; + } + + if (this == null || p == null || p != path) + return; + + if (!path.error && runModifiers) { + // This will send the path for post processing to modifiers attached to this Seeker + RunModifiers(ModifierPass.PostProcess, path); + } + + if (sendCallbacks) { + p.Claim(this); + + lastCompletedNodePath = p.path; + lastCompletedVectorPath = p.vectorPath; + + // This will send the path to the callback (if any) specified when calling StartPath + if (tmpPathCallback != null) { + tmpPathCallback(p); + } + + // This will send the path to any script which has registered to the callback + if (pathCallback != null) { + pathCallback(p); + } + + // Note: it is important that #prevPath is kept alive (i.e. not pooled) + // if we are drawing gizmos. + // It is also important that #path is kept alive since it can be returned + // from the GetCurrentPath method. + // Since #path will be copied to #prevPath it is sufficient that #prevPath + // is kept alive until it is replaced. + + // Recycle the previous path to reduce the load on the GC + if (prevPath != null) { + prevPath.Release(this, true); + } + + prevPath = p; + } + } + + + /// + /// Returns a new path instance. + /// The path will be taken from the path pool if path recycling is turned on.\n + /// This path can be sent to with no change, but if no change is required does just that. + /// + /// var seeker = GetComponent(); + /// Path p = seeker.GetNewPath (transform.position, transform.position+transform.forward*100); + /// // Disable heuristics on just this path for example + /// p.heuristic = Heuristic.None; + /// seeker.StartPath (p, OnPathComplete); + /// + /// Deprecated: Use ABPath.Construct(start, end, null) instead. + /// + [System.Obsolete("Use ABPath.Construct(start, end, null) instead")] + public ABPath GetNewPath (Vector3 start, Vector3 end) { + // Construct a path with start and end points + return ABPath.Construct(start, end, null); + } + + /// + /// Call this function to start calculating a path. + /// Since this method does not take a callback parameter, you should set the field before calling this method. + /// + /// The start point of the path + /// The end point of the path + public Path StartPath (Vector3 start, Vector3 end) { + return StartPath(start, end, null); + } + + /// + /// Call this function to start calculating a path. + /// + /// callback will be called when the path has completed. + /// Callback will not be called if the path is canceled (e.g when a new path is requested before the previous one has completed) + /// + /// The start point of the path + /// The end point of the path + /// The function to call when the path has been calculated + public Path StartPath (Vector3 start, Vector3 end, OnPathDelegate callback) { + return StartPath(ABPath.Construct(start, end, null), callback); + } + + /// + /// Call this function to start calculating a path. + /// + /// callback will be called when the path has completed. + /// Callback will not be called if the path is canceled (e.g when a new path is requested before the previous one has completed) + /// + /// The start point of the path + /// The end point of the path + /// The function to call when the path has been calculated + /// Mask used to specify which graphs should be searched for close nodes. See #Pathfinding.NNConstraint.graphMask. This will override #graphMask for this path request. + public Path StartPath (Vector3 start, Vector3 end, OnPathDelegate callback, GraphMask graphMask) { + return StartPath(ABPath.Construct(start, end, null), callback, graphMask); + } + + /// + /// Call this function to start calculating a path. + /// + /// The callback will be called when the path has been calculated (which may be several frames into the future). + /// The callback will not be called if a new path request is started before this path request has been calculated. + /// + /// Version: Since 3.8.3 this method works properly if a MultiTargetPath is used. + /// It now behaves identically to the StartMultiTargetPath(MultiTargetPath) method. + /// + /// Version: Since 4.1.x this method will no longer overwrite the graphMask on the path unless it is explicitly passed as a parameter (see other overloads of this method). + /// + /// The path to start calculating + /// The function to call when the path has been calculated + public Path StartPath (Path p, OnPathDelegate callback = null) { + // Set the graph mask only if the user has not changed it from the default value. + // This is not perfect as the user may have wanted it to be precisely -1 + // however it is the best detection that I can do. + // The non-default check is primarily for compatibility reasons to avoid breaking peoples existing code. + // The StartPath overloads with an explicit graphMask field should be used instead to set the graphMask. + if (p.nnConstraint.graphMask == -1) p.nnConstraint.graphMask = graphMask; + StartPathInternal(p, callback); + return p; + } + + /// + /// Call this function to start calculating a path. + /// + /// The callback will be called when the path has been calculated (which may be several frames into the future). + /// The callback will not be called if a new path request is started before this path request has been calculated. + /// + /// Version: Since 3.8.3 this method works properly if a MultiTargetPath is used. + /// It now behaves identically to the StartMultiTargetPath(MultiTargetPath) method. + /// + /// The path to start calculating + /// The function to call when the path has been calculated + /// Mask used to specify which graphs should be searched for close nodes. See #Pathfinding.GraphMask. This will override #graphMask for this path request. + public Path StartPath (Path p, OnPathDelegate callback, GraphMask graphMask) { + p.nnConstraint.graphMask = graphMask; + StartPathInternal(p, callback); + return p; + } + + /// Internal method to start a path and mark it as the currently active path + void StartPathInternal (Path p, OnPathDelegate callback) { + p.callback += onPathDelegate; + + p.enabledTags = traversableTags; + p.tagPenalties = tagPenalties; + + // Cancel a previously requested path is it has not been processed yet and also make sure that it has not been recycled and used somewhere else + if (path != null && path.PipelineState <= PathState.Processing && path.CompleteState != PathCompleteState.Error && lastPathID == path.pathID) { + path.FailWithError("Canceled path because a new one was requested.\n"+ + "This happens when a new path is requested from the seeker when one was already being calculated.\n" + + "For example if a unit got a new order, you might request a new path directly instead of waiting for the now" + + " invalid path to be calculated. Which is probably what you want.\n" + + "If you are getting this a lot, you might want to consider how you are scheduling path requests."); + // No callback will be sent for the canceled path + } + + // Set p as the active path + path = p; + tmpPathCallback = callback; + + // Save the path id so we can make sure that if we cancel a path (see above) it should not have been recycled yet. + lastPathID = path.pathID; + + // Pre process the path + RunModifiers(ModifierPass.PreProcess, path); + + // Send the request to the pathfinder + AstarPath.StartPath(path); + } + + + /// Draws gizmos for the Seeker + public void OnDrawGizmos () { + if (lastCompletedNodePath == null || !drawGizmos) { + return; + } + + if (detailedGizmos) { + Gizmos.color = new Color(0.7F, 0.5F, 0.1F, 0.5F); + + if (lastCompletedNodePath != null) { + for (int i = 0; i < lastCompletedNodePath.Count-1; i++) { + Gizmos.DrawLine((Vector3)lastCompletedNodePath[i].position, (Vector3)lastCompletedNodePath[i+1].position); + } + } + } + + Gizmos.color = new Color(0, 1F, 0, 1F); + + if (lastCompletedVectorPath != null) { + for (int i = 0; i < lastCompletedVectorPath.Count-1; i++) { + Gizmos.DrawLine(lastCompletedVectorPath[i], lastCompletedVectorPath[i+1]); + } + } + } + + protected override int OnUpgradeSerializedData (int version, bool unityThread) { + if (graphMaskCompatibility != -1) { + Debug.Log("Loaded " + graphMaskCompatibility + " " + graphMask.value); + graphMask = graphMaskCompatibility; + graphMaskCompatibility = -1; + } + return base.OnUpgradeSerializedData(version, unityThread); + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/Seeker.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/Seeker.cs.meta new file mode 100644 index 0000000..5605e82 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/Seeker.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 373b52eb9bf8c40f785bb6947a1aee66 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/TurnBasedAI.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/TurnBasedAI.cs new file mode 100644 index 0000000..a79c7d7 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/TurnBasedAI.cs @@ -0,0 +1,25 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Pathfinding.Examples { + /// Helper script in the example scene 'Turn Based' + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_examples_1_1_turn_based_a_i.php")] + public class TurnBasedAI : VersionedMonoBehaviour { + public int movementPoints = 2; + public BlockManager blockManager; + public SingleNodeBlocker blocker; + public GraphNode targetNode; + public BlockManager.TraversalProvider traversalProvider; + + void Start () { + blocker.BlockAtCurrentPosition(); + } + + protected override void Awake () { + base.Awake(); + // Set the traversal provider to block all nodes that are blocked by a SingleNodeBlocker + // except the SingleNodeBlocker owned by this AI (we don't want to be blocked by ourself) + traversalProvider = new BlockManager.TraversalProvider(blockManager, BlockManager.BlockMode.AllExceptSelector, new List() { blocker }); + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AI/TurnBasedAI.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/TurnBasedAI.cs.meta new file mode 100644 index 0000000..e24964a --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AI/TurnBasedAI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8f95b80c439d6408b9afac9d013922e4 +timeCreated: 1453035991 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AstarData.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarData.cs new file mode 100644 index 0000000..3880e1b --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarData.cs @@ -0,0 +1,732 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using Pathfinding.WindowsStore; +#if UNITY_WINRT && !UNITY_EDITOR +//using MarkerMetro.Unity.WinLegacy.IO; +//using MarkerMetro.Unity.WinLegacy.Reflection; +#endif + +namespace Pathfinding { + [System.Serializable] + /// + /// Stores the navigation graphs for the A* Pathfinding System. + /// \ingroup relevant + /// + /// An instance of this class is assigned to AstarPath.data, from it you can access all graphs loaded through the variable.\n + /// This class also handles a lot of the high level serialization. + /// + public class AstarData { + /// Shortcut to AstarPath.active + public static AstarPath active { + get { + return AstarPath.active; + } + } + + #region Fields + /// + /// Shortcut to the first NavMeshGraph. + /// Updated at scanning time + /// + public NavMeshGraph navmesh { get; private set; } + +#if !ASTAR_NO_GRID_GRAPH + /// + /// Shortcut to the first GridGraph. + /// Updated at scanning time + /// + public GridGraph gridGraph { get; private set; } +#endif + +#if !ASTAR_NO_POINT_GRAPH + /// + /// Shortcut to the first PointGraph. + /// Updated at scanning time + /// + public PointGraph pointGraph { get; private set; } +#endif + + + /// + /// All supported graph types. + /// Populated through reflection search + /// + public System.Type[] graphTypes { get; private set; } + +#if ASTAR_FAST_NO_EXCEPTIONS || UNITY_WINRT || UNITY_WEBGL + /// + /// Graph types to use when building with Fast But No Exceptions for iPhone. + /// If you add any custom graph types, you need to add them to this hard-coded list. + /// + public static readonly System.Type[] DefaultGraphTypes = new System.Type[] { +#if !ASTAR_NO_GRID_GRAPH + typeof(GridGraph), +#endif +#if !ASTAR_NO_POINT_GRAPH + typeof(PointGraph), +#endif + typeof(NavMeshGraph), + }; +#endif + + /// + /// All graphs this instance holds. + /// This will be filled only after deserialization has completed. + /// May contain null entries if graph have been removed. + /// + [System.NonSerialized] + public NavGraph[] graphs = new NavGraph[0]; + + //Serialization Settings + + /// + /// Serialized data for all graphs and settings. + /// Stored as a base64 encoded string because otherwise Unity's Undo system would sometimes corrupt the byte data (because it only stores deltas). + /// + /// This can be accessed as a byte array from the property. + /// + /// \since 3.6.1 + /// + [SerializeField] + string dataString; + + /// + /// Data from versions from before 3.6.1. + /// Used for handling upgrades + /// \since 3.6.1 + /// + [SerializeField] + [UnityEngine.Serialization.FormerlySerializedAs("data")] + private byte[] upgradeData; + + /// Serialized data for all graphs and settings + private byte[] data { + get { + // Handle upgrading from earlier versions than 3.6.1 + if (upgradeData != null && upgradeData.Length > 0) { + data = upgradeData; + upgradeData = null; + } + return dataString != null? System.Convert.FromBase64String (dataString) : null; + } + set { + dataString = value != null? System.Convert.ToBase64String (value) : null; + } + } + + /// + /// Serialized data for cached startup. + /// If set, on start the graphs will be deserialized from this file. + /// + public TextAsset file_cachedStartup; + + /// + /// Serialized data for cached startup. + /// + /// Deprecated: Deprecated since 3.6, AstarData.file_cachedStartup is now used instead + /// + public byte[] data_cachedStartup; + + /// + /// Should graph-data be cached. + /// Caching the startup means saving the whole graphs - not only the settings - to a file ( which can + /// be loaded when the game starts. This is usually much faster than scanning the graphs when the game starts. This is configured from the editor under the "Save & Load" tab. + /// + /// See: save-load-graphs (view in online documentation for working links) + /// + [SerializeField] + public bool cacheStartup; + + //End Serialization Settings + + List graphStructureLocked = new List(); + + #endregion + + public byte[] GetData () { + return data; + } + + public void SetData (byte[] data) { + this.data = data; + } + + /// Loads the graphs from memory, will load cached graphs if any exists + public void Awake () { + graphs = new NavGraph[0]; + + if (cacheStartup && file_cachedStartup != null) { + LoadFromCache(); + } else { + DeserializeGraphs(); + } + } + + /// + /// Prevent the graph structure from changing during the time this lock is held. + /// This prevents graphs from being added or removed and also prevents graphs from being serialized or deserialized. + /// This is used when e.g an async scan is happening to ensure that for example a graph that is being scanned is not destroyed. + /// + /// Each call to this method *must* be paired with exactly one call to . + /// The calls may be nested. + /// + internal void LockGraphStructure (bool allowAddingGraphs = false) { + graphStructureLocked.Add(allowAddingGraphs); + } + + /// + /// Allows the graph structure to change again. + /// See: + /// + internal void UnlockGraphStructure () { + if (graphStructureLocked.Count == 0) throw new System.InvalidOperationException(); + graphStructureLocked.RemoveAt(graphStructureLocked.Count - 1); + } + + PathProcessor.GraphUpdateLock AssertSafe (bool onlyAddingGraph = false) { + if (graphStructureLocked.Count > 0) { + bool allowAdding = true; + for (int i = 0; i < graphStructureLocked.Count; i++) allowAdding &= graphStructureLocked[i]; + if (!(onlyAddingGraph && allowAdding)) throw new System.InvalidOperationException("Graphs cannot be added, removed or serialized while the graph structure is locked. This is the case when a graph is currently being scanned and when executing graph updates and work items.\nHowever as a special case, graphs can be added inside work items."); + } + + // Pause the pathfinding threads + var graphLock = active.PausePathfinding(); + if (!active.IsInsideWorkItem) { + // Make sure all graph updates and other callbacks are done + // Only do this if this code is not being called from a work item itself as that would cause a recursive wait that could never complete. + // There are some valid cases when this can happen. For example it may be necessary to add a new graph inside a work item. + active.FlushWorkItems(); + + // Paths that are already calculated and waiting to be returned to the Seeker component need to be + // processed immediately as their results usually depend on graphs that currently exist. If this was + // not done then after destroying a graph one could get a path result with destroyed nodes in it. + active.pathReturnQueue.ReturnPaths(false); + } + return graphLock; + } + + /// + /// Calls the callback with every node in all graphs. + /// This is the easiest way to iterate through every existing node. + /// + /// + /// AstarPath.active.data.GetNodes(node => { + /// Debug.Log("I found a node at position " + (Vector3)node.position); + /// }); + /// + /// + /// See: for getting the nodes of a single graph instead of all. + /// See: graph-updates (view in online documentation for working links) + /// + public void GetNodes (System.Action callback) { + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] != null) graphs[i].GetNodes(callback); + } + } + + /// + /// Updates shortcuts to the first graph of different types. + /// Hard coding references to some graph types is not really a good thing imo. I want to keep it dynamic and flexible. + /// But these references ease the use of the system, so I decided to keep them. + /// + public void UpdateShortcuts () { + navmesh = (NavMeshGraph)FindGraphOfType(typeof(NavMeshGraph)); + +#if !ASTAR_NO_GRID_GRAPH + gridGraph = (GridGraph)FindGraphOfType(typeof(GridGraph)); +#endif + +#if !ASTAR_NO_POINT_GRAPH + pointGraph = (PointGraph)FindGraphOfType(typeof(PointGraph)); +#endif + } + + /// Load from data from + public void LoadFromCache () { + var graphLock = AssertSafe(); + + if (file_cachedStartup != null) { + var bytes = file_cachedStartup.bytes; + DeserializeGraphs(bytes); + + GraphModifier.TriggerEvent(GraphModifier.EventType.PostCacheLoad); + } else { + Debug.LogError("Can't load from cache since the cache is empty"); + } + graphLock.Release(); + } + + #region Serialization + + /// + /// Serializes all graphs settings to a byte array. + /// See: DeserializeGraphs(byte[]) + /// + public byte[] SerializeGraphs () { + return SerializeGraphs(Pathfinding.Serialization.SerializeSettings.Settings); + } + + /// + /// Serializes all graphs settings and optionally node data to a byte array. + /// See: DeserializeGraphs(byte[]) + /// See: Pathfinding.Serialization.SerializeSettings + /// + public byte[] SerializeGraphs (Pathfinding.Serialization.SerializeSettings settings) { + uint checksum; + + return SerializeGraphs(settings, out checksum); + } + + /// + /// Main serializer function. + /// Serializes all graphs to a byte array + /// A similar function exists in the AstarPathEditor.cs script to save additional info + /// + public byte[] SerializeGraphs (Pathfinding.Serialization.SerializeSettings settings, out uint checksum) { + var graphLock = AssertSafe(); + var sr = new Pathfinding.Serialization.AstarSerializer(this, settings); + + sr.OpenSerialize(); + sr.SerializeGraphs(graphs); + sr.SerializeExtraInfo(); + byte[] bytes = sr.CloseSerialize(); + checksum = sr.GetChecksum(); +#if ASTARDEBUG + Debug.Log("Got a whole bunch of data, "+bytes.Length+" bytes"); +#endif + graphLock.Release(); + return bytes; + } + + /// Deserializes graphs from + public void DeserializeGraphs () { + if (data != null) { + DeserializeGraphs(data); + } + } + + /// Destroys all graphs and sets graphs to null + void ClearGraphs () { + if (graphs == null) return; + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] != null) { + ((IGraphInternals)graphs[i]).OnDestroy(); + graphs[i].active = null; + } + } + graphs = null; + UpdateShortcuts(); + } + + public void OnDestroy () { + ClearGraphs(); + } + + /// + /// Deserializes graphs from the specified byte array. + /// An error will be logged if deserialization fails. + /// + public void DeserializeGraphs (byte[] bytes) { + var graphLock = AssertSafe(); + + ClearGraphs(); + DeserializeGraphsAdditive(bytes); + graphLock.Release(); + } + + /// + /// Deserializes graphs from the specified byte array additively. + /// An error will be logged if deserialization fails. + /// This function will add loaded graphs to the current ones. + /// + public void DeserializeGraphsAdditive (byte[] bytes) { + var graphLock = AssertSafe(); + + try { + if (bytes != null) { + var sr = new Pathfinding.Serialization.AstarSerializer(this); + + if (sr.OpenDeserialize(bytes)) { + DeserializeGraphsPartAdditive(sr); + sr.CloseDeserialize(); + } else { + Debug.Log("Invalid data file (cannot read zip).\nThe data is either corrupt or it was saved using a 3.0.x or earlier version of the system"); + } + } else { + throw new System.ArgumentNullException("bytes"); + } + active.VerifyIntegrity(); + } catch (System.Exception e) { + Debug.LogError("Caught exception while deserializing data.\n"+e); + graphs = new NavGraph[0]; + } + + UpdateShortcuts(); + graphLock.Release(); + } + + /// Helper function for deserializing graphs + void DeserializeGraphsPartAdditive (Pathfinding.Serialization.AstarSerializer sr) { + if (graphs == null) graphs = new NavGraph[0]; + + var gr = new List(graphs); + + // Set an offset so that the deserializer will load + // the graphs with the correct graph indexes + sr.SetGraphIndexOffset(gr.Count); + + if (graphTypes == null) FindGraphTypes(); + gr.AddRange(sr.DeserializeGraphs(graphTypes)); + graphs = gr.ToArray(); + + sr.DeserializeEditorSettingsCompatibility(); + sr.DeserializeExtraInfo(); + + //Assign correct graph indices. + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] == null) continue; + graphs[i].GetNodes(node => node.GraphIndex = (uint)i); + } + + for (int i = 0; i < graphs.Length; i++) { + for (int j = i+1; j < graphs.Length; j++) { + if (graphs[i] != null && graphs[j] != null && graphs[i].guid == graphs[j].guid) { + Debug.LogWarning("Guid Conflict when importing graphs additively. Imported graph will get a new Guid.\nThis message is (relatively) harmless."); + graphs[i].guid = Pathfinding.Util.Guid.NewGuid(); + break; + } + } + } + + sr.PostDeserialization(); + active.hierarchicalGraph.RecalculateIfNecessary(); + } + + #endregion + + /// + /// Find all graph types supported in this build. + /// Using reflection, the assembly is searched for types which inherit from NavGraph. + /// + public void FindGraphTypes () { +#if !ASTAR_FAST_NO_EXCEPTIONS && !UNITY_WINRT && !UNITY_WEBGL + var graphList = new List(); + foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies()) { + System.Type[] types = null; + try { + types = assembly.GetTypes(); + } catch { + // Ignore type load exceptions and things like that. + // We might not be able to read all assemblies for some reason, but hopefully the relevant types exist in the assemblies that we can read + continue; + } + + foreach (var type in types) { +#if NETFX_CORE && !UNITY_EDITOR + System.Type baseType = type.GetTypeInfo().BaseType; +#else + var baseType = type.BaseType; +#endif + while (baseType != null) { + if (System.Type.Equals(baseType, typeof(NavGraph))) { + graphList.Add(type); + + break; + } + +#if NETFX_CORE && !UNITY_EDITOR + baseType = baseType.GetTypeInfo().BaseType; +#else + baseType = baseType.BaseType; +#endif + } + } + } + + graphTypes = graphList.ToArray(); + +#if ASTARDEBUG + Debug.Log("Found "+graphTypes.Length+" graph types"); +#endif +#else + graphTypes = DefaultGraphTypes; +#endif + } + + #region GraphCreation + /// + /// Returns: A System.Type which matches the specified type string. If no mathing graph type was found, null is returned + /// + /// Deprecated: + /// + [System.Obsolete("If really necessary. Use System.Type.GetType instead.")] + public System.Type GetGraphType (string type) { + for (int i = 0; i < graphTypes.Length; i++) { + if (graphTypes[i].Name == type) { + return graphTypes[i]; + } + } + return null; + } + + /// + /// Creates a new instance of a graph of type type. If no matching graph type was found, an error is logged and null is returned + /// Returns: The created graph + /// See: + /// + /// Deprecated: + /// + [System.Obsolete("Use CreateGraph(System.Type) instead")] + public NavGraph CreateGraph (string type) { + Debug.Log("Creating Graph of type '"+type+"'"); + + for (int i = 0; i < graphTypes.Length; i++) { + if (graphTypes[i].Name == type) { + return CreateGraph(graphTypes[i]); + } + } + Debug.LogError("Graph type ("+type+") wasn't found"); + return null; + } + + /// + /// Creates a new graph instance of type type + /// See: + /// + internal NavGraph CreateGraph (System.Type type) { + var graph = System.Activator.CreateInstance(type) as NavGraph; + + graph.active = active; + return graph; + } + + /// + /// Adds a graph of type type to the array + /// + /// Deprecated: + /// + [System.Obsolete("Use AddGraph(System.Type) instead")] + public NavGraph AddGraph (string type) { + NavGraph graph = null; + + for (int i = 0; i < graphTypes.Length; i++) { + if (graphTypes[i].Name == type) { + graph = CreateGraph(graphTypes[i]); + } + } + + if (graph == null) { + Debug.LogError("No NavGraph of type '"+type+"' could be found"); + return null; + } + + AddGraph(graph); + + return graph; + } + + /// + /// Adds a graph of type type to the array. + /// See: runtime-graphs (view in online documentation for working links) + /// + public NavGraph AddGraph (System.Type type) { + NavGraph graph = null; + + for (int i = 0; i < graphTypes.Length; i++) { + if (System.Type.Equals(graphTypes[i], type)) { + graph = CreateGraph(graphTypes[i]); + } + } + + if (graph == null) { + Debug.LogError("No NavGraph of type '"+type+"' could be found, "+graphTypes.Length+" graph types are avaliable"); + return null; + } + + AddGraph(graph); + + return graph; + } + + /// Adds the specified graph to the array + void AddGraph (NavGraph graph) { + // Make sure to not interfere with pathfinding + var graphLock = AssertSafe(true); + + // Try to fill in an empty position + bool foundEmpty = false; + + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] == null) { + graphs[i] = graph; + graph.graphIndex = (uint)i; + foundEmpty = true; + break; + } + } + + if (!foundEmpty) { + if (graphs != null && graphs.Length >= GraphNode.MaxGraphIndex) { + throw new System.Exception("Graph Count Limit Reached. You cannot have more than " + GraphNode.MaxGraphIndex + " graphs."); + } + + // Add a new entry to the list + var graphList = new List(graphs ?? new NavGraph[0]); + graphList.Add(graph); + graphs = graphList.ToArray(); + graph.graphIndex = (uint)(graphs.Length-1); + } + + UpdateShortcuts(); + graph.active = active; + graphLock.Release(); + } + + /// + /// Removes the specified graph from the array and Destroys it in a safe manner. + /// To avoid changing graph indices for the other graphs, the graph is simply nulled in the array instead + /// of actually removing it from the array. + /// The empty position will be reused if a new graph is added. + /// + /// Returns: True if the graph was sucessfully removed (i.e it did exist in the array). False otherwise. + /// + /// Version: Changed in 3.2.5 to call SafeOnDestroy before removing + /// and nulling it in the array instead of removing the element completely in the array. + /// + public bool RemoveGraph (NavGraph graph) { + // Make sure the pathfinding threads are stopped + // If we don't wait until pathfinding that is potentially running on + // this graph right now we could end up with NullReferenceExceptions + var graphLock = AssertSafe(); + + ((IGraphInternals)graph).OnDestroy(); + graph.active = null; + + int i = System.Array.IndexOf(graphs, graph); + if (i != -1) graphs[i] = null; + + UpdateShortcuts(); + graphLock.Release(); + return i != -1; + } + + #endregion + + #region GraphUtility + + /// + /// Returns the graph which contains the specified node. + /// The graph must be in the array. + /// + /// Returns: Returns the graph which contains the node. Null if the graph wasn't found + /// + public static NavGraph GetGraph (GraphNode node) { + if (node == null) return null; + + AstarPath script = AstarPath.active; + if (script == null) return null; + + AstarData data = script.data; + if (data == null || data.graphs == null) return null; + + uint graphIndex = node.GraphIndex; + + if (graphIndex >= data.graphs.Length) { + return null; + } + + return data.graphs[(int)graphIndex]; + } + + /// Returns the first graph which satisfies the predicate. Returns null if no graph was found. + public NavGraph FindGraph (System.Func predicate) { + if (graphs != null) { + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] != null && predicate(graphs[i])) { + return graphs[i]; + } + } + } + return null; + } + + /// Returns the first graph of type type found in the array. Returns null if no graph was found. + public NavGraph FindGraphOfType (System.Type type) { + return FindGraph(graph => System.Type.Equals(graph.GetType(), type)); + } + + /// Returns the first graph which inherits from the type type. Returns null if no graph was found. + public NavGraph FindGraphWhichInheritsFrom (System.Type type) { + return FindGraph(graph => WindowsStoreCompatibility.GetTypeInfo(type).IsAssignableFrom(WindowsStoreCompatibility.GetTypeInfo(graph.GetType()))); + } + + /// + /// Loop through this function to get all graphs of type 'type' + /// + /// foreach (GridGraph graph in AstarPath.data.FindGraphsOfType (typeof(GridGraph))) { + /// //Do something with the graph + /// } + /// + /// See: AstarPath.RegisterSafeNodeUpdate + /// + public IEnumerable FindGraphsOfType (System.Type type) { + if (graphs == null) yield break; + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] != null && System.Type.Equals(graphs[i].GetType(), type)) { + yield return graphs[i]; + } + } + } + + /// + /// All graphs which implements the UpdateableGraph interface + /// foreach (IUpdatableGraph graph in AstarPath.data.GetUpdateableGraphs ()) { + /// //Do something with the graph + /// } + /// See: AstarPath.AddWorkItem + /// See: Pathfinding.IUpdatableGraph + /// + public IEnumerable GetUpdateableGraphs () { + if (graphs == null) yield break; + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] is IUpdatableGraph) { + yield return graphs[i]; + } + } + } + + /// + /// All graphs which implements the UpdateableGraph interface + /// foreach (IRaycastableGraph graph in AstarPath.data.GetRaycastableGraphs ()) { + /// //Do something with the graph + /// } + /// See: Pathfinding.IRaycastableGraph + /// Deprecated: Deprecated because it is not used by the package internally and the use cases are few. Iterate through the array instead. + /// + [System.Obsolete("Obsolete because it is not used by the package internally and the use cases are few. Iterate through the graphs array instead.")] + public IEnumerable GetRaycastableGraphs () { + if (graphs == null) yield break; + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] is IRaycastableGraph) { + yield return graphs[i]; + } + } + } + + /// Gets the index of the NavGraph in the array + public int GetGraphIndex (NavGraph graph) { + if (graph == null) throw new System.ArgumentNullException("graph"); + + var index = -1; + if (graphs != null) { + index = System.Array.IndexOf(graphs, graph); + if (index == -1) Debug.LogError("Graph doesn't exist"); + } + return index; + } + + #endregion + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AstarData.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarData.cs.meta new file mode 100644 index 0000000..eb5a46f --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarData.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 38d211caa07cb44ef886481aa1cf755c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AstarMath.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarMath.cs new file mode 100644 index 0000000..c0d618f --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarMath.cs @@ -0,0 +1,1528 @@ +using UnityEngine; +using System.Collections.Generic; +using System; + +namespace Pathfinding { + using Pathfinding.Util; + + /// + /// Contains various spline functions. + /// \ingroup utils + /// + static class AstarSplines { + public static Vector3 CatmullRom (Vector3 previous, Vector3 start, Vector3 end, Vector3 next, float elapsedTime) { + // References used: + // p.266 GemsV1 + // + // tension is often set to 0.5 but you can use any reasonable value: + // http://www.cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf + // + // bias and tension controls: + // http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/interpolation/ + + float percentComplete = elapsedTime; + float percentCompleteSquared = percentComplete * percentComplete; + float percentCompleteCubed = percentCompleteSquared * percentComplete; + + return + previous * (-0.5F*percentCompleteCubed + + percentCompleteSquared - + 0.5F*percentComplete) + + + start * + (1.5F*percentCompleteCubed + + -2.5F*percentCompleteSquared + 1.0F) + + + end * + (-1.5F*percentCompleteCubed + + 2.0F*percentCompleteSquared + + 0.5F*percentComplete) + + + next * + (0.5F*percentCompleteCubed - + 0.5F*percentCompleteSquared); + } + + /// Returns a point on a cubic bezier curve. t is clamped between 0 and 1 + public static Vector3 CubicBezier (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { + t = Mathf.Clamp01(t); + float t2 = 1-t; + return t2*t2*t2 * p0 + 3 * t2*t2 * t * p1 + 3 * t2 * t*t * p2 + t*t*t * p3; + } + + /// Returns the derivative for a point on a cubic bezier curve. t is clamped between 0 and 1 + public static Vector3 CubicBezierDerivative (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { + t = Mathf.Clamp01(t); + float t2 = 1-t; + return 3*t2*t2*(p1-p0) + 6*t2*t*(p2 - p1) + 3*t*t*(p3 - p2); + } + + /// Returns the second derivative for a point on a cubic bezier curve. t is clamped between 0 and 1 + public static Vector3 CubicBezierSecondDerivative (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) { + t = Mathf.Clamp01(t); + float t2 = 1-t; + return 6*t2*(p2 - 2*p1 + p0) + 6*t*(p3 - 2*p2 + p1); + } + } + + /// + /// Various vector math utility functions. + /// Version: A lot of functions in the Polygon class have been moved to this class + /// the names have changed slightly and everything now consistently assumes a left handed + /// coordinate system now instead of sometimes using a left handed one and sometimes + /// using a right handed one. This is why the 'Left' methods in the Polygon class redirect + /// to methods named 'Right'. The functionality is exactly the same. + /// + /// Note the difference between segments and lines. Lines are infinitely + /// long but segments have only a finite length. + /// + /// \ingroup utils + /// + public static class VectorMath { + /// + /// Complex number multiplication. + /// Returns: a * b + /// + /// Used to rotate vectors in an efficient way. + /// + /// See: https://en.wikipedia.org/wiki/Complex_number + /// + public static Vector2 ComplexMultiply (Vector2 a, Vector2 b) { + return new Vector2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); + } + + /// + /// Complex number multiplication. + /// Returns: a * conjugate(b) + /// + /// Used to rotate vectors in an efficient way. + /// + /// See: https://en.wikipedia.org/wiki/Complex_number + /// See: https://en.wikipedia.org/wiki/Complex_conjugate + /// + public static Vector2 ComplexMultiplyConjugate (Vector2 a, Vector2 b) { + return new Vector2(a.x * b.x + a.y * b.y, a.y * b.x - a.x * b.y); + } + + /// + /// Returns the closest point on the line. + /// The line is treated as infinite. + /// See: ClosestPointOnSegment + /// See: ClosestPointOnLineFactor + /// + public static Vector3 ClosestPointOnLine (Vector3 lineStart, Vector3 lineEnd, Vector3 point) { + Vector3 lineDirection = Vector3.Normalize(lineEnd - lineStart); + float dot = Vector3.Dot(point - lineStart, lineDirection); + + return lineStart + (dot*lineDirection); + } + + /// + /// Factor along the line which is closest to the point. + /// Returned value is in the range [0,1] if the point lies on the segment otherwise it just lies on the line. + /// The closest point can be calculated using (end-start)*factor + start. + /// + /// See: ClosestPointOnLine + /// See: ClosestPointOnSegment + /// + public static float ClosestPointOnLineFactor (Vector3 lineStart, Vector3 lineEnd, Vector3 point) { + var dir = lineEnd - lineStart; + float sqrMagn = dir.sqrMagnitude; + + if (sqrMagn <= 0.000001) return 0; + + return Vector3.Dot(point - lineStart, dir) / sqrMagn; + } + + /// + /// Factor along the line which is closest to the point. + /// Returned value is in the range [0,1] if the point lies on the segment otherwise it just lies on the line. + /// The closest point can be calculated using (end-start)*factor + start + /// + public static float ClosestPointOnLineFactor (Int3 lineStart, Int3 lineEnd, Int3 point) { + var lineDirection = lineEnd - lineStart; + float magn = lineDirection.sqrMagnitude; + + float closestPoint = Int3.Dot((point - lineStart), lineDirection); + + if (magn != 0) closestPoint /= magn; + + return closestPoint; + } + + /// + /// Factor of the nearest point on the segment. + /// Returned value is in the range [0,1] if the point lies on the segment otherwise it just lies on the line. + /// The closest point can be calculated using (end-start)*factor + start; + /// + public static float ClosestPointOnLineFactor (Int2 lineStart, Int2 lineEnd, Int2 point) { + var lineDirection = lineEnd - lineStart; + double magn = lineDirection.sqrMagnitudeLong; + + double closestPoint = Int2.DotLong(point - lineStart, lineDirection); + + if (magn != 0) closestPoint /= magn; + + return (float)closestPoint; + } + + /// + /// Returns the closest point on the segment. + /// The segment is NOT treated as infinite. + /// See: ClosestPointOnLine + /// See: ClosestPointOnSegmentXZ + /// + public static Vector3 ClosestPointOnSegment (Vector3 lineStart, Vector3 lineEnd, Vector3 point) { + var dir = lineEnd - lineStart; + float sqrMagn = dir.sqrMagnitude; + + if (sqrMagn <= 0.000001) return lineStart; + + float factor = Vector3.Dot(point - lineStart, dir) / sqrMagn; + return lineStart + Mathf.Clamp01(factor)*dir; + } + + /// + /// Returns the closest point on the segment in the XZ plane. + /// The y coordinate of the result will be the same as the y coordinate of the point parameter. + /// + /// The segment is NOT treated as infinite. + /// See: ClosestPointOnSegment + /// See: ClosestPointOnLine + /// + public static Vector3 ClosestPointOnSegmentXZ (Vector3 lineStart, Vector3 lineEnd, Vector3 point) { + lineStart.y = point.y; + lineEnd.y = point.y; + Vector3 fullDirection = lineEnd-lineStart; + Vector3 fullDirection2 = fullDirection; + fullDirection2.y = 0; + float magn = fullDirection2.magnitude; + Vector3 lineDirection = magn > float.Epsilon ? fullDirection2/magn : Vector3.zero; + + float closestPoint = Vector3.Dot((point-lineStart), lineDirection); + return lineStart+(Mathf.Clamp(closestPoint, 0.0f, fullDirection2.magnitude)*lineDirection); + } + + /// + /// Returns the approximate shortest squared distance between x,z and the segment p-q. + /// The segment is not considered infinite. + /// This function is not entirely exact, but it is about twice as fast as DistancePointSegment2. + /// TODO: Is this actually approximate? It looks exact. + /// + public static float SqrDistancePointSegmentApproximate (int x, int z, int px, int pz, int qx, int qz) { + float pqx = (float)(qx - px); + float pqz = (float)(qz - pz); + float dx = (float)(x - px); + float dz = (float)(z - pz); + float d = pqx*pqx + pqz*pqz; + float t = pqx*dx + pqz*dz; + + if (d > 0) + t /= d; + if (t < 0) + t = 0; + else if (t > 1) + t = 1; + + dx = px + t*pqx - x; + dz = pz + t*pqz - z; + + return dx*dx + dz*dz; + } + + /// + /// Returns the approximate shortest squared distance between x,z and the segment p-q. + /// The segment is not considered infinite. + /// This function is not entirely exact, but it is about twice as fast as DistancePointSegment2. + /// TODO: Is this actually approximate? It looks exact. + /// + public static float SqrDistancePointSegmentApproximate (Int3 a, Int3 b, Int3 p) { + float pqx = (float)(b.x - a.x); + float pqz = (float)(b.z - a.z); + float dx = (float)(p.x - a.x); + float dz = (float)(p.z - a.z); + float d = pqx*pqx + pqz*pqz; + float t = pqx*dx + pqz*dz; + + if (d > 0) + t /= d; + if (t < 0) + t = 0; + else if (t > 1) + t = 1; + + dx = a.x + t*pqx - p.x; + dz = a.z + t*pqz - p.z; + + return dx*dx + dz*dz; + } + + /// + /// Returns the squared distance between p and the segment a-b. + /// The line is not considered infinite. + /// + public static float SqrDistancePointSegment (Vector3 a, Vector3 b, Vector3 p) { + var nearest = ClosestPointOnSegment(a, b, p); + + return (nearest-p).sqrMagnitude; + } + + /// + /// 3D minimum distance between 2 segments. + /// Input: two 3D line segments S1 and S2 + /// Returns: the shortest squared distance between S1 and S2 + /// + public static float SqrDistanceSegmentSegment (Vector3 s1, Vector3 e1, Vector3 s2, Vector3 e2) { + Vector3 u = e1 - s1; + Vector3 v = e2 - s2; + Vector3 w = s1 - s2; + float a = Vector3.Dot(u, u); // always >= 0 + float b = Vector3.Dot(u, v); + float c = Vector3.Dot(v, v); // always >= 0 + float d = Vector3.Dot(u, w); + float e = Vector3.Dot(v, w); + float D = a*c - b*b; // always >= 0 + float sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0 + float tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0 + + // compute the line parameters of the two closest points + if (D < 0.000001f) { // the lines are almost parallel + sN = 0.0f; // force using point P0 on segment S1 + sD = 1.0f; // to prevent possible division by 0.0 later + tN = e; + tD = c; + } else { // get the closest points on the infinite lines + sN = (b*e - c*d); + tN = (a*e - b*d); + if (sN < 0.0f) { // sc < 0 => the s=0 edge is visible + sN = 0.0f; + tN = e; + tD = c; + } else if (sN > sD) { // sc > 1 => the s=1 edge is visible + sN = sD; + tN = e + b; + tD = c; + } + } + + if (tN < 0.0f) { // tc < 0 => the t=0 edge is visible + tN = 0.0f; + // recompute sc for this edge + if (-d < 0.0f) + sN = 0.0f; + else if (-d > a) + sN = sD; + else { + sN = -d; + sD = a; + } + } else if (tN > tD) { // tc > 1 => the t=1 edge is visible + tN = tD; + // recompute sc for this edge + if ((-d + b) < 0.0f) + sN = 0; + else if ((-d + b) > a) + sN = sD; + else { + sN = (-d + b); + sD = a; + } + } + // finally do the division to get sc and tc + sc = (Math.Abs(sN) < 0.000001f ? 0.0f : sN / sD); + tc = (Math.Abs(tN) < 0.000001f ? 0.0f : tN / tD); + + // get the difference of the two closest points + Vector3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc) + + return dP.sqrMagnitude; // return the closest distance + } + + /// Squared distance between two points in the XZ plane + public static float SqrDistanceXZ (Vector3 a, Vector3 b) { + var delta = a-b; + + return delta.x*delta.x+delta.z*delta.z; + } + + /// + /// Signed area of a triangle in the XZ plane multiplied by 2. + /// This will be negative for clockwise triangles and positive for counter-clockwise ones + /// + public static long SignedTriangleAreaTimes2XZ (Int3 a, Int3 b, Int3 c) { + return (long)(b.x - a.x) * (long)(c.z - a.z) - (long)(c.x - a.x) * (long)(b.z - a.z); + } + + /// + /// Signed area of a triangle in the XZ plane multiplied by 2. + /// This will be negative for clockwise triangles and positive for counter-clockwise ones. + /// + public static float SignedTriangleAreaTimes2XZ (Vector3 a, Vector3 b, Vector3 c) { + return (b.x - a.x) * (c.z - a.z) - (c.x - a.x) * (b.z - a.z); + } + + /// + /// Returns if p lies on the right side of the line a - b. + /// Uses XZ space. Does not return true if the points are colinear. + /// + public static bool RightXZ (Vector3 a, Vector3 b, Vector3 p) { + return (b.x - a.x) * (p.z - a.z) - (p.x - a.x) * (b.z - a.z) < -float.Epsilon; + } + + /// + /// Returns if p lies on the right side of the line a - b. + /// Uses XZ space. Does not return true if the points are colinear. + /// + public static bool RightXZ (Int3 a, Int3 b, Int3 p) { + return (long)(b.x - a.x) * (long)(p.z - a.z) - (long)(p.x - a.x) * (long)(b.z - a.z) < 0; + } + + /// + /// Returns which side of the line a - b that p lies on. + /// Uses XZ space. + /// + public static Side SideXZ (Int3 a, Int3 b, Int3 p) { + var s = (long)(b.x - a.x) * (long)(p.z - a.z) - (long)(p.x - a.x) * (long)(b.z - a.z); + + return s > 0 ? Side.Left : (s < 0 ? Side.Right : Side.Colinear); + } + + /// + /// Returns if p lies on the right side of the line a - b. + /// Also returns true if the points are colinear. + /// + public static bool RightOrColinear (Vector2 a, Vector2 b, Vector2 p) { + return (b.x - a.x) * (p.y - a.y) - (p.x - a.x) * (b.y - a.y) <= 0; + } + + /// + /// Returns if p lies on the right side of the line a - b. + /// Also returns true if the points are colinear. + /// + public static bool RightOrColinear (Int2 a, Int2 b, Int2 p) { + return (long)(b.x - a.x) * (long)(p.y - a.y) - (long)(p.x - a.x) * (long)(b.y - a.y) <= 0; + } + + /// + /// Returns if p lies on the left side of the line a - b. + /// Uses XZ space. Also returns true if the points are colinear. + /// + public static bool RightOrColinearXZ (Vector3 a, Vector3 b, Vector3 p) { + return (b.x - a.x) * (p.z - a.z) - (p.x - a.x) * (b.z - a.z) <= 0; + } + + /// + /// Returns if p lies on the left side of the line a - b. + /// Uses XZ space. Also returns true if the points are colinear. + /// + public static bool RightOrColinearXZ (Int3 a, Int3 b, Int3 p) { + return (long)(b.x - a.x) * (long)(p.z - a.z) - (long)(p.x - a.x) * (long)(b.z - a.z) <= 0; + } + + /// + /// Returns if the points a in a clockwise order. + /// Will return true even if the points are colinear or very slightly counter-clockwise + /// (if the signed area of the triangle formed by the points has an area less than or equals to float.Epsilon) + /// + public static bool IsClockwiseMarginXZ (Vector3 a, Vector3 b, Vector3 c) { + return (b.x-a.x)*(c.z-a.z)-(c.x-a.x)*(b.z-a.z) <= float.Epsilon; + } + + /// Returns if the points a in a clockwise order + public static bool IsClockwiseXZ (Vector3 a, Vector3 b, Vector3 c) { + return (b.x-a.x)*(c.z-a.z)-(c.x-a.x)*(b.z-a.z) < 0; + } + + /// Returns if the points a in a clockwise order + public static bool IsClockwiseXZ (Int3 a, Int3 b, Int3 c) { + return RightXZ(a, b, c); + } + + /// Returns true if the points a in a clockwise order or if they are colinear + public static bool IsClockwiseOrColinearXZ (Int3 a, Int3 b, Int3 c) { + return RightOrColinearXZ(a, b, c); + } + + /// Returns true if the points a in a clockwise order or if they are colinear + public static bool IsClockwiseOrColinear (Int2 a, Int2 b, Int2 c) { + return RightOrColinear(a, b, c); + } + + /// Returns if the points are colinear (lie on a straight line) + public static bool IsColinear (Vector3 a, Vector3 b, Vector3 c) { + var lhs = b - a; + var rhs = c - a; + // Take the cross product of lhs and rhs + // The magnitude of the cross product will be zero if the points a,b,c are colinear + float x = lhs.y * rhs.z - lhs.z * rhs.y; + float y = lhs.z * rhs.x - lhs.x * rhs.z; + float z = lhs.x * rhs.y - lhs.y * rhs.x; + float v = x*x + y*y + z*z; + + // Epsilon not chosen with much thought, just that float.Epsilon was a bit too small. + return v <= 0.0000001f; + } + + /// Returns if the points are colinear (lie on a straight line) + public static bool IsColinear (Vector2 a, Vector2 b, Vector2 c) { + float v = (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y); + + // Epsilon not chosen with much thought, just that float.Epsilon was a bit too small. + return v <= 0.0000001f && v >= -0.0000001f; + } + + /// Returns if the points are colinear (lie on a straight line) + public static bool IsColinearXZ (Int3 a, Int3 b, Int3 c) { + return (long)(b.x - a.x) * (long)(c.z - a.z) - (long)(c.x - a.x) * (long)(b.z - a.z) == 0; + } + + /// Returns if the points are colinear (lie on a straight line) + public static bool IsColinearXZ (Vector3 a, Vector3 b, Vector3 c) { + float v = (b.x-a.x)*(c.z-a.z)-(c.x-a.x)*(b.z-a.z); + + // Epsilon not chosen with much thought, just that float.Epsilon was a bit too small. + return v <= 0.0000001f && v >= -0.0000001f; + } + + /// Returns if the points are colinear (lie on a straight line) + public static bool IsColinearAlmostXZ (Int3 a, Int3 b, Int3 c) { + long v = (long)(b.x - a.x) * (long)(c.z - a.z) - (long)(c.x - a.x) * (long)(b.z - a.z); + + return v > -1 && v < 1; + } + + /// + /// Returns if the line segment start2 - end2 intersects the line segment start1 - end1. + /// If only the endpoints coincide, the result is undefined (may be true or false). + /// + public static bool SegmentsIntersect (Int2 start1, Int2 end1, Int2 start2, Int2 end2) { + return RightOrColinear(start1, end1, start2) != RightOrColinear(start1, end1, end2) && RightOrColinear(start2, end2, start1) != RightOrColinear(start2, end2, end1); + } + + /// + /// Returns if the line segment start2 - end2 intersects the line segment start1 - end1. + /// If only the endpoints coincide, the result is undefined (may be true or false). + /// + /// Note: XZ space + /// + public static bool SegmentsIntersectXZ (Int3 start1, Int3 end1, Int3 start2, Int3 end2) { + return RightOrColinearXZ(start1, end1, start2) != RightOrColinearXZ(start1, end1, end2) && RightOrColinearXZ(start2, end2, start1) != RightOrColinearXZ(start2, end2, end1); + } + + /// + /// Returns if the two line segments intersects. The lines are NOT treated as infinite (just for clarification) + /// See: IntersectionPoint + /// + public static bool SegmentsIntersectXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2) { + Vector3 dir1 = end1-start1; + Vector3 dir2 = end2-start2; + + float den = dir2.z*dir1.x - dir2.x * dir1.z; + + if (den == 0) { + return false; + } + + float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + float nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x); + float u = nom/den; + float u2 = nom2/den; + + if (u < 0F || u > 1F || u2 < 0F || u2 > 1F) { + return false; + } + + return true; + } + + /// + /// Intersection point between two infinite lines. + /// Note that start points and directions are taken as parameters instead of start and end points. + /// Lines are treated as infinite. If the lines are parallel 'start1' will be returned. + /// Intersections are calculated on the XZ plane. + /// + /// See: LineIntersectionPointXZ + /// + public static Vector3 LineDirIntersectionPointXZ (Vector3 start1, Vector3 dir1, Vector3 start2, Vector3 dir2) { + float den = dir2.z*dir1.x - dir2.x * dir1.z; + + if (den == 0) { + return start1; + } + + float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + float u = nom/den; + + return start1 + dir1*u; + } + + /// + /// Intersection point between two infinite lines. + /// Note that start points and directions are taken as parameters instead of start and end points. + /// Lines are treated as infinite. If the lines are parallel 'start1' will be returned. + /// Intersections are calculated on the XZ plane. + /// + /// See: LineIntersectionPointXZ + /// + public static Vector3 LineDirIntersectionPointXZ (Vector3 start1, Vector3 dir1, Vector3 start2, Vector3 dir2, out bool intersects) { + float den = dir2.z*dir1.x - dir2.x * dir1.z; + + if (den == 0) { + intersects = false; + return start1; + } + + float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + float u = nom/den; + + intersects = true; + return start1 + dir1*u; + } + + /// + /// Returns if the ray (start1, end1) intersects the segment (start2, end2). + /// false is returned if the lines are parallel. + /// Only the XZ coordinates are used. + /// TODO: Double check that this actually works + /// + public static bool RaySegmentIntersectXZ (Int3 start1, Int3 end1, Int3 start2, Int3 end2) { + Int3 dir1 = end1-start1; + Int3 dir2 = end2-start2; + + long den = dir2.z*dir1.x - dir2.x * dir1.z; + + if (den == 0) { + return false; + } + + long nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + long nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x); + + //factor1 < 0 + // If both have the same sign, then nom/den < 0 and thus the segment cuts the ray before the ray starts + if (!(nom < 0 ^ den < 0)) { + return false; + } + + //factor2 < 0 + if (!(nom2 < 0 ^ den < 0)) { + return false; + } + + if ((den >= 0 && nom2 > den) || (den < 0 && nom2 <= den)) { + return false; + } + + return true; + } + + /// + /// Returns the intersection factors for line 1 and line 2. The intersection factors is a distance along the line start - end where the other line intersects it.\n + /// intersectionPoint = start1 + factor1 * (end1-start1) + /// intersectionPoint2 = start2 + factor2 * (end2-start2) + /// Lines are treated as infinite.\n + /// false is returned if the lines are parallel and true if they are not. + /// Only the XZ coordinates are used. + /// + public static bool LineIntersectionFactorXZ (Int3 start1, Int3 end1, Int3 start2, Int3 end2, out float factor1, out float factor2) { + Int3 dir1 = end1-start1; + Int3 dir2 = end2-start2; + + long den = dir2.z*dir1.x - dir2.x * dir1.z; + + if (den == 0) { + factor1 = 0; + factor2 = 0; + return false; + } + + long nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + long nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x); + + factor1 = (float)nom/den; + factor2 = (float)nom2/den; + + return true; + } + + /// + /// Returns the intersection factors for line 1 and line 2. The intersection factors is a distance along the line start - end where the other line intersects it.\n + /// intersectionPoint = start1 + factor1 * (end1-start1) + /// intersectionPoint2 = start2 + factor2 * (end2-start2) + /// Lines are treated as infinite.\n + /// false is returned if the lines are parallel and true if they are not. + /// Only the XZ coordinates are used. + /// + public static bool LineIntersectionFactorXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2, out float factor1, out float factor2) { + Vector3 dir1 = end1-start1; + Vector3 dir2 = end2-start2; + + float den = dir2.z*dir1.x - dir2.x * dir1.z; + + if (den <= 0.00001f && den >= -0.00001f) { + factor1 = 0; + factor2 = 0; + return false; + } + + float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + float nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x); + + float u = nom/den; + float u2 = nom2/den; + + factor1 = u; + factor2 = u2; + + return true; + } + + /// + /// Returns the intersection factor for line 1 with ray 2. + /// The intersection factors is a factor distance along the line start - end where the other line intersects it.\n + /// intersectionPoint = start1 + factor * (end1-start1) + /// Lines are treated as infinite.\n + /// + /// The second "line" is treated as a ray, meaning only matches on start2 or forwards towards end2 (and beyond) will be returned + /// If the point lies on the wrong side of the ray start, Nan will be returned. + /// + /// NaN is returned if the lines are parallel. + /// + public static float LineRayIntersectionFactorXZ (Int3 start1, Int3 end1, Int3 start2, Int3 end2) { + Int3 dir1 = end1-start1; + Int3 dir2 = end2-start2; + + int den = dir2.z*dir1.x - dir2.x * dir1.z; + + if (den == 0) { + return float.NaN; + } + + int nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + int nom2 = dir1.x*(start1.z-start2.z) - dir1.z * (start1.x - start2.x); + + if ((float)nom2/den < 0) { + return float.NaN; + } + return (float)nom/den; + } + + /// + /// Returns the intersection factor for line 1 with line 2. + /// The intersection factor is a distance along the line start1 - end1 where the line start2 - end2 intersects it.\n + /// intersectionPoint = start1 + intersectionFactor * (end1-start1) . + /// Lines are treated as infinite.\n + /// -1 is returned if the lines are parallel (note that this is a valid return value if they are not parallel too) + /// + public static float LineIntersectionFactorXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2) { + Vector3 dir1 = end1-start1; + Vector3 dir2 = end2-start2; + + float den = dir2.z*dir1.x - dir2.x * dir1.z; + + if (den == 0) { + return -1; + } + + float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + float u = nom/den; + + return u; + } + + /// Returns the intersection point between the two lines. Lines are treated as infinite. start1 is returned if the lines are parallel + public static Vector3 LineIntersectionPointXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2) { + bool s; + + return LineIntersectionPointXZ(start1, end1, start2, end2, out s); + } + + /// Returns the intersection point between the two lines. Lines are treated as infinite. start1 is returned if the lines are parallel + public static Vector3 LineIntersectionPointXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2, out bool intersects) { + Vector3 dir1 = end1-start1; + Vector3 dir2 = end2-start2; + + float den = dir2.z*dir1.x - dir2.x * dir1.z; + + if (den == 0) { + intersects = false; + return start1; + } + + float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + + float u = nom/den; + + intersects = true; + return start1 + dir1*u; + } + + /// Returns the intersection point between the two lines. Lines are treated as infinite. start1 is returned if the lines are parallel + public static Vector2 LineIntersectionPoint (Vector2 start1, Vector2 end1, Vector2 start2, Vector2 end2) { + bool s; + + return LineIntersectionPoint(start1, end1, start2, end2, out s); + } + + /// Returns the intersection point between the two lines. Lines are treated as infinite. start1 is returned if the lines are parallel + public static Vector2 LineIntersectionPoint (Vector2 start1, Vector2 end1, Vector2 start2, Vector2 end2, out bool intersects) { + Vector2 dir1 = end1-start1; + Vector2 dir2 = end2-start2; + + float den = dir2.y*dir1.x - dir2.x * dir1.y; + + if (den == 0) { + intersects = false; + return start1; + } + + float nom = dir2.x*(start1.y-start2.y)- dir2.y*(start1.x-start2.x); + + float u = nom/den; + + intersects = true; + return start1 + dir1*u; + } + + /// + /// Returns the intersection point between the two line segments in XZ space. + /// Lines are NOT treated as infinite. start1 is returned if the line segments do not intersect + /// The point will be returned along the line [start1, end1] (this matters only for the y coordinate). + /// + public static Vector3 SegmentIntersectionPointXZ (Vector3 start1, Vector3 end1, Vector3 start2, Vector3 end2, out bool intersects) { + Vector3 dir1 = end1-start1; + Vector3 dir2 = end2-start2; + + float den = dir2.z * dir1.x - dir2.x * dir1.z; + + if (den == 0) { + intersects = false; + return start1; + } + + float nom = dir2.x*(start1.z-start2.z)- dir2.z*(start1.x-start2.x); + float nom2 = dir1.x*(start1.z-start2.z) - dir1.z*(start1.x-start2.x); + float u = nom/den; + float u2 = nom2/den; + + if (u < 0F || u > 1F || u2 < 0F || u2 > 1F) { + intersects = false; + return start1; + } + + intersects = true; + return start1 + dir1*u; + } + + /// + /// Does the line segment intersect the bounding box. + /// The line is NOT treated as infinite. + /// \author Slightly modified code from http://www.3dkingdoms.com/weekly/weekly.php?a=21 + /// + public static bool SegmentIntersectsBounds (Bounds bounds, Vector3 a, Vector3 b) { + // Put segment in box space + a -= bounds.center; + b -= bounds.center; + + // Get line midpoint and extent + var LMid = (a + b) * 0.5F; + var L = (a - LMid); + var LExt = new Vector3(Math.Abs(L.x), Math.Abs(L.y), Math.Abs(L.z)); + + Vector3 extent = bounds.extents; + + // Use Separating Axis Test + // Separation vector from box center to segment center is LMid, since the line is in box space + if (Math.Abs(LMid.x) > extent.x + LExt.x) return false; + if (Math.Abs(LMid.y) > extent.y + LExt.y) return false; + if (Math.Abs(LMid.z) > extent.z + LExt.z) return false; + // Crossproducts of line and each axis + if (Math.Abs(LMid.y * L.z - LMid.z * L.y) > (extent.y * LExt.z + extent.z * LExt.y)) return false; + if (Math.Abs(LMid.x * L.z - LMid.z * L.x) > (extent.x * LExt.z + extent.z * LExt.x)) return false; + if (Math.Abs(LMid.x * L.y - LMid.y * L.x) > (extent.x * LExt.y + extent.y * LExt.x)) return false; + // No separating axis, the line intersects + return true; + } + + /// + /// Intersection of a line and a circle. + /// Returns the greatest t such that segmentStart+t*(segmentEnd-segmentStart) lies on the circle. + /// + /// In case the line does not intersect with the circle, the closest point on the line + /// to the circle will be returned. + /// + /// Note: Works for line and sphere in 3D space as well. + /// + /// See: http://mathworld.wolfram.com/Circle-LineIntersection.html + /// See: https://en.wikipedia.org/wiki/Intersection_(Euclidean_geometry) + /// + public static float LineCircleIntersectionFactor (Vector3 circleCenter, Vector3 linePoint1, Vector3 linePoint2, float radius) { + float segmentLength; + var normalizedDirection = Normalize(linePoint2 - linePoint1, out segmentLength); + var dirToStart = linePoint1 - circleCenter; + + var dot = Vector3.Dot(dirToStart, normalizedDirection); + var discriminant = dot * dot - (dirToStart.sqrMagnitude - radius*radius); + + if (discriminant < 0) { + // No intersection, pick closest point on segment + discriminant = 0; + } + + var t = -dot + Mathf.Sqrt(discriminant); + // Note: the default value of 1 is important for the PathInterpolator.MoveToCircleIntersection2D + // method to work properly. Maybe find some better abstraction where this default value is more obvious. + return segmentLength > 0.00001f ? t / segmentLength : 1f; + } + + /// + /// True if the matrix will reverse orientations of faces. + /// + /// Scaling by a negative value along an odd number of axes will reverse + /// the orientation of e.g faces on a mesh. This must be counter adjusted + /// by for example the recast rasterization system to be able to handle + /// meshes with negative scales properly. + /// + /// We can find out if they are flipped by finding out how the signed + /// volume of a unit cube is transformed when applying the matrix + /// + /// If the (signed) volume turns out to be negative + /// that also means that the orientation of it has been reversed. + /// + /// See: https://en.wikipedia.org/wiki/Normal_(geometry) + /// See: https://en.wikipedia.org/wiki/Parallelepiped + /// + public static bool ReversesFaceOrientations (Matrix4x4 matrix) { + var dX = matrix.MultiplyVector(new Vector3(1, 0, 0)); + var dY = matrix.MultiplyVector(new Vector3(0, 1, 0)); + var dZ = matrix.MultiplyVector(new Vector3(0, 0, 1)); + + // Calculate the signed volume of the parallelepiped + var volume = Vector3.Dot(Vector3.Cross(dX, dY), dZ); + + return volume < 0; + } + + /// + /// True if the matrix will reverse orientations of faces in the XZ plane. + /// Almost the same as ReversesFaceOrientations, but this method assumes + /// that scaling a face with a negative scale along the Y axis does not + /// reverse the orientation of the face. + /// + /// This is used for navmesh cuts. + /// + /// Scaling by a negative value along one axis or rotating + /// it so that it is upside down will reverse + /// the orientation of the cut, so we need to be reverse + /// it again as a countermeasure. + /// However if it is flipped along two axes it does not need to + /// be reversed. + /// We can handle all these cases by finding out how a unit square formed + /// by our forward axis and our rightward axis is transformed in XZ space + /// when applying the local to world matrix. + /// If the (signed) area of the unit square turns out to be negative + /// that also means that the orientation of it has been reversed. + /// The signed area is calculated using a cross product of the vectors. + /// + public static bool ReversesFaceOrientationsXZ (Matrix4x4 matrix) { + var dX = matrix.MultiplyVector(new Vector3(1, 0, 0)); + var dZ = matrix.MultiplyVector(new Vector3(0, 0, 1)); + + // Take the cross product of the vectors projected onto the XZ plane + var cross = (dX.x*dZ.z - dZ.x*dX.z); + + return cross < 0; + } + + /// + /// Normalize vector and also return the magnitude. + /// This is more efficient than calculating the magnitude and normalizing separately + /// + public static Vector3 Normalize (Vector3 v, out float magnitude) { + magnitude = v.magnitude; + // This is the same constant that Unity uses + if (magnitude > 1E-05f) { + return v / magnitude; + } else { + return Vector3.zero; + } + } + + /// + /// Normalize vector and also return the magnitude. + /// This is more efficient than calculating the magnitude and normalizing separately + /// + public static Vector2 Normalize (Vector2 v, out float magnitude) { + magnitude = v.magnitude; + // This is the same constant that Unity uses + if (magnitude > 1E-05f) { + return v / magnitude; + } else { + return Vector2.zero; + } + } + + /* Clamp magnitude along the X and Z axes. + * The y component will not be changed. + */ + public static Vector3 ClampMagnitudeXZ (Vector3 v, float maxMagnitude) { + float squaredMagnitudeXZ = v.x*v.x + v.z*v.z; + + if (squaredMagnitudeXZ > maxMagnitude*maxMagnitude && maxMagnitude > 0) { + var factor = maxMagnitude / Mathf.Sqrt(squaredMagnitudeXZ); + v.x *= factor; + v.z *= factor; + } + return v; + } + + /* Magnitude in the XZ plane */ + public static float MagnitudeXZ (Vector3 v) { + return Mathf.Sqrt(v.x*v.x + v.z*v.z); + } + } + + /// + /// Utility functions for working with numbers and strings. + /// \ingroup utils + /// See: Polygon + /// See: VectorMath + /// + public static class AstarMath { + /// Maps a value between startMin and startMax to be between targetMin and targetMax + public static float MapTo (float startMin, float startMax, float targetMin, float targetMax, float value) { + return Mathf.Lerp(targetMin, targetMax, Mathf.InverseLerp(startMin, startMax, value)); + } + + /// Returns a nicely formatted string for the number of bytes (KiB, MiB, GiB etc). Uses decimal names (KB, Mb - 1000) but calculates using binary values (KiB, MiB - 1024) + public static string FormatBytesBinary (int bytes) { + double sign = bytes >= 0 ? 1D : -1D; + + bytes = Mathf.Abs(bytes); + + if (bytes < 1024) { + return (bytes*sign)+" bytes"; + } else if (bytes < 1024*1024) { + return ((bytes/1024D)*sign).ToString("0.0") + " KiB"; + } else if (bytes < 1024*1024*1024) { + return ((bytes/(1024D*1024D))*sign).ToString("0.0") +" MiB"; + } + return ((bytes/(1024D*1024D*1024D))*sign).ToString("0.0") +" GiB"; + } + + /// + /// Returns bit number b from int a. The bit number is zero based. Relevant b values are from 0 to 31. + /// Equals to (a >> b) & 1 + /// + static int Bit (int a, int b) { + return (a >> b) & 1; + } + + /// + /// Returns a nice color from int i with alpha a. Got code from the open-source Recast project, works really well. + /// Seems like there are only 64 possible colors from studying the code + /// + public static Color IntToColor (int i, float a) { + int r = Bit(i, 2) + Bit(i, 3) * 2 + 1; + int g = Bit(i, 1) + Bit(i, 4) * 2 + 1; + int b = Bit(i, 0) + Bit(i, 5) * 2 + 1; + + return new Color(r*0.25F, g*0.25F, b*0.25F, a); + } + + /// + /// Converts an HSV color to an RGB color. + /// According to the algorithm described at http://en.wikipedia.org/wiki/HSL_and_HSV + /// + /// @author Wikipedia + /// @return the RGB representation of the color. + /// + public static Color HSVToRGB (float h, float s, float v) { + float r = 0, g = 0, b = 0; + + float Chroma = s * v; + float Hdash = h / 60.0f; + float X = Chroma * (1.0f - System.Math.Abs((Hdash % 2.0f) - 1.0f)); + + if (Hdash < 1.0f) { + r = Chroma; + g = X; + } else if (Hdash < 2.0f) { + r = X; + g = Chroma; + } else if (Hdash < 3.0f) { + g = Chroma; + b = X; + } else if (Hdash < 4.0f) { + g = X; + b = Chroma; + } else if (Hdash < 5.0f) { + r = X; + b = Chroma; + } else if (Hdash < 6.0f) { + r = Chroma; + b = X; + } + + float Min = v - Chroma; + + r += Min; + g += Min; + b += Min; + + return new Color(r, g, b); + } + } + + /// + /// Utility functions for working with polygons, lines, and other vector math. + /// All functions which accepts Vector3s but work in 2D space uses the XZ space if nothing else is said. + /// + /// Version: A lot of functions in this class have been moved to the VectorMath class + /// the names have changed slightly and everything now consistently assumes a left handed + /// coordinate system now instead of sometimes using a left handed one and sometimes + /// using a right handed one. This is why the 'Left' methods redirect to methods + /// named 'Right'. The functionality is exactly the same. + /// + /// \ingroup utils + /// + public static class Polygon { + /// + /// Returns if the triangle ABC contains the point p in XZ space. + /// The triangle vertices are assumed to be laid out in clockwise order. + /// + public static bool ContainsPointXZ (Vector3 a, Vector3 b, Vector3 c, Vector3 p) { + return VectorMath.IsClockwiseMarginXZ(a, b, p) && VectorMath.IsClockwiseMarginXZ(b, c, p) && VectorMath.IsClockwiseMarginXZ(c, a, p); + } + + /// + /// Returns if the triangle ABC contains the point p. + /// The triangle vertices are assumed to be laid out in clockwise order. + /// + public static bool ContainsPointXZ (Int3 a, Int3 b, Int3 c, Int3 p) { + return VectorMath.IsClockwiseOrColinearXZ(a, b, p) && VectorMath.IsClockwiseOrColinearXZ(b, c, p) && VectorMath.IsClockwiseOrColinearXZ(c, a, p); + } + + /// + /// Returns if the triangle ABC contains the point p. + /// The triangle vertices are assumed to be laid out in clockwise order. + /// + public static bool ContainsPoint (Int2 a, Int2 b, Int2 c, Int2 p) { + return VectorMath.IsClockwiseOrColinear(a, b, p) && VectorMath.IsClockwiseOrColinear(b, c, p) && VectorMath.IsClockwiseOrColinear(c, a, p); + } + + /// + /// Checks if p is inside the polygon. + /// \author http://unifycommunity.com/wiki/index.php?title=PolyContainsPoint (Eric5h5) + /// + public static bool ContainsPoint (Vector2[] polyPoints, Vector2 p) { + int j = polyPoints.Length-1; + bool inside = false; + + for (int i = 0; i < polyPoints.Length; j = i++) { + if (((polyPoints[i].y <= p.y && p.y < polyPoints[j].y) || (polyPoints[j].y <= p.y && p.y < polyPoints[i].y)) && + (p.x < (polyPoints[j].x - polyPoints[i].x) * (p.y - polyPoints[i].y) / (polyPoints[j].y - polyPoints[i].y) + polyPoints[i].x)) + inside = !inside; + } + return inside; + } + + /// + /// Checks if p is inside the polygon (XZ space). + /// \author http://unifycommunity.com/wiki/index.php?title=PolyContainsPoint (Eric5h5) + /// + public static bool ContainsPointXZ (Vector3[] polyPoints, Vector3 p) { + int j = polyPoints.Length-1; + bool inside = false; + + for (int i = 0; i < polyPoints.Length; j = i++) { + if (((polyPoints[i].z <= p.z && p.z < polyPoints[j].z) || (polyPoints[j].z <= p.z && p.z < polyPoints[i].z)) && + (p.x < (polyPoints[j].x - polyPoints[i].x) * (p.z - polyPoints[i].z) / (polyPoints[j].z - polyPoints[i].z) + polyPoints[i].x)) + inside = !inside; + } + return inside; + } + + /// + /// Sample Y coordinate of the triangle (p1, p2, p3) at the point p in XZ space. + /// The y coordinate of p is ignored. + /// + /// Returns: The interpolated y coordinate unless the triangle is degenerate in which case a DivisionByZeroException will be thrown + /// + /// See: https://en.wikipedia.org/wiki/Barycentric_coordinate_system + /// + public static int SampleYCoordinateInTriangle (Int3 p1, Int3 p2, Int3 p3, Int3 p) { + double det = ((double)(p2.z - p3.z)) * (p1.x - p3.x) + ((double)(p3.x - p2.x)) * (p1.z - p3.z); + + double lambda1 = ((((double)(p2.z - p3.z)) * (p.x - p3.x) + ((double)(p3.x - p2.x)) * (p.z - p3.z)) / det); + double lambda2 = ((((double)(p3.z - p1.z)) * (p.x - p3.x) + ((double)(p1.x - p3.x)) * (p.z - p3.z)) / det); + + return (int)Math.Round(lambda1 * p1.y + lambda2 * p2.y + (1 - lambda1 - lambda2) * p3.y); + } + + /// + /// Calculates convex hull in XZ space for the points. + /// Implemented using the very simple Gift Wrapping Algorithm + /// which has a complexity of O(nh) where n is the number of points and h is the number of points on the hull, + /// so it is in the worst case quadratic. + /// + public static Vector3[] ConvexHullXZ (Vector3[] points) { + if (points.Length == 0) return new Vector3[0]; + + var hull = Pathfinding.Util.ListPool.Claim (); + + int pointOnHull = 0; + for (int i = 1; i < points.Length; i++) if (points[i].x < points[pointOnHull].x) pointOnHull = i; + + int startpoint = pointOnHull; + int counter = 0; + + do { + hull.Add(points[pointOnHull]); + int endpoint = 0; + for (int i = 0; i < points.Length; i++) if (endpoint == pointOnHull || !VectorMath.RightOrColinearXZ(points[pointOnHull], points[endpoint], points[i])) endpoint = i; + + pointOnHull = endpoint; + + counter++; + if (counter > 10000) { + Debug.LogWarning("Infinite Loop in Convex Hull Calculation"); + break; + } + } while (pointOnHull != startpoint); + + var result = hull.ToArray(); + + // Return to pool + Pathfinding.Util.ListPool.Release (hull); + return result; + } + + /// + /// Closest point on the triangle abc to the point p. + /// See: 'Real Time Collision Detection' by Christer Ericson, chapter 5.1, page 141 + /// + public static Vector2 ClosestPointOnTriangle (Vector2 a, Vector2 b, Vector2 c, Vector2 p) { + // Check if p is in vertex region outside A + var ab = b - a; + var ac = c - a; + var ap = p - a; + + var d1 = Vector2.Dot(ab, ap); + var d2 = Vector2.Dot(ac, ap); + + // Barycentric coordinates (1,0,0) + if (d1 <= 0 && d2 <= 0) { + return a; + } + + // Check if p is in vertex region outside B + var bp = p - b; + var d3 = Vector2.Dot(ab, bp); + var d4 = Vector2.Dot(ac, bp); + + // Barycentric coordinates (0,1,0) + if (d3 >= 0 && d4 <= d3) { + return b; + } + + // Check if p is in edge region outside AB, if so return a projection of p onto AB + if (d1 >= 0 && d3 <= 0) { + var vc = d1 * d4 - d3 * d2; + if (vc <= 0) { + // Barycentric coordinates (1-v, v, 0) + var v = d1 / (d1 - d3); + return a + ab*v; + } + } + + // Check if p is in vertex region outside C + var cp = p - c; + var d5 = Vector2.Dot(ab, cp); + var d6 = Vector2.Dot(ac, cp); + + // Barycentric coordinates (0,0,1) + if (d6 >= 0 && d5 <= d6) { + return c; + } + + // Check if p is in edge region of AC, if so return a projection of p onto AC + if (d2 >= 0 && d6 <= 0) { + var vb = d5 * d2 - d1 * d6; + if (vb <= 0) { + // Barycentric coordinates (1-v, 0, v) + var v = d2 / (d2 - d6); + return a + ac*v; + } + } + + // Check if p is in edge region of BC, if so return projection of p onto BC + if ((d4 - d3) >= 0 && (d5 - d6) >= 0) { + var va = d3 * d6 - d5 * d4; + if (va <= 0) { + var v = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + return b + (c - b) * v; + } + } + + return p; + } + + /// + /// Closest point on the triangle abc to the point p when seen from above. + /// See: 'Real Time Collision Detection' by Christer Ericson, chapter 5.1, page 141 + /// + public static Vector3 ClosestPointOnTriangleXZ (Vector3 a, Vector3 b, Vector3 c, Vector3 p) { + // Check if p is in vertex region outside A + var ab = new Vector2(b.x - a.x, b.z - a.z); + var ac = new Vector2(c.x - a.x, c.z - a.z); + var ap = new Vector2(p.x - a.x, p.z - a.z); + + var d1 = Vector2.Dot(ab, ap); + var d2 = Vector2.Dot(ac, ap); + + // Barycentric coordinates (1,0,0) + if (d1 <= 0 && d2 <= 0) { + return a; + } + + // Check if p is in vertex region outside B + var bp = new Vector2(p.x - b.x, p.z - b.z); + var d3 = Vector2.Dot(ab, bp); + var d4 = Vector2.Dot(ac, bp); + + // Barycentric coordinates (0,1,0) + if (d3 >= 0 && d4 <= d3) { + return b; + } + + // Check if p is in edge region outside AB, if so return a projection of p onto AB + var vc = d1 * d4 - d3 * d2; + if (d1 >= 0 && d3 <= 0 && vc <= 0) { + // Barycentric coordinates (1-v, v, 0) + var v = d1 / (d1 - d3); + return (1-v)*a + v*b; + } + + // Check if p is in vertex region outside C + var cp = new Vector2(p.x - c.x, p.z - c.z); + var d5 = Vector2.Dot(ab, cp); + var d6 = Vector2.Dot(ac, cp); + + // Barycentric coordinates (0,0,1) + if (d6 >= 0 && d5 <= d6) { + return c; + } + + // Check if p is in edge region of AC, if so return a projection of p onto AC + var vb = d5 * d2 - d1 * d6; + if (d2 >= 0 && d6 <= 0 && vb <= 0) { + // Barycentric coordinates (1-v, 0, v) + var v = d2 / (d2 - d6); + return (1-v)*a + v*c; + } + + // Check if p is in edge region of BC, if so return projection of p onto BC + var va = d3 * d6 - d5 * d4; + if ((d4 - d3) >= 0 && (d5 - d6) >= 0 && va <= 0) { + var v = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + return b + (c - b) * v; + } else { + // P is inside the face region. Compute the point using its barycentric coordinates (u, v, w) + // Note that the x and z coordinates will be exactly the same as P's x and z coordinates + var denom = 1f / (va + vb + vc); + var v = vb * denom; + var w = vc * denom; + + return new Vector3(p.x, (1 - v - w)*a.y + v*b.y + w*c.y, p.z); + } + } + + /// + /// Closest point on the triangle abc to the point p. + /// See: 'Real Time Collision Detection' by Christer Ericson, chapter 5.1, page 141 + /// + public static Vector3 ClosestPointOnTriangle (Vector3 a, Vector3 b, Vector3 c, Vector3 p) { + // Check if p is in vertex region outside A + var ab = b - a; + var ac = c - a; + var ap = p - a; + + var d1 = Vector3.Dot(ab, ap); + var d2 = Vector3.Dot(ac, ap); + + // Barycentric coordinates (1,0,0) + if (d1 <= 0 && d2 <= 0) + return a; + + // Check if p is in vertex region outside B + var bp = p - b; + var d3 = Vector3.Dot(ab, bp); + var d4 = Vector3.Dot(ac, bp); + + // Barycentric coordinates (0,1,0) + if (d3 >= 0 && d4 <= d3) + return b; + + // Check if p is in edge region outside AB, if so return a projection of p onto AB + var vc = d1 * d4 - d3 * d2; + if (d1 >= 0 && d3 <= 0 && vc <= 0) { + // Barycentric coordinates (1-v, v, 0) + var v = d1 / (d1 - d3); + return a + ab * v; + } + + // Check if p is in vertex region outside C + var cp = p - c; + var d5 = Vector3.Dot(ab, cp); + var d6 = Vector3.Dot(ac, cp); + + // Barycentric coordinates (0,0,1) + if (d6 >= 0 && d5 <= d6) + return c; + + // Check if p is in edge region of AC, if so return a projection of p onto AC + var vb = d5 * d2 - d1 * d6; + if (d2 >= 0 && d6 <= 0 && vb <= 0) { + // Barycentric coordinates (1-v, 0, v) + var v = d2 / (d2 - d6); + return a + ac * v; + } + + // Check if p is in edge region of BC, if so return projection of p onto BC + var va = d3 * d6 - d5 * d4; + if ((d4 - d3) >= 0 && (d5 - d6) >= 0 && va <= 0) { + var v = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + return b + (c - b) * v; + } else { + // P is inside the face region. Compute the point using its barycentric coordinates (u, v, w) + var denom = 1f / (va + vb + vc); + var v = vb * denom; + var w = vc * denom; + + // This is equal to: u*a + v*b + w*c, u = va*denom = 1 - v - w; + return a + ab * v + ac * w; + } + } + + /// Cached dictionary to avoid excessive allocations + static readonly Dictionary cached_Int3_int_dict = new Dictionary(); + + /// + /// Compress the mesh by removing duplicate vertices. + /// + /// Vertices that differ by only 1 along the y coordinate will also be merged together. + /// Warning: This function is not threadsafe. It uses some cached structures to reduce allocations. + /// + /// Vertices of the input mesh + /// Triangles of the input mesh + /// Vertices of the output mesh. + /// Triangles of the output mesh. + public static void CompressMesh (List vertices, List triangles, out Int3[] outVertices, out int[] outTriangles) { + Dictionary firstVerts = cached_Int3_int_dict; + + firstVerts.Clear(); + + // Use cached array to reduce memory allocations + int[] compressedPointers = ArrayPool.Claim (vertices.Count); + + // Map positions to the first index they were encountered at + int count = 0; + for (int i = 0; i < vertices.Count; i++) { + // Check if the vertex position has already been added + // Also check one position up and one down because rounding errors can cause vertices + // that should end up in the same position to be offset 1 unit from each other + // TODO: Check along X and Z axes as well? + int ind; + if (!firstVerts.TryGetValue(vertices[i], out ind) && !firstVerts.TryGetValue(vertices[i] + new Int3(0, 1, 0), out ind) && !firstVerts.TryGetValue(vertices[i] + new Int3(0, -1, 0), out ind)) { + firstVerts.Add(vertices[i], count); + compressedPointers[i] = count; + vertices[count] = vertices[i]; + count++; + } else { + compressedPointers[i] = ind; + } + } + + // Create the triangle array or reuse the existing buffer + outTriangles = new int[triangles.Count]; + + // Remap the triangles to the new compressed indices + for (int i = 0; i < outTriangles.Length; i++) { + outTriangles[i] = compressedPointers[triangles[i]]; + } + + // Create the vertex array or reuse the existing buffer + outVertices = new Int3[count]; + + for (int i = 0; i < count; i++) + outVertices[i] = vertices[i]; + + ArrayPool.Release (ref compressedPointers); + } + + /// + /// Given a set of edges between vertices, follows those edges and returns them as chains and cycles. + /// + /// [Open online documentation to see images] + /// + /// outline[a] = b if there is an edge from a to b. + /// hasInEdge should contain b if outline[a] = b for any key a. + /// Will be called once for each contour with the contour as a parameter as well as a boolean indicating if the contour is a cycle or a chain (see image). + public static void TraceContours (Dictionary outline, HashSet hasInEdge, System.Action, bool> results) { + // Iterate through chains of the navmesh outline. + // I.e segments of the outline that are not loops + // we need to start these at the beginning of the chain. + // Then iterate over all the loops of the outline. + // Since they are loops, we can start at any point. + var obstacleVertices = ListPool.Claim (); + var outlineKeys = ListPool.Claim (); + + outlineKeys.AddRange(outline.Keys); + for (int k = 0; k <= 1; k++) { + bool cycles = k == 1; + for (int i = 0; i < outlineKeys.Count; i++) { + var startIndex = outlineKeys[i]; + + // Chains (not cycles) need to start at the start of the chain + // Cycles can start at any point + if (!cycles && hasInEdge.Contains(startIndex)) { + continue; + } + + var index = startIndex; + obstacleVertices.Clear(); + obstacleVertices.Add(index); + + while (outline.ContainsKey(index)) { + var next = outline[index]; + outline.Remove(index); + + obstacleVertices.Add(next); + + // We traversed a full cycle + if (next == startIndex) break; + + index = next; + } + + if (obstacleVertices.Count > 1) { + results(obstacleVertices, cycles); + } + } + } + + ListPool.Release (ref outlineKeys); + ListPool.Release (ref obstacleVertices); + } + + /// Divides each segment in the list into subSegments segments and fills the result list with the new points + public static void Subdivide (List points, List result, int subSegments) { + for (int i = 0; i < points.Count-1; i++) + for (int j = 0; j < subSegments; j++) + result.Add(Vector3.Lerp(points[i], points[i+1], j / (float)subSegments)); + + result.Add(points[points.Count-1]); + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AstarMath.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarMath.cs.meta new file mode 100644 index 0000000..fe1bca0 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarMath.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 960fd9020b1f74f939fee737c3c0f491 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AstarPath.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarPath.cs new file mode 100644 index 0000000..f7cc806 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarPath.cs @@ -0,0 +1,2157 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using Pathfinding; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +#if NETFX_CORE +using Thread = Pathfinding.WindowsStore.Thread; +#else +using Thread = System.Threading.Thread; +#endif + +[ExecuteInEditMode] +[AddComponentMenu("Pathfinding/Pathfinder")] +/// +/// Core component for the A* %Pathfinding System. +/// This class handles all of the pathfinding system, calculates all paths and stores the info.\n +/// This class is a singleton class, meaning there should only exist at most one active instance of it in the scene.\n +/// It might be a bit hard to use directly, usually interfacing with the pathfinding system is done through the class. +/// +/// \nosubgrouping +/// \ingroup relevant +/// +[HelpURL("http://arongranberg.com/astar/docs/class_astar_path.php")] +public class AstarPath : VersionedMonoBehaviour { + /// The version number for the A* %Pathfinding Project + public static readonly System.Version Version = new System.Version(4, 2, 15); + + /// Information about where the package was downloaded + public enum AstarDistribution { WebsiteDownload, AssetStore, PackageManager }; + + /// Used by the editor to guide the user to the correct place to download updates + public static readonly AstarDistribution Distribution = AstarDistribution.WebsiteDownload; + + /// + /// Which branch of the A* %Pathfinding Project is this release. + /// Used when checking for updates so that + /// users of the development versions can get notifications of development + /// updates. + /// + public static readonly string Branch = "master"; + + /// + /// See Pathfinding.AstarData + /// Deprecated: + /// + [System.Obsolete] + public System.Type[] graphTypes { + get { + return data.graphTypes; + } + } + + /// Holds all graph data + [UnityEngine.Serialization.FormerlySerializedAs("astarData")] + public AstarData data; + + /// + /// Holds all graph data. + /// Deprecated: The 'astarData' field has been renamed to 'data' + /// + [System.Obsolete("The 'astarData' field has been renamed to 'data'")] + public AstarData astarData { get { return data; } } + + /// + /// Returns the active AstarPath object in the scene. + /// Note: This is only set if the AstarPath object has been initialized (which happens in Awake). + /// +#if UNITY_4_6 || UNITY_4_3 + public static new AstarPath active; +#else + public static AstarPath active; +#endif + + /// Shortcut to Pathfinding.AstarData.graphs + public NavGraph[] graphs { + get { + if (data == null) + data = new AstarData(); + return data.graphs; + } + } + + #region InspectorDebug + /// + /// @name Inspector - Debug + /// @{ + /// + + /// + /// Visualize graphs in the scene view (editor only). + /// [Open online documentation to see images] + /// + public bool showNavGraphs = true; + + /// + /// Toggle to show unwalkable nodes. + /// + /// Note: Only relevant in the editor + /// + /// See: + /// + public bool showUnwalkableNodes = true; + + /// + /// The mode to use for drawing nodes in the sceneview. + /// + /// Note: Only relevant in the editor + /// + /// See: Pathfinding.GraphDebugMode + /// + public GraphDebugMode debugMode; + + /// + /// Low value to use for certain modes. + /// For example if is set to G, this value will determine when the node will be completely red. + /// + /// Note: Only relevant in the editor + /// + /// See: + /// See: + /// + public float debugFloor = 0; + + /// + /// High value to use for certain modes. + /// For example if is set to G, this value will determine when the node will be completely green. + /// + /// For the penalty debug mode, the nodes will be colored green when they have a penalty less than and red + /// when their penalty is greater or equal to this value and something between red and green otherwise. + /// + /// Note: Only relevant in the editor + /// + /// See: + /// See: + /// + public float debugRoof = 20000; + + /// + /// If set, the and values will not be automatically recalculated. + /// + /// Note: Only relevant in the editor + /// + public bool manualDebugFloorRoof = false; + + + /// + /// If enabled, nodes will draw a line to their 'parent'. + /// This will show the search tree for the latest path. + /// + /// Note: Only relevant in the editor + /// + /// TODO: Add a showOnlyLastPath flag to indicate whether to draw every node or only the ones visited by the latest path. + /// + public bool showSearchTree = false; + + /// + /// Size of the red cubes shown in place of unwalkable nodes. + /// + /// Note: Only relevant in the editor. Does not apply to grid graphs. + /// See: + /// + public float unwalkableNodeDebugSize = 0.3F; + + /// + /// The amount of debugging messages. + /// Use less debugging to improve performance (a bit) or just to get rid of the Console spamming. + /// Use more debugging (heavy) if you want more information about what the pathfinding scripts are doing. + /// The InGame option will display the latest path log using in-game GUI. + /// + /// [Open online documentation to see images] + /// + public PathLog logPathResults = PathLog.Normal; + + /// @} + #endregion + + #region InspectorSettings + /// + /// @name Inspector - Settings + /// @{ + /// + + /// + /// Maximum distance to search for nodes. + /// When searching for the nearest node to a point, this is the limit (in world units) for how far away it is allowed to be. + /// + /// This is relevant if you try to request a path to a point that cannot be reached and it thus has to search for + /// the closest node to that point which can be reached (which might be far away). If it cannot find a node within this distance + /// then the path will fail. + /// + /// [Open online documentation to see images] + /// + /// See: Pathfinding.NNConstraint.constrainDistance + /// + public float maxNearestNodeDistance = 100; + + /// + /// Max Nearest Node Distance Squared. + /// See: + /// + public float maxNearestNodeDistanceSqr { + get { return maxNearestNodeDistance*maxNearestNodeDistance; } + } + + /// + /// If true, all graphs will be scanned during Awake. + /// This does not include loading from the cache. + /// If you disable this, you will have to call \link Scan AstarPath.active.Scan() \endlink yourself to enable pathfinding. + /// Alternatively you could load a saved graph from a file. + /// + /// See: + /// See: + /// + public bool scanOnStartup = true; + + /// + /// Do a full GetNearest search for all graphs. + /// Additional searches will normally only be done on the graph which in the first fast search seemed to have the closest node. + /// With this setting on, additional searches will be done on all graphs since the first check is not always completely accurate.\n + /// More technically: GetNearestForce on all graphs will be called if true, otherwise only on the one graph which's GetNearest search returned the best node.\n + /// Usually faster when disabled, but higher quality searches when enabled. + /// When using a a navmesh or recast graph, for best quality, this setting should be combined with the Pathfinding.NavMeshGraph.accurateNearestNode setting set to true. + /// Note: For the PointGraph this setting doesn't matter much as it has only one search mode. + /// + public bool fullGetNearestSearch = false; + + /// + /// Prioritize graphs. + /// Graphs will be prioritized based on their order in the inspector. + /// The first graph which has a node closer than will be chosen instead of searching all graphs. + /// + public bool prioritizeGraphs = false; + + /// + /// Distance limit for . + /// See: + /// + public float prioritizeGraphsLimit = 1F; + + /// + /// Reference to the color settings for this AstarPath object. + /// Color settings include for example which color the nodes should be in, in the sceneview. + /// + public AstarColor colorSettings; + + /// + /// Stored tag names. + /// See: AstarPath.FindTagNames + /// See: AstarPath.GetTagNames + /// + [SerializeField] + protected string[] tagNames = null; + + /// + /// The distance function to use as a heuristic. + /// The heuristic, often referred to as just 'H' is the estimated cost from a node to the target. + /// Different heuristics affect how the path picks which one to follow from multiple possible with the same length + /// See: for more details and descriptions of the different modes. + /// See: Wikipedia: Admissible heuristic + /// See: Wikipedia: A* search algorithm + /// See: Wikipedia: Dijkstra's Algorithm + /// + public Heuristic heuristic = Heuristic.Euclidean; + + /// + /// The scale of the heuristic. + /// If a value lower than 1 is used, the pathfinder will search more nodes (slower). + /// If 0 is used, the pathfinding algorithm will be reduced to dijkstra's algorithm. This is equivalent to setting to None. + /// If a value larger than 1 is used the pathfinding will (usually) be faster because it expands fewer nodes, but the paths may no longer be the optimal (i.e the shortest possible paths). + /// + /// Usually you should leave this to the default value of 1. + /// + /// See: https://en.wikipedia.org/wiki/Admissible_heuristic + /// See: https://en.wikipedia.org/wiki/A*_search_algorithm + /// See: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm + /// + public float heuristicScale = 1F; + + /// + /// Number of pathfinding threads to use. + /// Multithreading puts pathfinding in another thread, this is great for performance on 2+ core computers since the framerate will barely be affected by the pathfinding at all. + /// - None indicates that the pathfinding is run in the Unity thread as a coroutine + /// - Automatic will try to adjust the number of threads to the number of cores and memory on the computer. + /// Less than 512mb of memory or a single core computer will make it revert to using no multithreading. + /// + /// It is recommended that you use one of the "Auto" settings that are available. + /// The reason is that even if your computer might be beefy and have 8 cores. + /// Other computers might only be quad core or dual core in which case they will not benefit from more than + /// 1 or 3 threads respectively (you usually want to leave one core for the unity thread). + /// If you use more threads than the number of cores on the computer it is mostly just wasting memory, it will not run any faster. + /// The extra memory usage is not trivially small. Each thread needs to keep a small amount of data for each node in all the graphs. + /// It is not the full graph data but it is proportional to the number of nodes. + /// The automatic settings will inspect the machine it is running on and use that to determine the number of threads so that no memory is wasted. + /// + /// The exception is if you only have one (or maybe two characters) active at time. Then you should probably just go with one thread always since it is very unlikely + /// that you will need the extra throughput given by more threads. Keep in mind that more threads primarily increases throughput by calculating different paths on different + /// threads, it will not calculate individual paths any faster. + /// + /// Note that if you are modifying the pathfinding core scripts or if you are directly modifying graph data without using any of the + /// safe wrappers (like multithreading can cause strange errors and pathfinding stopping to work if you are not careful. + /// For basic usage (not modding the pathfinding core) it should be safe. + /// + /// Note: WebGL does not support threads at all (since javascript is single-threaded) so no threads will be used on that platform. + /// + /// See: CalculateThreadCount + /// + public ThreadCount threadCount = ThreadCount.One; + + /// + /// Max number of milliseconds to spend each frame for pathfinding. + /// At least 500 nodes will be searched each frame (if there are that many to search). + /// When using multithreading this value is irrelevant. + /// + public float maxFrameTime = 1F; + + /// + /// Throttle graph updates and batch them to improve performance. + /// If toggled, graph updates will batched and executed less often (specified by . + /// + /// This can have a positive impact on pathfinding throughput since the pathfinding threads do not need + /// to be stopped as often, and it reduces the overhead per graph update. + /// All graph updates are still applied however, they are just batched together so that more of them are + /// applied at the same time. + /// + /// However do not use this if you want minimal latency between a graph update being requested + /// and it being applied. + /// + /// This only applies to graph updates requested using the method. Not those requested + /// using or . + /// + /// If you want to apply graph updates immediately at some point, you can call . + /// + /// See: graph-updates (view in online documentation for working links) + /// + public bool batchGraphUpdates = false; + + /// + /// Minimum number of seconds between each batch of graph updates. + /// If is true, this defines the minimum number of seconds between each batch of graph updates. + /// + /// This can have a positive impact on pathfinding throughput since the pathfinding threads do not need + /// to be stopped as often, and it reduces the overhead per graph update. + /// All graph updates are still applied however, they are just batched together so that more of them are + /// applied at the same time. + /// + /// Do not use this if you want minimal latency between a graph update being requested + /// and it being applied. + /// + /// This only applies to graph updates requested using the method. Not those requested + /// using or . + /// + /// See: graph-updates (view in online documentation for working links) + /// + public float graphUpdateBatchingInterval = 0.2F; + + /// + /// Batch graph updates. + /// Deprecated: This field has been renamed to . + /// + [System.Obsolete("This field has been renamed to 'batchGraphUpdates'")] + public bool limitGraphUpdates { get { return batchGraphUpdates; } set { batchGraphUpdates = value; } } + + /// + /// Limit for how often should graphs be updated. + /// Deprecated: This field has been renamed to . + /// + [System.Obsolete("This field has been renamed to 'graphUpdateBatchingInterval'")] + public float maxGraphUpdateFreq { get { return graphUpdateBatchingInterval; } set { graphUpdateBatchingInterval = value; } } + + /// @} + #endregion + + #region DebugVariables + /// + /// @name Debug Members + /// @{ + /// + +#if ProfileAstar + /// + /// How many paths has been computed this run. From application start.\n + /// Debugging variable + /// + public static int PathsCompleted = 0; + + public static System.Int64 TotalSearchedNodes = 0; + public static System.Int64 TotalSearchTime = 0; +#endif + + /// + /// The time it took for the last call to Scan() to complete. + /// Used to prevent automatically rescanning the graphs too often (editor only) + /// + public float lastScanTime { get; private set; } + + /// + /// The path to debug using gizmos. + /// This is the path handler used to calculate the last path. + /// It is used in the editor to draw debug information using gizmos. + /// + [System.NonSerialized] + public PathHandler debugPathData; + + /// The path ID to debug using gizmos + [System.NonSerialized] + public ushort debugPathID; + + /// + /// Debug string from the last completed path. + /// Will be updated if == PathLog.InGame + /// + string inGameDebugPath; + + /* @} */ + #endregion + + #region StatusVariables + + /// + /// Backing field for . + /// Cannot use an auto-property because they cannot be marked with System.NonSerialized. + /// + [System.NonSerialized] + bool isScanningBacking; + + /// + /// Set while any graphs are being scanned. + /// It will be true up until the FloodFill is done. + /// + /// Note: Not to be confused with graph updates. + /// + /// Used to better support Graph Update Objects called for example in OnPostScan + /// + /// See: IsAnyGraphUpdateQueued + /// See: IsAnyGraphUpdateInProgress + /// + public bool isScanning { get { return isScanningBacking; } private set { isScanningBacking = value; } } + + /// + /// Number of parallel pathfinders. + /// Returns the number of concurrent processes which can calculate paths at once. + /// When using multithreading, this will be the number of threads, if not using multithreading it is always 1 (since only 1 coroutine is used). + /// See: IsUsingMultithreading + /// + public int NumParallelThreads { + get { + return pathProcessor.NumThreads; + } + } + + /// + /// Returns whether or not multithreading is used. + /// \exception System.Exception Is thrown when it could not be decided if multithreading was used or not. + /// This should not happen if pathfinding is set up correctly. + /// Note: This uses info about if threads are running right now, it does not use info from the settings on the A* object. + /// + public bool IsUsingMultithreading { + get { + return pathProcessor.IsUsingMultithreading; + } + } + + /// + /// Returns if any graph updates are waiting to be applied. + /// Deprecated: Use IsAnyGraphUpdateQueued instead + /// + [System.Obsolete("Fixed grammar, use IsAnyGraphUpdateQueued instead")] + public bool IsAnyGraphUpdatesQueued { get { return IsAnyGraphUpdateQueued; } } + + /// + /// Returns if any graph updates are waiting to be applied. + /// Note: This is false while the updates are being performed. + /// Note: This does *not* includes other types of work items such as navmesh cutting or anything added by or . + /// + public bool IsAnyGraphUpdateQueued { get { return graphUpdates.IsAnyGraphUpdateQueued; } } + + /// + /// Returns if any graph updates are being calculated right now. + /// Note: This does *not* includes other types of work items such as navmesh cutting or anything added by or . + /// + /// See: IsAnyWorkItemInProgress + /// + public bool IsAnyGraphUpdateInProgress { get { return graphUpdates.IsAnyGraphUpdateInProgress; } } + + /// + /// Returns if any work items are in progress right now. + /// Note: This includes pretty much all types of graph updates. + /// Such as normal graph updates, navmesh cutting and anything added by or . + /// + public bool IsAnyWorkItemInProgress { get { return workItems.workItemsInProgress; } } + + /// + /// Returns if this code is currently being exectuted inside a work item. + /// Note: This includes pretty much all types of graph updates. + /// Such as normal graph updates, navmesh cutting and anything added by or . + /// + /// In contrast to this is only true when work item code is being executed, it is not + /// true in-between the updates to a work item that takes several frames to complete. + /// + internal bool IsInsideWorkItem { get { return workItems.workItemsInProgressRightNow; } } + + #endregion + + #region Callbacks + /// @name Callbacks + /* Callbacks to pathfinding events. + * These allow you to hook in to the pathfinding process.\n + * Callbacks can be used like this: + * \snippet MiscSnippets.cs AstarPath.Callbacks + */ + /// @{ + + /// + /// Called on Awake before anything else is done. + /// This is called at the start of the Awake call, right after has been set, but this is the only thing that has been done.\n + /// Use this when you want to set up default settings for an AstarPath component created during runtime since some settings can only be changed in Awake + /// (such as multithreading related stuff) + /// + /// // Create a new AstarPath object on Start and apply some default settings + /// public void Start () { + /// AstarPath.OnAwakeSettings += ApplySettings; + /// AstarPath astar = gameObject.AddComponent(); + /// } + /// + /// public void ApplySettings () { + /// // Unregister from the delegate + /// AstarPath.OnAwakeSettings -= ApplySettings; + /// // For example threadCount should not be changed after the Awake call + /// // so here's the only place to set it if you create the component during runtime + /// AstarPath.active.threadCount = ThreadCount.One; + /// } + /// + /// + public static System.Action OnAwakeSettings; + + /// Called for each graph before they are scanned + public static OnGraphDelegate OnGraphPreScan; + + /// Called for each graph after they have been scanned. All other graphs might not have been scanned yet. + public static OnGraphDelegate OnGraphPostScan; + + /// Called for each path before searching. Be careful when using multithreading since this will be called from a different thread. + public static OnPathDelegate OnPathPreSearch; + + /// Called for each path after searching. Be careful when using multithreading since this will be called from a different thread. + public static OnPathDelegate OnPathPostSearch; + + /// Called before starting the scanning + public static OnScanDelegate OnPreScan; + + /// Called after scanning. This is called before applying links, flood-filling the graphs and other post processing. + public static OnScanDelegate OnPostScan; + + /// Called after scanning has completed fully. This is called as the last thing in the Scan function. + public static OnScanDelegate OnLatePostScan; + + /// Called when any graphs are updated. Register to for example recalculate the path whenever a graph changes. + public static OnScanDelegate OnGraphsUpdated; + + /// + /// Called when pathID overflows 65536 and resets back to zero. + /// Note: This callback will be cleared every time it is called, so if you want to register to it repeatedly, register to it directly on receiving the callback as well. + /// + public static System.Action On65KOverflow; + + /// Deprecated: + [System.ObsoleteAttribute] + public System.Action OnGraphsWillBeUpdated; + + /// Deprecated: + [System.ObsoleteAttribute] + public System.Action OnGraphsWillBeUpdated2; + + /* @} */ + #endregion + + #region MemoryStructures + + /// Processes graph updates + readonly GraphUpdateProcessor graphUpdates; + + /// Holds a hierarchical graph to speed up some queries like if there is a path between two nodes + internal readonly HierarchicalGraph hierarchicalGraph = new HierarchicalGraph(); + + /// + /// Handles navmesh cuts. + /// See: + /// + public readonly NavmeshUpdates navmeshUpdates = new NavmeshUpdates(); + + /// Processes work items + readonly WorkItemProcessor workItems; + + /// Holds all paths waiting to be calculated and calculates them + PathProcessor pathProcessor; + + bool graphUpdateRoutineRunning = false; + + /// Makes sure QueueGraphUpdates will not queue multiple graph update orders + bool graphUpdatesWorkItemAdded = false; + + /// + /// Time the last graph update was done. + /// Used to group together frequent graph updates to batches + /// + float lastGraphUpdate = -9999F; + + /// Held if any work items are currently queued + PathProcessor.GraphUpdateLock workItemLock; + + /// Holds all completed paths waiting to be returned to where they were requested + internal readonly PathReturnQueue pathReturnQueue; + + /// + /// Holds settings for heuristic optimization. + /// See: heuristic-opt (view in online documentation for working links) + /// + public EuclideanEmbedding euclideanEmbedding = new EuclideanEmbedding(); + + #endregion + + /// + /// Shows or hides graph inspectors. + /// Used internally by the editor + /// + public bool showGraphs = false; + + /// + /// The next unused Path ID. + /// Incremented for every call to GetNextPathID + /// + private ushort nextFreePathID = 1; + + private AstarPath () { + pathReturnQueue = new PathReturnQueue(this); + + // Make sure that the pathProcessor is never null + pathProcessor = new PathProcessor(this, pathReturnQueue, 1, false); + + workItems = new WorkItemProcessor(this); + graphUpdates = new GraphUpdateProcessor(this); + + // Forward graphUpdates.OnGraphsUpdated to AstarPath.OnGraphsUpdated + graphUpdates.OnGraphsUpdated += () => { + if (OnGraphsUpdated != null) { + OnGraphsUpdated(this); + } + }; + } + + /// + /// Returns tag names. + /// Makes sure that the tag names array is not null and of length 32. + /// If it is null or not of length 32, it creates a new array and fills it with 0,1,2,3,4 etc... + /// See: AstarPath.FindTagNames + /// + public string[] GetTagNames () { + if (tagNames == null || tagNames.Length != 32) { + tagNames = new string[32]; + for (int i = 0; i < tagNames.Length; i++) { + tagNames[i] = ""+i; + } + tagNames[0] = "Basic Ground"; + } + return tagNames; + } + + /// + /// Used outside of play mode to initialize the AstarPath object even if it has not been selected in the inspector yet. + /// This will set the property and deserialize all graphs. + /// + /// This is useful if you want to do changes to the graphs in the editor outside of play mode, but cannot be sure that the graphs have been deserialized yet. + /// In play mode this method does nothing. + /// + public static void FindAstarPath () { + if (Application.isPlaying) return; + if (active == null) active = GameObject.FindObjectOfType(); + if (active != null && (active.data.graphs == null || active.data.graphs.Length == 0)) active.data.DeserializeGraphs(); + } + + /// + /// Tries to find an AstarPath object and return tag names. + /// If an AstarPath object cannot be found, it returns an array of length 1 with an error message. + /// See: AstarPath.GetTagNames + /// + public static string[] FindTagNames () { + FindAstarPath(); + return active != null? active.GetTagNames () : new string[1] { "There is no AstarPath component in the scene" }; + } + + /// Returns the next free path ID + internal ushort GetNextPathID () { + if (nextFreePathID == 0) { + nextFreePathID++; + + if (On65KOverflow != null) { + System.Action tmp = On65KOverflow; + On65KOverflow = null; + tmp(); + } + } + return nextFreePathID++; + } + + void RecalculateDebugLimits () { + debugFloor = float.PositiveInfinity; + debugRoof = float.NegativeInfinity; + + bool ignoreSearchTree = !showSearchTree || debugPathData == null; + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] != null && graphs[i].drawGizmos) { + graphs[i].GetNodes(node => { + if (node.Walkable && (ignoreSearchTree || Pathfinding.Util.GraphGizmoHelper.InSearchTree(node, debugPathData, debugPathID))) { + if (debugMode == GraphDebugMode.Penalty) { + debugFloor = Mathf.Min(debugFloor, node.Penalty); + debugRoof = Mathf.Max(debugRoof, node.Penalty); + } else if (debugPathData != null) { + var rnode = debugPathData.GetPathNode(node); + switch (debugMode) { + case GraphDebugMode.F: + debugFloor = Mathf.Min(debugFloor, rnode.F); + debugRoof = Mathf.Max(debugRoof, rnode.F); + break; + case GraphDebugMode.G: + debugFloor = Mathf.Min(debugFloor, rnode.G); + debugRoof = Mathf.Max(debugRoof, rnode.G); + break; + case GraphDebugMode.H: + debugFloor = Mathf.Min(debugFloor, rnode.H); + debugRoof = Mathf.Max(debugRoof, rnode.H); + break; + } + } + } + }); + } + } + + if (float.IsInfinity(debugFloor)) { + debugFloor = 0; + debugRoof = 1; + } + + // Make sure they are not identical, that will cause the color interpolation to fail + if (debugRoof-debugFloor < 1) debugRoof += 1; + } + + Pathfinding.Util.RetainedGizmos gizmos = new Pathfinding.Util.RetainedGizmos(); + + /// Calls OnDrawGizmos on graph generators + private void OnDrawGizmos () { + // Make sure the singleton pattern holds + // Might not hold if the Awake method + // has not been called yet + if (active == null) active = this; + + if (active != this || graphs == null) { + return; + } + + // In Unity one can select objects in the scene view by simply clicking on them with the mouse. + // Graph gizmos interfere with this however. If we would draw a mesh here the user would + // not be able to select whatever was behind it because the gizmos would block them. + // (presumably Unity cannot associate the gizmos with the AstarPath component because we are using + // Graphics.DrawMeshNow to draw most gizmos). It turns out that when scene picking happens + // then Event.current.type will be 'mouseUp'. We will therefore ignore all events which are + // not repaint events to make sure that the gizmos do not interfere with any kind of scene picking. + // This will not have any visual impact as only repaint events will result in any changes on the screen. + // From testing it seems the only events that can happen during OnDrawGizmos are the mouseUp and repaint events. + if (Event.current.type != EventType.Repaint) return; + + colorSettings.PushToStatic(this); + + AstarProfiler.StartProfile("OnDrawGizmos"); + + if (workItems.workItemsInProgress || isScanning) { + // If updating graphs, graph info might not be valid right now + // so just draw the same thing as last frame. + // Also if the scene has multiple cameras (or in the editor if we have a scene view and a game view) we + // just calculate the mesh once and then redraw the existing one for the other cameras. + // This improves performance quite a bit. + gizmos.DrawExisting(); + } else { + if (showNavGraphs && !manualDebugFloorRoof) { + RecalculateDebugLimits(); + } + + Profiler.BeginSample("Graph.OnDrawGizmos"); + // Loop through all graphs and draw their gizmos + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] != null && graphs[i].drawGizmos) + graphs[i].OnDrawGizmos(gizmos, showNavGraphs); + } + Profiler.EndSample(); + + if (showNavGraphs) { + euclideanEmbedding.OnDrawGizmos(); + if (debugMode == GraphDebugMode.HierarchicalNode) hierarchicalGraph.OnDrawGizmos(gizmos); + } + } + + gizmos.FinalizeDraw(); + + AstarProfiler.EndProfile("OnDrawGizmos"); + } + +#if !ASTAR_NO_GUI + /// + /// Draws the InGame debugging (if enabled), also shows the fps if 'L' is pressed down. + /// See: PathLog + /// + private void OnGUI () { + if (logPathResults == PathLog.InGame && inGameDebugPath != "") { + GUI.Label(new Rect(5, 5, 400, 600), inGameDebugPath); + } + } +#endif + + /// + /// Prints path results to the log. What it prints can be controled using . + /// See: + /// See: PathLog + /// See: Pathfinding.Path.DebugString + /// + private void LogPathResults (Path path) { + if (logPathResults != PathLog.None && (path.error || logPathResults != PathLog.OnlyErrors)) { + string debug = path.DebugString(logPathResults); + + if (logPathResults == PathLog.InGame) { + inGameDebugPath = debug; + } else if (path.error) { + Debug.LogWarning(debug); + } else { + Debug.Log(debug); + } + } + } + + /// + /// Checks if any work items need to be executed + /// then runs pathfinding for a while (if not using multithreading because + /// then the calculation happens in other threads) + /// and then returns any calculated paths to the + /// scripts that requested them. + /// + /// See: PerformBlockingActions + /// See: PathProcessor.TickNonMultithreaded + /// See: PathReturnQueue.ReturnPaths + /// + private void Update () { + // This class uses the [ExecuteInEditMode] attribute + // So Update is called even when not playing + // Don't do anything when not in play mode + if (!Application.isPlaying) return; + + navmeshUpdates.Update(); + + // Execute blocking actions such as graph updates + // when not scanning + if (!isScanning) { + PerformBlockingActions(); + } + + // Calculates paths when not using multithreading + pathProcessor.TickNonMultithreaded(); + + // Return calculated paths + pathReturnQueue.ReturnPaths(true); + } + + private void PerformBlockingActions (bool force = false) { + if (workItemLock.Held && pathProcessor.queue.AllReceiversBlocked) { + // Return all paths before starting blocking actions + // since these might change the graph and make returned paths invalid (at least the nodes) + pathReturnQueue.ReturnPaths(false); + + Profiler.BeginSample("Work Items"); + if (workItems.ProcessWorkItems(force)) { + // At this stage there are no more work items, resume pathfinding threads + workItemLock.Release(); + } + Profiler.EndSample(); + } + } + + /// + /// Call during work items to queue a flood fill. + /// Deprecated: This method has been moved. Use the method on the context object that can be sent with work item delegates instead + /// + /// AstarPath.active.AddWorkItem(new AstarWorkItem(() => { + /// // Safe to update graphs here + /// var node = AstarPath.active.GetNearest(transform.position).node; + /// node.Walkable = false; + /// })); + /// + /// + /// See: + /// + [System.Obsolete("This method has been moved. Use the method on the context object that can be sent with work item delegates instead")] + public void QueueWorkItemFloodFill () { + throw new System.Exception("This method has been moved. Use the method on the context object that can be sent with work item delegates instead"); + } + + /// + /// If a WorkItem needs to have a valid flood fill during execution, call this method to ensure there are no pending flood fills. + /// Deprecated: This method has been moved. Use the method on the context object that can be sent with work item delegates instead + /// + /// AstarPath.active.AddWorkItem(new AstarWorkItem(() => { + /// // Safe to update graphs here + /// var node = AstarPath.active.GetNearest(transform.position).node; + /// node.Walkable = false; + /// })); + /// + /// + /// See: + /// + [System.Obsolete("This method has been moved. Use the method on the context object that can be sent with work item delegates instead")] + public void EnsureValidFloodFill () { + throw new System.Exception("This method has been moved. Use the method on the context object that can be sent with work item delegates instead"); + } + + /// + /// Add a work item to be processed when pathfinding is paused. + /// Convenience method that is equivalent to + /// + /// AddWorkItem(new AstarWorkItem(callback)); + /// + /// + /// See: + /// + public void AddWorkItem (System.Action callback) { + AddWorkItem(new AstarWorkItem(callback)); + } + + /// + /// Add a work item to be processed when pathfinding is paused. + /// Convenience method that is equivalent to + /// + /// AddWorkItem(new AstarWorkItem(callback)); + /// + /// + /// See: + /// + public void AddWorkItem (System.Action callback) { + AddWorkItem(new AstarWorkItem(callback)); + } + + /// + /// Add a work item to be processed when pathfinding is paused. + /// + /// The work item will be executed when it is safe to update nodes. This is defined as between the path searches. + /// When using more threads than one, calling this often might decrease pathfinding performance due to a lot of idling in the threads. + /// Not performance as in it will use much CPU power, but performance as in the number of paths per second will probably go down + /// (though your framerate might actually increase a tiny bit). + /// + /// You should only call this function from the main unity thread (i.e normal game code). + /// + /// + /// AstarPath.active.AddWorkItem(new AstarWorkItem(() => { + /// // Safe to update graphs here + /// var node = AstarPath.active.GetNearest(transform.position).node; + /// node.Walkable = false; + /// })); + /// + /// + /// + /// AstarPath.active.AddWorkItem(() => { + /// // Safe to update graphs here + /// var node = AstarPath.active.GetNearest(transform.position).node; + /// node.position = (Int3)transform.position; + /// }); + /// + /// + /// See: + /// + public void AddWorkItem (AstarWorkItem item) { + workItems.AddWorkItem(item); + + // Make sure pathfinding is stopped and work items are processed + if (!workItemLock.Held) { + workItemLock = PausePathfindingSoon(); + } + +#if UNITY_EDITOR + // If not playing, execute instantly + if (!Application.isPlaying) { + FlushWorkItems(); + } +#endif + } + + #region GraphUpdateMethods + + /// + /// Will apply queued graph updates as soon as possible, regardless of . + /// Calling this multiple times will not create multiple callbacks. + /// This function is useful if you are limiting graph updates, but you want a specific graph update to be applied as soon as possible regardless of the time limit. + /// Note that this does not block until the updates are done, it merely bypasses the time limit. + /// + /// See: + /// + public void QueueGraphUpdates () { + if (!graphUpdatesWorkItemAdded) { + graphUpdatesWorkItemAdded = true; + var workItem = graphUpdates.GetWorkItem(); + + // Add a new work item which first + // sets the graphUpdatesWorkItemAdded flag to false + // and then processes the graph updates + AddWorkItem(new AstarWorkItem(() => { + graphUpdatesWorkItemAdded = false; + lastGraphUpdate = Time.realtimeSinceStartup; + + workItem.init(); + }, workItem.update)); + } + } + + /// + /// Waits a moment with updating graphs. + /// If batchGraphUpdates is set, we want to keep some space between them to let pathfinding threads running and then calculate all queued calls at once + /// + IEnumerator DelayedGraphUpdate () { + graphUpdateRoutineRunning = true; + + yield return new WaitForSeconds(graphUpdateBatchingInterval-(Time.realtimeSinceStartup-lastGraphUpdate)); + QueueGraphUpdates(); + graphUpdateRoutineRunning = false; + } + + /// + /// Update all graphs within bounds after delay seconds. + /// The graphs will be updated as soon as possible. + /// + /// See: FlushGraphUpdates + /// See: batchGraphUpdates + /// See: graph-updates (view in online documentation for working links) + /// + public void UpdateGraphs (Bounds bounds, float delay) { + UpdateGraphs(new GraphUpdateObject(bounds), delay); + } + + /// + /// Update all graphs using the GraphUpdateObject after delay seconds. + /// This can be used to, e.g make all nodes in a region unwalkable, or set them to a higher penalty. + /// + /// See: FlushGraphUpdates + /// See: batchGraphUpdates + /// See: graph-updates (view in online documentation for working links) + /// + public void UpdateGraphs (GraphUpdateObject ob, float delay) { + StartCoroutine(UpdateGraphsInternal(ob, delay)); + } + + /// Update all graphs using the GraphUpdateObject after delay seconds + IEnumerator UpdateGraphsInternal (GraphUpdateObject ob, float delay) { + yield return new WaitForSeconds(delay); + UpdateGraphs(ob); + } + + /// + /// Update all graphs within bounds. + /// The graphs will be updated as soon as possible. + /// + /// This is equivalent to + /// + /// UpdateGraphs(new GraphUpdateObject(bounds)); + /// + /// + /// See: FlushGraphUpdates + /// See: batchGraphUpdates + /// See: graph-updates (view in online documentation for working links) + /// + public void UpdateGraphs (Bounds bounds) { + UpdateGraphs(new GraphUpdateObject(bounds)); + } + + /// + /// Update all graphs using the GraphUpdateObject. + /// This can be used to, e.g make all nodes in a region unwalkable, or set them to a higher penalty. + /// The graphs will be updated as soon as possible (with respect to + /// + /// See: FlushGraphUpdates + /// See: batchGraphUpdates + /// See: graph-updates (view in online documentation for working links) + /// + public void UpdateGraphs (GraphUpdateObject ob) { + graphUpdates.AddToQueue(ob); + + // If we should limit graph updates, start a coroutine which waits until we should update graphs + if (batchGraphUpdates && Time.realtimeSinceStartup-lastGraphUpdate < graphUpdateBatchingInterval) { + if (!graphUpdateRoutineRunning) { + StartCoroutine(DelayedGraphUpdate()); + } + } else { + // Otherwise, graph updates should be carried out as soon as possible + QueueGraphUpdates(); + } + } + + /// + /// Forces graph updates to complete in a single frame. + /// This will force the pathfinding threads to finish calculating the path they are currently calculating (if any) and then pause. + /// When all threads have paused, graph updates will be performed. + /// Warning: Using this very often (many times per second) can reduce your fps due to a lot of threads waiting for one another. + /// But you probably wont have to worry about that. + /// + /// Note: This is almost identical to , but added for more descriptive name. + /// This function will also override any time limit delays for graph updates. + /// This is because graph updates are implemented using work items. + /// So calling this function will also execute any other work items (if any are queued). + /// + /// Will not do anything if there are no graph updates queued (not even execute other work items). + /// + public void FlushGraphUpdates () { + if (IsAnyGraphUpdateQueued) { + QueueGraphUpdates(); + FlushWorkItems(); + } + } + + #endregion + + /// + /// Forces work items to complete in a single frame. + /// This will force all work items to run immidiately. + /// This will force the pathfinding threads to finish calculating the path they are currently calculating (if any) and then pause. + /// When all threads have paused, work items will be executed (which can be e.g graph updates). + /// + /// Warning: Using this very often (many times per second) can reduce your fps due to a lot of threads waiting for one another. + /// But you probably wont have to worry about that + /// + /// Note: This is almost (note almost) identical to , but added for more descriptive name. + /// + /// Will not do anything if there are no queued work items waiting to run. + /// + public void FlushWorkItems () { + if (workItems.anyQueued) { + var graphLock = PausePathfinding(); + PerformBlockingActions(true); + graphLock.Release(); + } + } + + /// + /// Make sure work items are executed. + /// + /// See: AddWorkItem + /// + /// Deprecated: Use instead. + /// + /// If true, pathfinding will be allowed to start running immediately after completing all work items. + /// If true, work items that usually take more than one frame to complete will be forced to complete during this call. + /// If false, then after this call there might still be work left to do. + [System.Obsolete("Use FlushWorkItems() instead")] + public void FlushWorkItems (bool unblockOnComplete, bool block) { + var graphLock = PausePathfinding(); + + // Run tasks + PerformBlockingActions(block); + graphLock.Release(); + } + + /// + /// Forces thread safe callbacks to run. + /// Deprecated: Use instead + /// + [System.Obsolete("Use FlushWorkItems instead")] + public void FlushThreadSafeCallbacks () { + FlushWorkItems(); + } + + /// + /// Calculates number of threads to use. + /// If count is not Automatic, simply returns count casted to an int. + /// Returns: An int specifying how many threads to use, 0 means a coroutine should be used for pathfinding instead of a separate thread. + /// + /// If count is set to Automatic it will return a value based on the number of processors and memory for the current system. + /// If memory is <= 512MB or logical cores are <= 1, it will return 0. If memory is <= 1024 it will clamp threads to max 2. + /// Otherwise it will return the number of logical cores clamped to 6. + /// + /// When running on WebGL this method always returns 0 + /// + public static int CalculateThreadCount (ThreadCount count) { +#if UNITY_WEBGL + return 0; +#else + if (count == ThreadCount.AutomaticLowLoad || count == ThreadCount.AutomaticHighLoad) { + int logicalCores = Mathf.Max(1, SystemInfo.processorCount); + int memory = SystemInfo.systemMemorySize; + + if (memory <= 0) { + Debug.LogError("Machine reporting that is has <= 0 bytes of RAM. This is definitely not true, assuming 1 GiB"); + memory = 1024; + } + + if (logicalCores <= 1) return 0; + if (memory <= 512) return 0; + + return 1; + } else { + return (int)count > 0 ? 1 : 0; + } +#endif + } + + /// + /// Sets up all needed variables and scans the graphs. + /// Calls Initialize, starts the ReturnPaths coroutine and scans all graphs. + /// Also starts threads if using multithreading + /// See: + /// + protected override void Awake () { + base.Awake(); + // Very important to set this. Ensures the singleton pattern holds + active = this; + + if (FindObjectsOfType(typeof(AstarPath)).Length > 1) { + Debug.LogError("You should NOT have more than one AstarPath component in the scene at any time.\n" + + "This can cause serious errors since the AstarPath component builds around a singleton pattern."); + } + + // Disable GUILayout to gain some performance, it is not used in the OnGUI call + useGUILayout = false; + + // This class uses the [ExecuteInEditMode] attribute + // So Awake is called even when not playing + // Don't do anything when not in play mode + if (!Application.isPlaying) return; + + if (OnAwakeSettings != null) { + OnAwakeSettings(); + } + + // To make sure all graph modifiers have been enabled before scan (to avoid script execution order issues) + GraphModifier.FindAllModifiers(); + RelevantGraphSurface.FindAllGraphSurfaces(); + + InitializePathProcessor(); + InitializeProfiler(); + ConfigureReferencesInternal(); + InitializeAstarData(); + + // Flush work items, possibly added in InitializeAstarData to load graph data + FlushWorkItems(); + + euclideanEmbedding.dirty = true; + + navmeshUpdates.OnEnable(); + + if (scanOnStartup && (!data.cacheStartup || data.file_cachedStartup == null)) { + Scan(); + } + } + + /// Initializes the field + void InitializePathProcessor () { + int numThreads = CalculateThreadCount(threadCount); + + // Outside of play mode everything is synchronous, so no threads are used. + if (!Application.isPlaying) numThreads = 0; + + // Trying to prevent simple modding to add support for more than one thread + if (numThreads > 1) { + threadCount = ThreadCount.One; + numThreads = 1; + } + + int numProcessors = Mathf.Max(numThreads, 1); + bool multithreaded = numThreads > 0; + pathProcessor = new PathProcessor(this, pathReturnQueue, numProcessors, multithreaded); + + pathProcessor.OnPathPreSearch += path => { + var tmp = OnPathPreSearch; + if (tmp != null) tmp(path); + }; + + pathProcessor.OnPathPostSearch += path => { + LogPathResults(path); + var tmp = OnPathPostSearch; + if (tmp != null) tmp(path); + }; + + // Sent every time the path queue is unblocked + pathProcessor.OnQueueUnblocked += () => { + if (euclideanEmbedding.dirty) { + euclideanEmbedding.RecalculateCosts(); + } + }; + + if (multithreaded) { + graphUpdates.EnableMultithreading(); + } + } + + /// Does simple error checking + internal void VerifyIntegrity () { + if (active != this) { + throw new System.Exception("Singleton pattern broken. Make sure you only have one AstarPath object in the scene"); + } + + if (data == null) { + throw new System.NullReferenceException("data is null... A* not set up correctly?"); + } + + if (data.graphs == null) { + data.graphs = new NavGraph[0]; + data.UpdateShortcuts(); + } + } + + /// \cond internal + /// + /// Internal method to make sure is set to this object and that is not null. + /// Also calls OnEnable for the and initializes data.userConnections if it wasn't initialized before + /// + /// Warning: This is mostly for use internally by the system. + /// + public void ConfigureReferencesInternal () { + active = this; + data = data ?? new AstarData(); + colorSettings = colorSettings ?? new AstarColor(); + colorSettings.PushToStatic(this); + } + /// \endcond + + /// Calls AstarProfiler.InitializeFastProfile + void InitializeProfiler () { + AstarProfiler.InitializeFastProfile(new string[14] { + "Prepare", //0 + "Initialize", //1 + "CalculateStep", //2 + "Trace", //3 + "Open", //4 + "UpdateAllG", //5 + "Add", //6 + "Remove", //7 + "PreProcessing", //8 + "Callback", //9 + "Overhead", //10 + "Log", //11 + "ReturnPaths", //12 + "PostPathCallback" //13 + }); + } + + /// + /// Initializes the AstarData class. + /// Searches for graph types, calls Awake on and on all graphs + /// + /// See: AstarData.FindGraphTypes + /// + void InitializeAstarData () { + data.FindGraphTypes(); + data.Awake(); + data.UpdateShortcuts(); + } + + /// Cleans up meshes to avoid memory leaks + void OnDisable () { + gizmos.ClearCache(); + } + + /// + /// Clears up variables and other stuff, destroys graphs. + /// Note that when destroying an AstarPath object, all static variables such as callbacks will be cleared. + /// + void OnDestroy () { + // This class uses the [ExecuteInEditMode] attribute + // So OnDestroy is called even when not playing + // Don't do anything when not in play mode + if (!Application.isPlaying) return; + + if (logPathResults == PathLog.Heavy) + Debug.Log("+++ AstarPath Component Destroyed - Cleaning Up Pathfinding Data +++"); + + if (active != this) return; + + // Block until the pathfinding threads have + // completed their current path calculation + PausePathfinding(); + + navmeshUpdates.OnDisable(); + + euclideanEmbedding.dirty = false; + FlushWorkItems(); + + // Don't accept any more path calls to this AstarPath instance. + // This will cause all pathfinding threads to exit (if any exist) + pathProcessor.queue.TerminateReceivers(); + + if (logPathResults == PathLog.Heavy) + Debug.Log("Processing Possible Work Items"); + + // Stop the graph update thread (if it is running) + graphUpdates.DisableMultithreading(); + + // Try to join pathfinding threads + pathProcessor.JoinThreads(); + + if (logPathResults == PathLog.Heavy) + Debug.Log("Returning Paths"); + + + // Return all paths + pathReturnQueue.ReturnPaths(false); + + if (logPathResults == PathLog.Heavy) + Debug.Log("Destroying Graphs"); + + + // Clean up graph data + data.OnDestroy(); + + if (logPathResults == PathLog.Heavy) + Debug.Log("Cleaning up variables"); + + // Clear variables up, static variables are good to clean up, otherwise the next scene might get weird data + + // Clear all callbacks + OnAwakeSettings = null; + OnGraphPreScan = null; + OnGraphPostScan = null; + OnPathPreSearch = null; + OnPathPostSearch = null; + OnPreScan = null; + OnPostScan = null; + OnLatePostScan = null; + On65KOverflow = null; + OnGraphsUpdated = null; + + active = null; + } + + #region ScanMethods + + /// + /// Floodfills starting from the specified node. + /// + /// Deprecated: Deprecated: Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes + /// + [System.Obsolete("Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes")] + public void FloodFill (GraphNode seed) { + } + + /// + /// Floodfills starting from 'seed' using the specified area. + /// + /// Deprecated: Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes + /// + [System.Obsolete("Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes")] + public void FloodFill (GraphNode seed, uint area) { + } + + /// + /// Floodfills all graphs and updates areas for every node. + /// The different colored areas that you see in the scene view when looking at graphs + /// are called just 'areas', this method calculates which nodes are in what areas. + /// See: Pathfinding.Node.area + /// + /// Deprecated: Avoid using. This will force a full recalculation of the connected components. In most cases the HierarchicalGraph class takes care of things automatically behind the scenes now. + /// + [ContextMenu("Flood Fill Graphs")] + [System.Obsolete("Avoid using. This will force a full recalculation of the connected components. In most cases the HierarchicalGraph class takes care of things automatically behind the scenes now.")] + public void FloodFill () { + hierarchicalGraph.RecalculateAll(); + workItems.OnFloodFill(); + } + + /// + /// Returns a new global node index. + /// Warning: This method should not be called directly. It is used by the GraphNode constructor. + /// + internal int GetNewNodeIndex () { + return pathProcessor.GetNewNodeIndex(); + } + + /// + /// Initializes temporary path data for a node. + /// Warning: This method should not be called directly. It is used by the GraphNode constructor. + /// + internal void InitializeNode (GraphNode node) { + pathProcessor.InitializeNode(node); + } + + /// + /// Internal method to destroy a given node. + /// This is to be called after the node has been disconnected from the graph so that it cannot be reached from any other nodes. + /// It should only be called during graph updates, that is when the pathfinding threads are either not running or paused. + /// + /// Warning: This method should not be called by user code. It is used internally by the system. + /// + internal void DestroyNode (GraphNode node) { + pathProcessor.DestroyNode(node); + } + + /// + /// Blocks until all pathfinding threads are paused and blocked. + /// Deprecated: Use instead. Make sure to call Release on the returned lock. + /// + [System.Obsolete("Use PausePathfinding instead. Make sure to call Release on the returned lock.", true)] + public void BlockUntilPathQueueBlocked () { + } + + /// + /// Blocks until all pathfinding threads are paused and blocked. + /// + /// + /// var graphLock = AstarPath.active.PausePathfinding(); + /// // Here we can modify the graphs safely. For example by adding a new node to a point graph + /// var node = AstarPath.active.data.pointGraph.AddNode((Int3) new Vector3(3, 1, 4)); + /// + /// // Allow pathfinding to resume + /// graphLock.Release(); + /// + /// + /// Returns: A lock object. You need to call on that object to allow pathfinding to resume. + /// Note: In most cases this should not be called from user code. Use the method instead. + /// + /// See: + /// + public PathProcessor.GraphUpdateLock PausePathfinding () { + return pathProcessor.PausePathfinding(true); + } + + /// Blocks the path queue so that e.g work items can be performed + PathProcessor.GraphUpdateLock PausePathfindingSoon () { + return pathProcessor.PausePathfinding(false); + } + + /// + /// Scans a particular graph. + /// Calling this method will recalculate the specified graph. + /// This method is pretty slow (depending on graph type and graph complexity of course), so it is advisable to use + /// smaller graph updates whenever possible. + /// + /// + /// // Recalculate all graphs + /// AstarPath.active.Scan(); + /// + /// // Recalculate only the first grid graph + /// var graphToScan = AstarPath.active.data.gridGraph; + /// AstarPath.active.Scan(graphToScan); + /// + /// // Recalculate only the first and third graphs + /// var graphsToScan = new [] { AstarPath.active.data.graphs[0], AstarPath.active.data.graphs[2] }; + /// AstarPath.active.Scan(graphsToScan); + /// + /// + /// See: graph-updates (view in online documentation for working links) + /// See: ScanAsync + /// + public void Scan (NavGraph graphToScan) { + if (graphToScan == null) throw new System.ArgumentNullException(); + Scan(new NavGraph[] { graphToScan }); + } + + /// + /// Scans all specified graphs. + /// + /// Calling this method will recalculate all specified graphs or all graphs if the graphsToScan parameter is null. + /// This method is pretty slow (depending on graph type and graph complexity of course), so it is advisable to use + /// smaller graph updates whenever possible. + /// + /// + /// // Recalculate all graphs + /// AstarPath.active.Scan(); + /// + /// // Recalculate only the first grid graph + /// var graphToScan = AstarPath.active.data.gridGraph; + /// AstarPath.active.Scan(graphToScan); + /// + /// // Recalculate only the first and third graphs + /// var graphsToScan = new [] { AstarPath.active.data.graphs[0], AstarPath.active.data.graphs[2] }; + /// AstarPath.active.Scan(graphsToScan); + /// + /// + /// See: graph-updates (view in online documentation for working links) + /// See: ScanAsync + /// + /// The graphs to scan. If this parameter is null then all graphs will be scanned + public void Scan (NavGraph[] graphsToScan = null) { + var prevProgress = new Progress(); + + Profiler.BeginSample("Scan"); + Profiler.BeginSample("Init"); + foreach (var p in ScanAsync(graphsToScan)) { + if (prevProgress.description != p.description) { +#if !NETFX_CORE && UNITY_EDITOR + Profiler.EndSample(); + Profiler.BeginSample(p.description); + // Log progress to the console + System.Console.WriteLine(p.description); + prevProgress = p; +#endif + } + } + Profiler.EndSample(); + Profiler.EndSample(); + } + + /// + /// Scans a particular graph asynchronously. This is a IEnumerable, you can loop through it to get the progress + /// + /// foreach (Progress progress in AstarPath.active.ScanAsync()) { + /// Debug.Log("Scanning... " + progress.description + " - " + (progress.progress*100).ToString("0") + "%"); + /// } + /// + /// You can scan graphs asyncronously by yielding when you loop through the progress. + /// Note that this does not guarantee a good framerate, but it will allow you + /// to at least show a progress bar during scanning. + /// + /// IEnumerator Start () { + /// foreach (Progress progress in AstarPath.active.ScanAsync()) { + /// Debug.Log("Scanning... " + progress.description + " - " + (progress.progress*100).ToString("0") + "%"); + /// yield return null; + /// } + /// } + /// + /// + /// See: Scan + /// + public IEnumerable ScanAsync (NavGraph graphToScan) { + if (graphToScan == null) throw new System.ArgumentNullException(); + return ScanAsync(new NavGraph[] { graphToScan }); + } + + /// + /// Scans all specified graphs asynchronously. This is a IEnumerable, you can loop through it to get the progress + /// + /// + /// foreach (Progress progress in AstarPath.active.ScanAsync()) { + /// Debug.Log("Scanning... " + progress.description + " - " + (progress.progress*100).ToString("0") + "%"); + /// } + /// + /// You can scan graphs asyncronously by yielding when you loop through the progress. + /// Note that this does not guarantee a good framerate, but it will allow you + /// to at least show a progress bar during scanning. + /// + /// IEnumerator Start () { + /// foreach (Progress progress in AstarPath.active.ScanAsync()) { + /// Debug.Log("Scanning... " + progress.description + " - " + (progress.progress*100).ToString("0") + "%"); + /// yield return null; + /// } + /// } + /// + /// + /// See: Scan + /// + /// The graphs to scan. If this parameter is null then all graphs will be scanned + public IEnumerable ScanAsync (NavGraph[] graphsToScan = null) { + if (graphsToScan == null) graphsToScan = graphs; + + if (graphsToScan == null) { + yield break; + } + + if (isScanning) throw new System.InvalidOperationException("Another async scan is already running"); + + isScanning = true; + + VerifyIntegrity(); + + var graphUpdateLock = PausePathfinding(); + + // Make sure all paths that are in the queue to be returned + // are returned immediately + // Some modifiers (e.g the funnel modifier) rely on + // the nodes being valid when the path is returned + pathReturnQueue.ReturnPaths(false); + + if (!Application.isPlaying) { + data.FindGraphTypes(); + GraphModifier.FindAllModifiers(); + } + + int startFrame = Time.frameCount; + + yield return new Progress(0.05F, "Pre processing graphs"); + + // Yes, this constraint is trivial to circumvent + // the code is the same because it is annoying + // to have to have separate code for the free + // and the pro version that does essentially the same thing. + // I would appreciate if you purchased the pro version of the A* Pathfinding Project + // if you need async scanning. + if (Time.frameCount != startFrame) { + throw new System.Exception("Async scanning can only be done in the pro version of the A* Pathfinding Project"); + } + + if (OnPreScan != null) { + OnPreScan(this); + } + + GraphModifier.TriggerEvent(GraphModifier.EventType.PreScan); + + data.LockGraphStructure(); + + var watch = System.Diagnostics.Stopwatch.StartNew(); + + // Destroy previous nodes + for (int i = 0; i < graphsToScan.Length; i++) { + if (graphsToScan[i] != null) { + ((IGraphInternals)graphsToScan[i]).DestroyAllNodes(); + } + } + + // Loop through all graphs and scan them one by one + for (int i = 0; i < graphsToScan.Length; i++) { + // Skip null graphs + if (graphsToScan[i] == null) continue; + + // Just used for progress information + // This graph will advance the progress bar from minp to maxp + float minp = Mathf.Lerp(0.1F, 0.8F, (float)(i)/(graphsToScan.Length)); + float maxp = Mathf.Lerp(0.1F, 0.8F, (float)(i+0.95F)/(graphsToScan.Length)); + + var progressDescriptionPrefix = "Scanning graph " + (i+1) + " of " + graphsToScan.Length + " - "; + + // Like a foreach loop but it gets a little complicated because of the exception + // handling (it is not possible to yield inside try-except clause). + var coroutine = ScanGraph(graphsToScan[i]).GetEnumerator(); + while (true) { + try { + if (!coroutine.MoveNext()) break; + } catch { + isScanning = false; + data.UnlockGraphStructure(); + graphUpdateLock.Release(); + throw; + } + yield return coroutine.Current.MapTo(minp, maxp, progressDescriptionPrefix); + } + } + + data.UnlockGraphStructure(); + yield return new Progress(0.8F, "Post processing graphs"); + + if (OnPostScan != null) { + OnPostScan(this); + } + GraphModifier.TriggerEvent(GraphModifier.EventType.PostScan); + + FlushWorkItems(); + + yield return new Progress(0.9F, "Computing areas"); + + hierarchicalGraph.RecalculateIfNecessary(); + + yield return new Progress(0.95F, "Late post processing"); + + // Signal that we have stopped scanning here + // Note that no yields can happen after this point + // since then other parts of the system can start to interfere + isScanning = false; + + if (OnLatePostScan != null) { + OnLatePostScan(this); + } + GraphModifier.TriggerEvent(GraphModifier.EventType.LatePostScan); + + euclideanEmbedding.dirty = true; + euclideanEmbedding.RecalculatePivots(); + + // Perform any blocking actions + FlushWorkItems(); + // Resume pathfinding threads + graphUpdateLock.Release(); + + watch.Stop(); + lastScanTime = (float)watch.Elapsed.TotalSeconds; + + System.GC.Collect(); + + if (logPathResults != PathLog.None && logPathResults != PathLog.OnlyErrors) { + Debug.Log("Scanning - Process took "+(lastScanTime*1000).ToString("0")+" ms to complete"); + } + } + + IEnumerable ScanGraph (NavGraph graph) { + if (OnGraphPreScan != null) { + yield return new Progress(0, "Pre processing"); + OnGraphPreScan(graph); + } + + yield return new Progress(0, ""); + + foreach (var p in ((IGraphInternals)graph).ScanInternal()) { + yield return p.MapTo(0, 0.95f); + } + + yield return new Progress(0.95f, "Assigning graph indices"); + + // Assign the graph index to every node in the graph + graph.GetNodes(node => node.GraphIndex = (uint)graph.graphIndex); + + if (OnGraphPostScan != null) { + yield return new Progress(0.99f, "Post processing"); + OnGraphPostScan(graph); + } + } + + #endregion + + private static int waitForPathDepth = 0; + + /// + /// Wait for the specified path to be calculated. + /// Normally it takes a few frames for a path to get calculated and returned. + /// + /// Deprecated: This method has been renamed to . + /// + [System.Obsolete("This method has been renamed to BlockUntilCalculated")] + public static void WaitForPath (Path path) { + BlockUntilCalculated(path); + } + + /// + /// Blocks until the path has been calculated. + /// + /// Normally it takes a few frames for a path to be calculated and returned. + /// This function will ensure that the path will be calculated when this function returns + /// and that the callback for that path has been called. + /// + /// If requesting a lot of paths in one go and waiting for the last one to complete, + /// it will calculate most of the paths in the queue (only most if using multithreading, all if not using multithreading). + /// + /// Use this function only if you really need to. + /// There is a point to spreading path calculations out over several frames. + /// It smoothes out the framerate and makes sure requesting a large + /// number of paths at the same time does not cause lag. + /// + /// Note: Graph updates and other callbacks might get called during the execution of this function. + /// + /// When the pathfinder is shutting down. I.e in OnDestroy, this function will not do anything. + /// + /// \throws Exception if pathfinding is not initialized properly for this scene (most likely no AstarPath object exists) + /// or if the path has not been started yet. + /// Also throws an exception if critical errors occur such as when the pathfinding threads have crashed (which should not happen in normal cases). + /// This prevents an infinite loop while waiting for the path. + /// + /// See: Pathfinding.Path.WaitForPath + /// See: Pathfinding.Path.BlockUntilCalculated + /// + /// The path to wait for. The path must be started, otherwise an exception will be thrown. + public static void BlockUntilCalculated (Path path) { + if (active == null) + throw new System.Exception("Pathfinding is not correctly initialized in this scene (yet?). " + + "AstarPath.active is null.\nDo not call this function in Awake"); + + if (path == null) throw new System.ArgumentNullException("Path must not be null"); + + if (active.pathProcessor.queue.IsTerminating) return; + + if (path.PipelineState == PathState.Created) { + throw new System.Exception("The specified path has not been started yet."); + } + + waitForPathDepth++; + + if (waitForPathDepth == 5) { + Debug.LogError("You are calling the BlockUntilCalculated function recursively (maybe from a path callback). Please don't do this."); + } + + if (path.PipelineState < PathState.ReturnQueue) { + if (active.IsUsingMultithreading) { + while (path.PipelineState < PathState.ReturnQueue) { + if (active.pathProcessor.queue.IsTerminating) { + waitForPathDepth--; + throw new System.Exception("Pathfinding Threads seem to have crashed."); + } + + // Wait for threads to calculate paths + Thread.Sleep(1); + active.PerformBlockingActions(true); + } + } else { + while (path.PipelineState < PathState.ReturnQueue) { + if (active.pathProcessor.queue.IsEmpty && path.PipelineState != PathState.Processing) { + waitForPathDepth--; + throw new System.Exception("Critical error. Path Queue is empty but the path state is '" + path.PipelineState + "'"); + } + + // Calculate some paths + active.pathProcessor.TickNonMultithreaded(); + active.PerformBlockingActions(true); + } + } + } + + active.pathReturnQueue.ReturnPaths(false); + waitForPathDepth--; + } + + /// + /// Will send a callback when it is safe to update nodes. This is defined as between the path searches. + /// This callback will only be sent once and is nulled directly after the callback has been sent. + /// When using more threads than one, calling this often might decrease pathfinding performance due to a lot of idling in the threads. + /// Not performance as in it will use much CPU power, + /// but performance as in the number of paths per second will probably go down (though your framerate might actually increase a tiny bit) + /// + /// You should only call this function from the main unity thread (i.e normal game code). + /// + /// Version: Since version 4.0 this is equivalent to AddWorkItem(new AstarWorkItem(callback)). Previously the + /// callbacks added using this method would not be ordered with respect to other work items, so they could be + /// executed before other work items or after them. + /// + /// Deprecated: Use instead. Note the slight change in behavior (mentioned above). + /// + [System.Obsolete("Use AddWorkItem(System.Action) instead. Note the slight change in behavior (mentioned in the documentation).")] + public static void RegisterSafeUpdate (System.Action callback) { + active.AddWorkItem(new AstarWorkItem(callback)); + } + + /// + /// Adds the path to a queue so that it will be calculated as soon as possible. + /// The callback specified when constructing the path will be called when the path has been calculated. + /// Usually you should use the Seeker component instead of calling this function directly. + /// + /// The path that should be enqueued. + /// If true, the path will be pushed to the front of the queue, bypassing all waiting paths and making it the next path to be calculated. + /// This can be useful if you have a path which you want to prioritize over all others. Be careful to not overuse it though. + /// If too many paths are put in the front of the queue often, this can lead to normal paths having to wait a very long time before being calculated. + public static void StartPath (Path path, bool pushToFront = false) { + // Copy to local variable to avoid multithreading issues + var astar = active; + + if (System.Object.ReferenceEquals(astar, null)) { + Debug.LogError("There is no AstarPath object in the scene or it has not been initialized yet"); + return; + } + + if (path.PipelineState != PathState.Created) { + throw new System.Exception("The path has an invalid state. Expected " + PathState.Created + " found " + path.PipelineState + "\n" + + "Make sure you are not requesting the same path twice"); + } + + if (astar.pathProcessor.queue.IsTerminating) { + path.FailWithError("No new paths are accepted"); + return; + } + + if (astar.graphs == null || astar.graphs.Length == 0) { + Debug.LogError("There are no graphs in the scene"); + path.FailWithError("There are no graphs in the scene"); + Debug.LogError(path.errorLog); + return; + } + + path.Claim(astar); + + // Will increment p.state to PathState.PathQueue + ((IPathInternals)path).AdvanceState(PathState.PathQueue); + if (pushToFront) { + astar.pathProcessor.queue.PushFront(path); + } else { + astar.pathProcessor.queue.Push(path); + } + + // Outside of play mode, all path requests are synchronous + if (!Application.isPlaying) { + BlockUntilCalculated(path); + } + } + + /// + /// Cached NNConstraint.None to avoid unnecessary allocations. + /// This should ideally be fixed by making NNConstraint an immutable class/struct. + /// + static readonly NNConstraint NNConstraintNone = NNConstraint.None; + + /// + /// Returns the nearest node to a position using the specified NNConstraint. + /// Searches through all graphs for their nearest nodes to the specified position and picks the closest one.\n + /// Using the NNConstraint.None constraint. + /// + /// + /// // Find the closest node to this GameObject's position + /// GraphNode node = AstarPath.active.GetNearest(transform.position).node; + /// + /// if (node.Walkable) { + /// // Yay, the node is walkable, we can place a tower here or something + /// } + /// + /// + /// See: Pathfinding.NNConstraint + /// + public NNInfo GetNearest (Vector3 position) { + return GetNearest(position, NNConstraintNone); + } + + /// + /// Returns the nearest node to a position using the specified NNConstraint. + /// Searches through all graphs for their nearest nodes to the specified position and picks the closest one. + /// The NNConstraint can be used to specify constraints on which nodes can be chosen such as only picking walkable nodes. + /// + /// + /// GraphNode node = AstarPath.active.GetNearest(transform.position, NNConstraint.Default).node; + /// + /// + /// + /// var constraint = NNConstraint.None; + /// + /// // Constrain the search to walkable nodes only + /// constraint.constrainWalkability = true; + /// constraint.walkable = true; + /// + /// // Constrain the search to only nodes with tag 3 or tag 5 + /// // The 'tags' field is a bitmask + /// constraint.constrainTags = true; + /// constraint.tags = (1 << 3) | (1 << 5); + /// + /// var info = AstarPath.active.GetNearest(transform.position, constraint); + /// var node = info.node; + /// var closestPoint = info.position; + /// + /// + /// See: Pathfinding.NNConstraint + /// + public NNInfo GetNearest (Vector3 position, NNConstraint constraint) { + return GetNearest(position, constraint, null); + } + + /// + /// Returns the nearest node to a position using the specified NNConstraint. + /// Searches through all graphs for their nearest nodes to the specified position and picks the closest one. + /// The NNConstraint can be used to specify constraints on which nodes can be chosen such as only picking walkable nodes. + /// See: Pathfinding.NNConstraint + /// + public NNInfo GetNearest (Vector3 position, NNConstraint constraint, GraphNode hint) { + // Cache property lookup + var graphs = this.graphs; + + float minDist = float.PositiveInfinity; + NNInfoInternal nearestNode = new NNInfoInternal(); + int nearestGraph = -1; + + if (graphs != null) { + for (int i = 0; i < graphs.Length; i++) { + NavGraph graph = graphs[i]; + + // Check if this graph should be searched + if (graph == null || !constraint.SuitableGraph(i, graph)) { + continue; + } + + NNInfoInternal nnInfo; + if (fullGetNearestSearch) { + // Slower nearest node search + // this will try to find a node which is suitable according to the constraint + nnInfo = graph.GetNearestForce(position, constraint); + } else { + // Fast nearest node search + // just find a node close to the position without using the constraint that much + // (unless that comes essentially 'for free') + nnInfo = graph.GetNearest(position, constraint); + } + + GraphNode node = nnInfo.node; + + // No node found in this graph + if (node == null) { + continue; + } + + // Distance to the closest point on the node from the requested position + float dist = ((Vector3)nnInfo.clampedPosition-position).magnitude; + + if (prioritizeGraphs && dist < prioritizeGraphsLimit) { + // The node is close enough, choose this graph and discard all others + minDist = dist; + nearestNode = nnInfo; + nearestGraph = i; + break; + } else { + // Choose the best node found so far + if (dist < minDist) { + minDist = dist; + nearestNode = nnInfo; + nearestGraph = i; + } + } + } + } + + // No matches found + if (nearestGraph == -1) { + return new NNInfo(); + } + + // Check if a constrained node has already been set + if (nearestNode.constrainedNode != null) { + nearestNode.node = nearestNode.constrainedNode; + nearestNode.clampedPosition = nearestNode.constClampedPosition; + } + + if (!fullGetNearestSearch && nearestNode.node != null && !constraint.Suitable(nearestNode.node)) { + // Otherwise, perform a check to force the graphs to check for a suitable node + NNInfoInternal nnInfo = graphs[nearestGraph].GetNearestForce(position, constraint); + + if (nnInfo.node != null) { + nearestNode = nnInfo; + } + } + + if (!constraint.Suitable(nearestNode.node) || (constraint.constrainDistance && (nearestNode.clampedPosition - position).sqrMagnitude > maxNearestNodeDistanceSqr)) { + return new NNInfo(); + } + + // Convert to NNInfo which doesn't have all the internal fields + return new NNInfo(nearestNode); + } + + /// + /// Returns the node closest to the ray (slow). + /// Warning: This function is brute-force and very slow, use with caution + /// + public GraphNode GetNearest (Ray ray) { + if (graphs == null) return null; + + float minDist = Mathf.Infinity; + GraphNode nearestNode = null; + + Vector3 lineDirection = ray.direction; + Vector3 lineOrigin = ray.origin; + + for (int i = 0; i < graphs.Length; i++) { + NavGraph graph = graphs[i]; + + graph.GetNodes(node => { + Vector3 pos = (Vector3)node.position; + Vector3 p = lineOrigin+(Vector3.Dot(pos-lineOrigin, lineDirection)*lineDirection); + + float tmp = Mathf.Abs(p.x-pos.x); + tmp *= tmp; + if (tmp > minDist) return; + + tmp = Mathf.Abs(p.z-pos.z); + tmp *= tmp; + if (tmp > minDist) return; + + float dist = (p-pos).sqrMagnitude; + + if (dist < minDist) { + minDist = dist; + nearestNode = node; + } + return; + }); + } + + return nearestNode; + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/AstarPath.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarPath.cs.meta new file mode 100644 index 0000000..0d9757a --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/AstarPath.cs.meta @@ -0,0 +1,17 @@ +fileFormatVersion: 2 +guid: 78396926cbbfc4ac3b48fc5fc34a87d1 +labels: +- Pathfinder +timeCreated: 1473972492 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: + - gizmoSurfaceMaterial: {fileID: 2100000, guid: 5ce51318bbfb1466188b929a68a6bd3a, + type: 2} + - gizmoLineMaterial: {fileID: 2100000, guid: 91035448860ba4e708919485c73f7edc, type: 2} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateScene.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateScene.cs new file mode 100644 index 0000000..eed7f50 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateScene.cs @@ -0,0 +1,398 @@ +using UnityEngine; + +namespace Pathfinding { + [AddComponentMenu("Pathfinding/GraphUpdateScene")] + /// + /// Helper class for easily updating graphs. + /// + /// The GraphUpdateScene component is really easy to use. Create a new empty GameObject and add the component to it, it can be found in Components-->Pathfinding-->GraphUpdateScene.\n + /// When you have added the component, you should see something like the image below. + /// [Open online documentation to see images] + /// The region which the component will affect is defined by creating a polygon in the scene. + /// If you make sure you have the Position tool enabled (top-left corner of the Unity window) you can shift+click in the scene view to add more points to the polygon. + /// You can remove points using shift+alt+click. + /// By clicking on the points you can bring up a positioning tool. You can also open the "points" array in the inspector to set each point's coordinates manually. + /// [Open online documentation to see images] + /// In the inspector there are a number of variables. The first one is named "Convex", it sets if the convex hull of the points should be calculated or if the polygon should be used as-is. + /// Using the convex hull is faster when applying the changes to the graph, but with a non-convex polygon you can specify more complicated areas.\n + /// The next two variables, called "Apply On Start" and "Apply On Scan" determine when to apply the changes. If the object is in the scene from the beginning, both can be left on, it doesn't + /// matter since the graph is also scanned at start. However if you instantiate it later in the game, you can make it apply it's setting directly, or wait until the next scan (if any). + /// If the graph is rescanned, all GraphUpdateScene components which have the Apply On Scan variable toggled will apply their settings again to the graph since rescanning clears all previous changes.\n + /// You can also make it apply it's changes using scripting. + /// GetComponent().Apply (); + /// The above code will make it apply its changes to the graph (assuming a GraphUpdateScene component is attached to the same GameObject). + /// + /// Next there is "Modify Walkability" and "Set Walkability" (which appears when "Modify Walkability" is toggled). + /// If Modify Walkability is set, then all nodes inside the area will either be set to walkable or unwalkable depending on the value of the "Set Walkability" variable. + /// + /// Penalty can also be applied to the nodes. A higher penalty (aka weight) makes the nodes harder to traverse so it will try to avoid those areas. + /// + /// The tagging variables can be read more about on this page: tags (view in online documentation for working links) "Working with tags". + /// + /// Note: The Y (up) axis of the transform that this component is attached to should be in the same direction as the up direction of the graph. + /// So if you for example have a grid in the XY plane then the transform should have the rotation (-90,0,0). + /// + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_graph_update_scene.php")] + public class GraphUpdateScene : GraphModifier { + /// Points which define the region to update + public Vector3[] points; + + /// Private cached convex hull of the + private Vector3[] convexPoints; + + /// + /// Use the convex hull of the points instead of the original polygon. + /// + /// See: https://en.wikipedia.org/wiki/Convex_hull + /// + public bool convex = true; + + /// + /// Minumum height of the bounds of the resulting Graph Update Object. + /// Useful when all points are laid out on a plane but you still need a bounds with a height greater than zero since a + /// zero height graph update object would usually result in no nodes being updated. + /// + public float minBoundsHeight = 1; + + /// + /// Penalty to add to nodes. + /// Usually you need quite large values, at least 1000-10000. A higher penalty means that agents will try to avoid those nodes more. + /// + /// Be careful when setting negative values since if a node gets a negative penalty it will underflow and instead get + /// really large. In most cases a warning will be logged if that happens. + /// + /// See: tags (view in online documentation for working links) for another way of applying penalties. + /// + public int penaltyDelta; + + /// If true, then all affected nodes will be made walkable or unwalkable according to + public bool modifyWalkability; + + /// Nodes will be made walkable or unwalkable according to this value if is true + public bool setWalkability; + + /// Apply this graph update object on start + public bool applyOnStart = true; + + /// Apply this graph update object whenever a graph is rescanned + public bool applyOnScan = true; + + /// + /// Update node's walkability and connectivity using physics functions. + /// For grid graphs, this will update the node's position and walkability exactly like when doing a scan of the graph. + /// If enabled for grid graphs, will be ignored. + /// + /// For Point Graphs, this will recalculate all connections which passes through the bounds of the resulting Graph Update Object + /// using raycasts (if enabled). + /// + public bool updatePhysics; + + /// \copydoc Pathfinding::GraphUpdateObject::resetPenaltyOnPhysics + public bool resetPenaltyOnPhysics = true; + + /// \copydoc Pathfinding::GraphUpdateObject::updateErosion + public bool updateErosion = true; + + /// + /// Should the tags of the nodes be modified. + /// If enabled, set all nodes' tags to + /// + public bool modifyTag; + + /// If is enabled, set all nodes' tags to this value + public int setTag; + + /// Emulates behavior from before version 4.0 + [HideInInspector] + public bool legacyMode = false; + + /// + /// Private cached inversion of . + /// Used for InvertSettings() + /// + private int setTagInvert; + + /// + /// Has apply been called yet. + /// Used to prevent applying twice when both applyOnScan and applyOnStart are enabled + /// + private bool firstApplied; + + [SerializeField] + private int serializedVersion = 0; + + /// + /// Use world space for coordinates. + /// If true, the shape will not follow when moving around the transform. + /// + /// See: + /// + [SerializeField] + [UnityEngine.Serialization.FormerlySerializedAs("useWorldSpace")] + private bool legacyUseWorldSpace; + + /// Do some stuff at start + public void Start () { + if (!Application.isPlaying) return; + + // If firstApplied is true, that means the graph was scanned during Awake. + // So we shouldn't apply it again because then we would end up applying it two times + if (!firstApplied && applyOnStart) { + Apply(); + } + } + + public override void OnPostScan () { + if (applyOnScan) Apply(); + } + + /// + /// Inverts all invertable settings for this GUS. + /// Namely: penalty delta, walkability, tags. + /// + /// Penalty delta will be changed to negative penalty delta.\n + /// will be inverted.\n + /// will be stored in a private variable, and the new value will be 0. When calling this function again, the saved + /// value will be the new value. + /// + /// Calling this function an even number of times without changing any settings in between will be identical to no change in settings. + /// + public virtual void InvertSettings () { + setWalkability = !setWalkability; + penaltyDelta = -penaltyDelta; + if (setTagInvert == 0) { + setTagInvert = setTag; + setTag = 0; + } else { + setTag = setTagInvert; + setTagInvert = 0; + } + } + + /// + /// Recalculate convex hull. + /// Will not do anything if is disabled. + /// + public void RecalcConvex () { + convexPoints = convex ? Polygon.ConvexHullXZ(points) : null; + } + + /// + /// Switches between using world space and using local space. + /// Deprecated: World space can no longer be used as it does not work well with rotated graphs. Use transform.InverseTransformPoint to transform points to local space. + /// + [System.ObsoleteAttribute("World space can no longer be used as it does not work well with rotated graphs. Use transform.InverseTransformPoint to transform points to local space.", true)] + void ToggleUseWorldSpace () { + } + + /// + /// Lock all points to a specific Y value. + /// Deprecated: The Y coordinate is no longer important. Use the position of the object instead. + /// + [System.ObsoleteAttribute("The Y coordinate is no longer important. Use the position of the object instead", true)] + public void LockToY () { + } + + /// + /// Calculates the bounds for this component. + /// This is a relatively expensive operation, it needs to go through all points and + /// run matrix multiplications. + /// + public Bounds GetBounds () { + if (points == null || points.Length == 0) { + Bounds bounds; + var coll = GetComponent(); + var coll2D = GetComponent(); + var rend = GetComponent(); + + if (coll != null) bounds = coll.bounds; + else if (coll2D != null) { + bounds = coll2D.bounds; + bounds.size = new Vector3(bounds.size.x, bounds.size.y, Mathf.Max(bounds.size.z, 1f)); + } else if (rend != null) { + bounds = rend.bounds; + } else { + return new Bounds(Vector3.zero, Vector3.zero); + } + + if (legacyMode && bounds.size.y < minBoundsHeight) bounds.size = new Vector3(bounds.size.x, minBoundsHeight, bounds.size.z); + return bounds; + } else { + return GraphUpdateShape.GetBounds(convex ? convexPoints : points, legacyMode && legacyUseWorldSpace ? Matrix4x4.identity : transform.localToWorldMatrix, minBoundsHeight); + } + } + + /// + /// Updates graphs with a created GUO. + /// Creates a Pathfinding.GraphUpdateObject with a Pathfinding.GraphUpdateShape + /// representing the polygon of this object and update all graphs using AstarPath.UpdateGraphs. + /// This will not update graphs immediately. See AstarPath.UpdateGraph for more info. + /// + public void Apply () { + if (AstarPath.active == null) { + Debug.LogError("There is no AstarPath object in the scene", this); + return; + } + + GraphUpdateObject guo; + + if (points == null || points.Length == 0) { + var polygonCollider = GetComponent(); + if (polygonCollider != null) { + var points2D = polygonCollider.points; + Vector3[] pts = new Vector3[points2D.Length]; + for (int i = 0; i < pts.Length; i++) { + var p = points2D[i] + polygonCollider.offset; + pts[i] = new Vector3(p.x, 0, p.y); + } + + var mat = transform.localToWorldMatrix * Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(-90, 0, 0), Vector3.one); + var shape = new GraphUpdateShape(points, convex, mat, minBoundsHeight); + guo = new GraphUpdateObject(GetBounds()); + guo.shape = shape; + } else { + var bounds = GetBounds(); + if (bounds.center == Vector3.zero && bounds.size == Vector3.zero) { + Debug.LogError("Cannot apply GraphUpdateScene, no points defined and no renderer or collider attached", this); + return; + } + + guo = new GraphUpdateObject(bounds); + } + } else { + GraphUpdateShape shape; + if (legacyMode && !legacyUseWorldSpace) { + // Used for compatibility with older versions + var worldPoints = new Vector3[points.Length]; + for (int i = 0; i < points.Length; i++) worldPoints[i] = transform.TransformPoint(points[i]); + shape = new GraphUpdateShape(worldPoints, convex, Matrix4x4.identity, minBoundsHeight); + } else { + shape = new GraphUpdateShape(points, convex, legacyMode && legacyUseWorldSpace ? Matrix4x4.identity : transform.localToWorldMatrix, minBoundsHeight); + } + var bounds = shape.GetBounds(); + guo = new GraphUpdateObject(bounds); + guo.shape = shape; + } + + firstApplied = true; + + guo.modifyWalkability = modifyWalkability; + guo.setWalkability = setWalkability; + guo.addPenalty = penaltyDelta; + guo.updatePhysics = updatePhysics; + guo.updateErosion = updateErosion; + guo.resetPenaltyOnPhysics = resetPenaltyOnPhysics; + + guo.modifyTag = modifyTag; + guo.setTag = setTag; + + AstarPath.active.UpdateGraphs(guo); + } + + /// Draws some gizmos + void OnDrawGizmos () { + OnDrawGizmos(false); + } + + /// Draws some gizmos + void OnDrawGizmosSelected () { + OnDrawGizmos(true); + } + + /// Draws some gizmos + void OnDrawGizmos (bool selected) { + Color c = selected ? new Color(227/255f, 61/255f, 22/255f, 1.0f) : new Color(227/255f, 61/255f, 22/255f, 0.9f); + + if (selected) { + Gizmos.color = Color.Lerp(c, new Color(1, 1, 1, 0.2f), 0.9f); + + Bounds b = GetBounds(); + Gizmos.DrawCube(b.center, b.size); + Gizmos.DrawWireCube(b.center, b.size); + } + + if (points == null) return; + + if (convex) c.a *= 0.5f; + + Gizmos.color = c; + + Matrix4x4 matrix = legacyMode && legacyUseWorldSpace ? Matrix4x4.identity : transform.localToWorldMatrix; + + if (convex) { + c.r -= 0.1f; + c.g -= 0.2f; + c.b -= 0.1f; + + Gizmos.color = c; + } + + if (selected || !convex) { + for (int i = 0; i < points.Length; i++) { + Gizmos.DrawLine(matrix.MultiplyPoint3x4(points[i]), matrix.MultiplyPoint3x4(points[(i+1)%points.Length])); + } + } + + if (convex) { + if (convexPoints == null) RecalcConvex(); + + Gizmos.color = selected ? new Color(227/255f, 61/255f, 22/255f, 1.0f) : new Color(227/255f, 61/255f, 22/255f, 0.9f); + + for (int i = 0; i < convexPoints.Length; i++) { + Gizmos.DrawLine(matrix.MultiplyPoint3x4(convexPoints[i]), matrix.MultiplyPoint3x4(convexPoints[(i+1)%convexPoints.Length])); + } + } + + // Draw the full 3D shape + var pts = convex ? convexPoints : points; + if (selected && pts != null && pts.Length > 0) { + Gizmos.color = new Color(1, 1, 1, 0.2f); + float miny = pts[0].y, maxy = pts[0].y; + for (int i = 0; i < pts.Length; i++) { + miny = Mathf.Min(miny, pts[i].y); + maxy = Mathf.Max(maxy, pts[i].y); + } + var extraHeight = Mathf.Max(minBoundsHeight - (maxy - miny), 0) * 0.5f; + miny -= extraHeight; + maxy += extraHeight; + + for (int i = 0; i < pts.Length; i++) { + var next = (i+1) % pts.Length; + var p1 = matrix.MultiplyPoint3x4(pts[i] + Vector3.up*(miny - pts[i].y)); + var p2 = matrix.MultiplyPoint3x4(pts[i] + Vector3.up*(maxy - pts[i].y)); + var p1n = matrix.MultiplyPoint3x4(pts[next] + Vector3.up*(miny - pts[next].y)); + var p2n = matrix.MultiplyPoint3x4(pts[next] + Vector3.up*(maxy - pts[next].y)); + Gizmos.DrawLine(p1, p2); + Gizmos.DrawLine(p1, p1n); + Gizmos.DrawLine(p2, p2n); + } + } + } + + /// + /// Disables legacy mode if it is enabled. + /// Legacy mode is automatically enabled for components when upgrading from an earlier version than 3.8.6. + /// + public void DisableLegacyMode () { + if (legacyMode) { + legacyMode = false; + if (legacyUseWorldSpace) { + legacyUseWorldSpace = false; + for (int i = 0; i < points.Length; i++) { + points[i] = transform.InverseTransformPoint(points[i]); + } + RecalcConvex(); + } + } + } + + protected override void Awake () { + if (serializedVersion == 0) { + // Use the old behavior if some points are already set + if (points != null && points.Length > 0) legacyMode = true; + serializedVersion = 1; + } + base.Awake(); + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateScene.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateScene.cs.meta new file mode 100644 index 0000000..f56a8cf --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateScene.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: efee954c69f0d421086729bb8df1137f +timeCreated: 1490044676 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: -221 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateShape.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateShape.cs new file mode 100644 index 0000000..d094936 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateShape.cs @@ -0,0 +1,143 @@ +using UnityEngine; + +namespace Pathfinding { + /// + /// Defines a shape for a Pathfinding.GraphUpdateObject. + /// The shape consists of a number of points which it can either calculate the convex hull of or use as a polygon directly. + /// + /// A shape is essentially a 2D shape however it can be rotated arbitrarily. + /// When a matrix and a list of points is specified in the constructor the matrix decides what direction + /// is the 'up' direction. When checking if a point is contained in the shape, the point will be projected down + /// on a plane where the 'up' direction is the normal and then it will check if the shape contains the point. + /// + /// See: Pathfinding.GraphUpdateObject.shape + /// + public class GraphUpdateShape { + Vector3[] _points; + Vector3[] _convexPoints; + bool _convex; + Vector3 right = Vector3.right; + Vector3 forward = Vector3.forward; + Vector3 up = Vector3.up; + Vector3 origin; + public float minimumHeight; + + /// + /// Gets or sets the points of the polygon in the shape. + /// These points should be specified in clockwise order. + /// Will automatically calculate the convex hull if is set to true + /// + public Vector3[] points { + get { + return _points; + } + set { + _points = value; + if (convex) CalculateConvexHull(); + } + } + + /// + /// Sets if the convex hull of the points should be calculated. + /// Convex hulls are faster but non-convex hulls can be used to specify more complicated shapes. + /// + public bool convex { + get { + return _convex; + } + set { + if (_convex != value && value) { + CalculateConvexHull(); + } + _convex = value; + } + } + + public GraphUpdateShape () { + } + + /// + /// Construct a shape. + /// See: + /// + /// Contour of the shape in local space with respect to the matrix (i.e the shape should be in the XZ plane, the Y coordinate will only affect the bounds) + /// If true, the convex hull of the points will be calculated. + /// local to world space matrix for the points. The matrix determines the up direction of the shape. + /// If the points would be in the XZ plane only, the shape would not have a height and then it might not + /// include any points inside it (as testing for inclusion is done in 3D space when updating graphs). This ensures + /// that the shape has at least the minimum height (in the up direction that the matrix specifies). + public GraphUpdateShape (Vector3[] points, bool convex, Matrix4x4 matrix, float minimumHeight) { + this.convex = convex; + this.points = points; + origin = matrix.MultiplyPoint3x4(Vector3.zero); + right = matrix.MultiplyPoint3x4(Vector3.right) - origin; + up = matrix.MultiplyPoint3x4(Vector3.up) - origin; + forward = matrix.MultiplyPoint3x4(Vector3.forward) - origin; + this.minimumHeight = minimumHeight; + } + + void CalculateConvexHull () { + _convexPoints = points != null? Polygon.ConvexHullXZ (points) : null; + } + + /// World space bounding box of this shape + public Bounds GetBounds () { + return GetBounds(convex ? _convexPoints : points, right, up, forward, origin, minimumHeight); + } + + public static Bounds GetBounds (Vector3[] points, Matrix4x4 matrix, float minimumHeight) { + var origin = matrix.MultiplyPoint3x4(Vector3.zero); + var right = matrix.MultiplyPoint3x4(Vector3.right) - origin; + var up = matrix.MultiplyPoint3x4(Vector3.up) - origin; + var forward = matrix.MultiplyPoint3x4(Vector3.forward) - origin; + + return GetBounds(points, right, up, forward, origin, minimumHeight); + } + + static Bounds GetBounds (Vector3[] points, Vector3 right, Vector3 up, Vector3 forward, Vector3 origin, float minimumHeight) { + if (points == null || points.Length == 0) return new Bounds(); + float miny = points[0].y, maxy = points[0].y; + for (int i = 0; i < points.Length; i++) { + miny = Mathf.Min(miny, points[i].y); + maxy = Mathf.Max(maxy, points[i].y); + } + var extraHeight = Mathf.Max(minimumHeight - (maxy - miny), 0) * 0.5f; + miny -= extraHeight; + maxy += extraHeight; + + Vector3 min = right * points[0].x + up * points[0].y + forward * points[0].z; + Vector3 max = min; + for (int i = 0; i < points.Length; i++) { + var p = right * points[i].x + forward * points[i].z; + var p1 = p + up * miny; + var p2 = p + up * maxy; + min = Vector3.Min(min, p1); + min = Vector3.Min(min, p2); + max = Vector3.Max(max, p1); + max = Vector3.Max(max, p2); + } + return new Bounds((min+max)*0.5F + origin, max-min); + } + + public bool Contains (GraphNode node) { + return Contains((Vector3)node.position); + } + + public bool Contains (Vector3 point) { + // Transform to local space (shape in the XZ plane) + point -= origin; + var localSpacePoint = new Vector3(Vector3.Dot(point, right)/right.sqrMagnitude, 0, Vector3.Dot(point, forward)/forward.sqrMagnitude); + + if (convex) { + if (_convexPoints == null) return false; + + for (int i = 0, j = _convexPoints.Length-1; i < _convexPoints.Length; j = i, i++) { + if (VectorMath.RightOrColinearXZ(_convexPoints[i], _convexPoints[j], localSpacePoint)) return false; + } + return true; + } else { + return _points != null && Polygon.ContainsPointXZ(_points, localSpacePoint); + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateShape.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateShape.cs.meta new file mode 100644 index 0000000..7df21dd --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/GraphUpdateShape.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1c31d3b0be14344e98aa458dc66c3a94 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc.meta new file mode 100644 index 0000000..77e0364 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: dfc976d61106d46b6a18ace94ffaea8d diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AnimationLink.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AnimationLink.cs new file mode 100644 index 0000000..3878cce --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AnimationLink.cs @@ -0,0 +1,122 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Pathfinding { + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_animation_link.php")] + public class AnimationLink : NodeLink2 { + public string clip; + public float animSpeed = 1; + public bool reverseAnim = true; + + public GameObject referenceMesh; + public LinkClip[] sequence; + public string boneRoot = "bn_COG_Root"; + + [System.Serializable] + public class LinkClip { + public AnimationClip clip; + public Vector3 velocity; + public int loopCount = 1; + + public string name { + get { + return clip != null ? clip.name : ""; + } + } + } + + static Transform SearchRec (Transform tr, string name) { + int childCount = tr.childCount; + + for (int i = 0; i < childCount; i++) { + Transform ch = tr.GetChild(i); + if (ch.name == name) return ch; + else { + Transform rec = SearchRec(ch, name); + if (rec != null) return rec; + } + } + return null; + } + + public void CalculateOffsets (List trace, out Vector3 endPosition) { + //Vector3 opos = transform.position; + endPosition = transform.position; + if (referenceMesh == null) return; + + GameObject ob = GameObject.Instantiate(referenceMesh, transform.position, transform.rotation) as GameObject; + ob.hideFlags = HideFlags.HideAndDontSave; + + Transform root = SearchRec(ob.transform, boneRoot); + if (root == null) throw new System.Exception("Could not find root transform"); + + Animation anim = ob.GetComponent(); + if (anim == null) anim = ob.AddComponent(); + + for (int i = 0; i < sequence.Length; i++) { + anim.AddClip(sequence[i].clip, sequence[i].clip.name); + } + + Vector3 prevOffset = Vector3.zero; + Vector3 position = transform.position; + Vector3 firstOffset = Vector3.zero; + + for (int i = 0; i < sequence.Length; i++) { + LinkClip c = sequence[i]; + if (c == null) { + endPosition = position; + return; + } + + anim[c.clip.name].enabled = true; + anim[c.clip.name].weight = 1; + + for (int repeat = 0; repeat < c.loopCount; repeat++) { + anim[c.clip.name].normalizedTime = 0; + anim.Sample(); + Vector3 soffset = root.position - transform.position; + + if (i > 0) { + position += prevOffset - soffset; + } else { + firstOffset = soffset; + } + + for (int t = 0; t <= 20; t++) { + float tf = t/20.0f; + anim[c.clip.name].normalizedTime = tf; + anim.Sample(); + Vector3 tmp = position + (root.position-transform.position) + c.velocity*tf*c.clip.length; + trace.Add(tmp); + } + position = position + c.velocity*1*c.clip.length; + + anim[c.clip.name].normalizedTime = 1; + anim.Sample(); + Vector3 eoffset = root.position - transform.position; + prevOffset = eoffset; + } + + anim[c.clip.name].enabled = false; + anim[c.clip.name].weight = 0; + } + + position += prevOffset - firstOffset; + + GameObject.DestroyImmediate(ob); + + endPosition = position; + } + + public override void OnDrawGizmosSelected () { + base.OnDrawGizmosSelected(); + List buffer = Pathfinding.Util.ListPool.Claim (); + Vector3 endPosition = Vector3.zero; + CalculateOffsets(buffer, out endPosition); + Gizmos.color = Color.blue; + for (int i = 0; i < buffer.Count-1; i++) { + Gizmos.DrawLine(buffer[i], buffer[i+1]); + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AnimationLink.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AnimationLink.cs.meta new file mode 100644 index 0000000..6c2aca2 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AnimationLink.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d2e8b1fd6fa484fc29f8a26fb5e8662b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ArrayPool.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ArrayPool.cs new file mode 100644 index 0000000..b7fcdc7 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ArrayPool.cs @@ -0,0 +1,198 @@ +#if !UNITY_EDITOR +// Extra optimizations when not running in the editor, but less error checking +#define ASTAR_OPTIMIZE_POOLING +#endif + +using System; +using System.Collections.Generic; + +namespace Pathfinding.Util { + /// + /// Lightweight Array Pool. + /// Handy class for pooling arrays of type T. + /// + /// Usage: + /// - Claim a new array using SomeClass[] foo = ArrayPool.Claim (capacity); + /// - Use it and do stuff with it + /// - Release it with ArrayPool.Release (foo); + /// + /// Warning: Arrays returned from the Claim method may contain arbitrary data. + /// You cannot rely on it being zeroed out. + /// + /// After you have released a array, you should never use it again, if you do use it + /// your code may modify it at the same time as some other code is using it which + /// will likely lead to bad results. + /// + /// \since Version 3.8.6 + /// See: Pathfinding.Util.ListPool + /// + public static class ArrayPool { +#if !ASTAR_NO_POOLING + /// + /// Maximum length of an array pooled using ClaimWithExactLength. + /// Arrays with lengths longer than this will silently not be pooled. + /// + const int MaximumExactArrayLength = 256; + + /// + /// Internal pool. + /// The arrays in each bucket have lengths of 2^i + /// + static readonly Stack[] pool = new Stack[31]; + static readonly Stack[] exactPool = new Stack[MaximumExactArrayLength+1]; +#if !ASTAR_OPTIMIZE_POOLING + static readonly HashSet inPool = new HashSet(); +#endif +#endif + + /// + /// Returns an array with at least the specified length. + /// Warning: Returned arrays may contain arbitrary data. + /// You cannot rely on it being zeroed out. + /// + public static T[] Claim (int minimumLength) { + if (minimumLength <= 0) { + return ClaimWithExactLength(0); + } + + int bucketIndex = 0; + while ((1 << bucketIndex) < minimumLength && bucketIndex < 30) { + bucketIndex++; + } + + if (bucketIndex == 30) + throw new System.ArgumentException("Too high minimum length"); + +#if !ASTAR_NO_POOLING + lock (pool) { + if (pool[bucketIndex] == null) { + pool[bucketIndex] = new Stack(); + } + + if (pool[bucketIndex].Count > 0) { + var array = pool[bucketIndex].Pop(); +#if !ASTAR_OPTIMIZE_POOLING + inPool.Remove(array); +#endif + return array; + } + } +#endif + return new T[1 << bucketIndex]; + } + + /// + /// Returns an array with the specified length. + /// Use with caution as pooling too many arrays with different lengths that + /// are rarely being reused will lead to an effective memory leak. + /// + /// Use if you just need an array that is at least as large as some value. + /// + /// Warning: Returned arrays may contain arbitrary data. + /// You cannot rely on it being zeroed out. + /// + public static T[] ClaimWithExactLength (int length) { +#if !ASTAR_NO_POOLING + bool isPowerOfTwo = length != 0 && (length & (length - 1)) == 0; + if (isPowerOfTwo) { + // Will return the correct array length + return Claim(length); + } + + if (length <= MaximumExactArrayLength) { + lock (pool) { + Stack stack = exactPool[length]; + if (stack != null && stack.Count > 0) { + var array = stack.Pop(); +#if !ASTAR_OPTIMIZE_POOLING + inPool.Remove(array); +#endif + return array; + } + } + } +#endif + return new T[length]; + } + + /// + /// Pool an array. + /// If the array was got using the method then the allowNonPowerOfTwo parameter must be set to true. + /// The parameter exists to make sure that non power of two arrays are not pooled unintentionally which could lead to memory leaks. + /// + public static void Release (ref T[] array, bool allowNonPowerOfTwo = false) { + if (array == null) return; + if (array.GetType() != typeof(T[])) { + throw new System.ArgumentException("Expected array type " + typeof(T[]).Name + " but found " + array.GetType().Name + "\nAre you using the correct generic class?\n"); + } + +#if !ASTAR_NO_POOLING + bool isPowerOfTwo = array.Length != 0 && (array.Length & (array.Length - 1)) == 0; + if (!isPowerOfTwo && !allowNonPowerOfTwo && array.Length != 0) throw new System.ArgumentException("Length is not a power of 2"); + + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING + if (!inPool.Add(array)) { + throw new InvalidOperationException("You are trying to pool an array twice. Please make sure that you only pool it once."); + } +#endif + if (isPowerOfTwo) { + int bucketIndex = 0; + while ((1 << bucketIndex) < array.Length && bucketIndex < 30) { + bucketIndex++; + } + + if (pool[bucketIndex] == null) { + pool[bucketIndex] = new Stack(); + } + + pool[bucketIndex].Push(array); + } else if (array.Length <= MaximumExactArrayLength) { + Stack stack = exactPool[array.Length]; + if (stack == null) stack = exactPool[array.Length] = new Stack(); + stack.Push(array); + } + } +#endif + array = null; + } + } + + /// Extension methods for List + public static class ListExtensions { + /// + /// Identical to ToArray but it uses ArrayPool to avoid allocations if possible. + /// + /// Use with caution as pooling too many arrays with different lengths that + /// are rarely being reused will lead to an effective memory leak. + /// + public static T[] ToArrayFromPool(this List list) { + var arr = ArrayPool.ClaimWithExactLength (list.Count); + + for (int i = 0; i < arr.Length; i++) { + arr[i] = list[i]; + } + return arr; + } + + /// + /// Clear a list faster than List.Clear. + /// It turns out that the List.Clear method will clear all elements in the underlaying array + /// not just the ones up to Count. If the list only has a few elements, but the capacity + /// is huge, this can cause performance problems. Using the RemoveRange method to remove + /// all elements in the list does not have this problem, however it is implemented in a + /// stupid way, so it will clear the elements twice (completely unnecessarily) so it will + /// only be faster than using the Clear method if the number of elements in the list is + /// less than half of the capacity of the list. + /// + /// Hopefully this method can be removed when Unity upgrades to a newer version of Mono. + /// + public static void ClearFast(this List list) { + if (list.Count*2 < list.Capacity) { + list.RemoveRange(0, list.Count); + } else { + list.Clear(); + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ArrayPool.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ArrayPool.cs.meta new file mode 100644 index 0000000..871cd64 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ArrayPool.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 787a564bf2d894ee09284b775074864c +timeCreated: 1470483941 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AstarDebugger.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AstarDebugger.cs new file mode 100644 index 0000000..5a61fbd --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AstarDebugger.cs @@ -0,0 +1,337 @@ +//#define ProfileAstar + +using UnityEngine; +using System.Text; + +namespace Pathfinding { + [AddComponentMenu("Pathfinding/Pathfinding Debugger")] + [ExecuteInEditMode] + /// + /// Debugger for the A* Pathfinding Project. + /// This class can be used to profile different parts of the pathfinding system + /// and the whole game as well to some extent. + /// + /// Clarification of the labels shown when enabled. + /// All memory related things profiles the whole game not just the A* Pathfinding System.\n + /// - Currently allocated: memory the GC (garbage collector) says the application has allocated right now. + /// - Peak allocated: maximum measured value of the above. + /// - Last collect peak: the last peak of 'currently allocated'. + /// - Allocation rate: how much the 'currently allocated' value increases per second. This value is not as reliable as you can think + /// it is often very random probably depending on how the GC thinks this application is using memory. + /// - Collection frequency: how often the GC is called. Again, the GC might decide it is better with many small collections + /// or with a few large collections. So you cannot really trust this variable much. + /// - Last collect fps: FPS during the last garbage collection, the GC will lower the fps a lot. + /// + /// - FPS: current FPS (not updated every frame for readability) + /// - Lowest FPS (last x): As the label says, the lowest fps of the last x frames. + /// + /// - Size: Size of the path pool. + /// - Total created: Number of paths of that type which has been created. Pooled paths are not counted twice. + /// If this value just keeps on growing and growing without an apparent stop, you are are either not pooling any paths + /// or you have missed to pool some path somewhere in your code. + /// + /// See: pooling + /// + /// TODO: Add field showing how many graph updates are being done right now + /// + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_astar_debugger.php")] + public class AstarDebugger : VersionedMonoBehaviour { + public int yOffset = 5; + + public bool show = true; + public bool showInEditor = false; + + public bool showFPS = false; + public bool showPathProfile = false; + public bool showMemProfile = false; + public bool showGraph = false; + + public int graphBufferSize = 200; + + /// + /// Font to use. + /// A monospaced font is the best + /// + public Font font = null; + public int fontSize = 12; + + StringBuilder text = new StringBuilder(); + string cachedText; + float lastUpdate = -999; + + private GraphPoint[] graph; + + struct GraphPoint { + public float fps, memory; + public bool collectEvent; + } + + private float delayedDeltaTime = 1; + private float lastCollect = 0; + private float lastCollectNum = 0; + private float delta = 0; + private float lastDeltaTime = 0; + private int allocRate = 0; + private int lastAllocMemory = 0; + private float lastAllocSet = -9999; + private int allocMem = 0; + private int collectAlloc = 0; + private int peakAlloc = 0; + + private int fpsDropCounterSize = 200; + private float[] fpsDrops; + + private Rect boxRect; + + private GUIStyle style; + + private Camera cam; + + float graphWidth = 100; + float graphHeight = 100; + float graphOffset = 50; + + public void Start () { + useGUILayout = false; + + fpsDrops = new float[fpsDropCounterSize]; + + cam = GetComponent(); + if (cam == null) { + cam = Camera.main; + } + + graph = new GraphPoint[graphBufferSize]; + + if (Time.unscaledDeltaTime > 0) { + for (int i = 0; i < fpsDrops.Length; i++) { + fpsDrops[i] = 1F / Time.unscaledDeltaTime; + } + } + } + + int maxVecPool = 0; + int maxNodePool = 0; + + PathTypeDebug[] debugTypes = new PathTypeDebug[] { + new PathTypeDebug("ABPath", () => PathPool.GetSize(typeof(ABPath)), () => PathPool.GetTotalCreated(typeof(ABPath))) + }; + + struct PathTypeDebug { + string name; + System.Func getSize; + System.Func getTotalCreated; + public PathTypeDebug (string name, System.Func getSize, System.Func getTotalCreated) { + this.name = name; + this.getSize = getSize; + this.getTotalCreated = getTotalCreated; + } + + public void Print (StringBuilder text) { + int totCreated = getTotalCreated(); + + if (totCreated > 0) { + text.Append("\n").Append((" " + name).PadRight(25)).Append(getSize()).Append("/").Append(totCreated); + } + } + } + + public void LateUpdate () { + if (!show || (!Application.isPlaying && !showInEditor)) return; + + if (Time.unscaledDeltaTime <= 0.0001f) + return; + + int collCount = System.GC.CollectionCount(0); + + if (lastCollectNum != collCount) { + lastCollectNum = collCount; + delta = Time.realtimeSinceStartup-lastCollect; + lastCollect = Time.realtimeSinceStartup; + lastDeltaTime = Time.unscaledDeltaTime; + collectAlloc = allocMem; + } + + allocMem = (int)System.GC.GetTotalMemory(false); + + bool collectEvent = allocMem < peakAlloc; + peakAlloc = !collectEvent ? allocMem : peakAlloc; + + if (Time.realtimeSinceStartup - lastAllocSet > 0.3F || !Application.isPlaying) { + int diff = allocMem - lastAllocMemory; + lastAllocMemory = allocMem; + lastAllocSet = Time.realtimeSinceStartup; + delayedDeltaTime = Time.unscaledDeltaTime; + + if (diff >= 0) { + allocRate = diff; + } + } + + if (Application.isPlaying) { + fpsDrops[Time.frameCount % fpsDrops.Length] = Time.unscaledDeltaTime > 0.00001f ? 1F / Time.unscaledDeltaTime : 0; + int graphIndex = Time.frameCount % graph.Length; + graph[graphIndex].fps = Time.unscaledDeltaTime < 0.00001f ? 1F / Time.unscaledDeltaTime : 0; + graph[graphIndex].collectEvent = collectEvent; + graph[graphIndex].memory = allocMem; + } + + if (Application.isPlaying && cam != null && showGraph) { + graphWidth = cam.pixelWidth*0.8f; + + + float minMem = float.PositiveInfinity, maxMem = 0, minFPS = float.PositiveInfinity, maxFPS = 0; + for (int i = 0; i < graph.Length; i++) { + minMem = Mathf.Min(graph[i].memory, minMem); + maxMem = Mathf.Max(graph[i].memory, maxMem); + minFPS = Mathf.Min(graph[i].fps, minFPS); + maxFPS = Mathf.Max(graph[i].fps, maxFPS); + } + + int currentGraphIndex = Time.frameCount % graph.Length; + + Matrix4x4 m = Matrix4x4.TRS(new Vector3((cam.pixelWidth - graphWidth)/2f, graphOffset, 1), Quaternion.identity, new Vector3(graphWidth, graphHeight, 1)); + + for (int i = 0; i < graph.Length-1; i++) { + if (i == currentGraphIndex) continue; + + DrawGraphLine(i, m, i/(float)graph.Length, (i+1)/(float)graph.Length, Mathf.InverseLerp(minMem, maxMem, graph[i].memory), Mathf.InverseLerp(minMem, maxMem, graph[i+1].memory), Color.blue); + DrawGraphLine(i, m, i/(float)graph.Length, (i+1)/(float)graph.Length, Mathf.InverseLerp(minFPS, maxFPS, graph[i].fps), Mathf.InverseLerp(minFPS, maxFPS, graph[i+1].fps), Color.green); + } + } + } + + void DrawGraphLine (int index, Matrix4x4 m, float x1, float x2, float y1, float y2, Color color) { + Debug.DrawLine(cam.ScreenToWorldPoint(m.MultiplyPoint3x4(new Vector3(x1, y1))), cam.ScreenToWorldPoint(m.MultiplyPoint3x4(new Vector3(x2, y2))), color); + } + + public void OnGUI () { + if (!show || (!Application.isPlaying && !showInEditor)) return; + + if (style == null) { + style = new GUIStyle(); + style.normal.textColor = Color.white; + style.padding = new RectOffset(5, 5, 5, 5); + } + + if (Time.realtimeSinceStartup - lastUpdate > 0.5f || cachedText == null || !Application.isPlaying) { + lastUpdate = Time.realtimeSinceStartup; + + boxRect = new Rect(5, yOffset, 310, 40); + + text.Length = 0; + text.AppendLine("A* Pathfinding Project Debugger"); + text.Append("A* Version: ").Append(AstarPath.Version.ToString()); + + if (showMemProfile) { + boxRect.height += 200; + + text.AppendLine(); + text.AppendLine(); + text.Append("Currently allocated".PadRight(25)); + text.Append((allocMem/1000000F).ToString("0.0 MB")); + text.AppendLine(); + + text.Append("Peak allocated".PadRight(25)); + text.Append((peakAlloc/1000000F).ToString("0.0 MB")).AppendLine(); + + text.Append("Last collect peak".PadRight(25)); + text.Append((collectAlloc/1000000F).ToString("0.0 MB")).AppendLine(); + + + text.Append("Allocation rate".PadRight(25)); + text.Append((allocRate/1000000F).ToString("0.0 MB")).AppendLine(); + + text.Append("Collection frequency".PadRight(25)); + text.Append(delta.ToString("0.00")); + text.Append("s\n"); + + text.Append("Last collect fps".PadRight(25)); + text.Append((1F/lastDeltaTime).ToString("0.0 fps")); + text.Append(" ("); + text.Append(lastDeltaTime.ToString("0.000 s")); + text.Append(")"); + } + + if (showFPS) { + text.AppendLine(); + text.AppendLine(); + var delayedFPS = delayedDeltaTime > 0.00001f ? 1F/delayedDeltaTime : 0; + text.Append("FPS".PadRight(25)).Append(delayedFPS.ToString("0.0 fps")); + + + float minFps = Mathf.Infinity; + + for (int i = 0; i < fpsDrops.Length; i++) if (fpsDrops[i] < minFps) minFps = fpsDrops[i]; + + text.AppendLine(); + text.Append(("Lowest fps (last " + fpsDrops.Length + ")").PadRight(25)).Append(minFps.ToString("0.0")); + } + + if (showPathProfile) { + AstarPath astar = AstarPath.active; + + text.AppendLine(); + + if (astar == null) { + text.Append("\nNo AstarPath Object In The Scene"); + } else { +#if ProfileAstar + double searchSpeed = (double)AstarPath.TotalSearchedNodes*10000 / (double)AstarPath.TotalSearchTime; + text.Append("\nSearch Speed (nodes/ms) ").Append(searchSpeed.ToString("0")).Append(" ("+AstarPath.TotalSearchedNodes+" / ").Append(((double)AstarPath.TotalSearchTime/10000F).ToString("0")+")"); +#endif + + if (Pathfinding.Util.ListPool.GetSize() > maxVecPool) maxVecPool = Pathfinding.Util.ListPool.GetSize (); + if (Pathfinding.Util.ListPool.GetSize() > maxNodePool) maxNodePool = Pathfinding.Util.ListPool.GetSize (); + + text.Append("\nPool Sizes (size/total created)"); + + for (int i = 0; i < debugTypes.Length; i++) { + debugTypes[i].Print(text); + } + } + } + + cachedText = text.ToString(); + } + + + if (font != null) { + style.font = font; + style.fontSize = fontSize; + } + + boxRect.height = style.CalcHeight(new GUIContent(cachedText), boxRect.width); + + GUI.Box(boxRect, ""); + GUI.Label(boxRect, cachedText, style); + + if (showGraph) { + float minMem = float.PositiveInfinity, maxMem = 0, minFPS = float.PositiveInfinity, maxFPS = 0; + for (int i = 0; i < graph.Length; i++) { + minMem = Mathf.Min(graph[i].memory, minMem); + maxMem = Mathf.Max(graph[i].memory, maxMem); + minFPS = Mathf.Min(graph[i].fps, minFPS); + maxFPS = Mathf.Max(graph[i].fps, maxFPS); + } + + float line; + GUI.color = Color.blue; + // Round to nearest x.x MB + line = Mathf.RoundToInt(maxMem/(100.0f*1000)); + GUI.Label(new Rect(5, Screen.height - AstarMath.MapTo(minMem, maxMem, 0 + graphOffset, graphHeight + graphOffset, line*1000*100) - 10, 100, 20), (line/10.0f).ToString("0.0 MB")); + + line = Mathf.Round(minMem/(100.0f*1000)); + GUI.Label(new Rect(5, Screen.height - AstarMath.MapTo(minMem, maxMem, 0 + graphOffset, graphHeight + graphOffset, line*1000*100) - 10, 100, 20), (line/10.0f).ToString("0.0 MB")); + + GUI.color = Color.green; + // Round to nearest x.x MB + line = Mathf.Round(maxFPS); + GUI.Label(new Rect(55, Screen.height - AstarMath.MapTo(minFPS, maxFPS, 0 + graphOffset, graphHeight + graphOffset, line) - 10, 100, 20), line.ToString("0 FPS")); + + line = Mathf.Round(minFPS); + GUI.Label(new Rect(55, Screen.height - AstarMath.MapTo(minFPS, maxFPS, 0 + graphOffset, graphHeight + graphOffset, line) - 10, 100, 20), line.ToString("0 FPS")); + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AstarDebugger.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AstarDebugger.cs.meta new file mode 100644 index 0000000..8e8a582 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/AstarDebugger.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5103795af2d504ea693528e938005441 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/BinaryHeap.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/BinaryHeap.cs new file mode 100644 index 0000000..8b2b909 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/BinaryHeap.cs @@ -0,0 +1,324 @@ +#pragma warning disable 162 +#pragma warning disable 429 +#define DECREASE_KEY + +namespace Pathfinding { + /// + /// Binary heap implementation. + /// Binary heaps are really fast for ordering nodes in a way that + /// makes it possible to get the node with the lowest F score. + /// Also known as a priority queue. + /// + /// This has actually been rewritten as a 4-ary heap + /// for performance, but it's the same principle. + /// + /// See: http://en.wikipedia.org/wiki/Binary_heap + /// See: https://en.wikipedia.org/wiki/D-ary_heap + /// + public class BinaryHeap { + /// Number of items in the tree + public int numberOfItems; + + /// The tree will grow by at least this factor every time it is expanded + public float growthFactor = 2; + + /// + /// Number of children of each node in the tree. + /// Different values have been tested and 4 has been empirically found to perform the best. + /// See: https://en.wikipedia.org/wiki/D-ary_heap + /// + const int D = 4; + + /// + /// Sort nodes by G score if there is a tie when comparing the F score. + /// Disabling this will improve pathfinding performance with around 2.5% + /// but may break ties between paths that have the same length in a less + /// desirable manner (only relevant for grid graphs). + /// + const bool SortGScores = true; + + public const ushort NotInHeap = 0xFFFF; + + /// Internal backing array for the heap + private Tuple[] heap; + + /// True if the heap does not contain any elements + public bool isEmpty { + get { + return numberOfItems <= 0; + } + } + + /// Item in the heap + private struct Tuple { + public PathNode node; + public uint F; + + public Tuple (uint f, PathNode node) { + this.F = f; + this.node = node; + } + } + + /// + /// Rounds up v so that it has remainder 1 when divided by D. + /// I.e it is of the form n*D + 1 where n is any non-negative integer. + /// + static int RoundUpToNextMultipleMod1 (int v) { + // I have a feeling there is a nicer way to do this + return v + (4 - ((v-1) % D)) % D; + } + + /// Create a new heap with the specified initial capacity + public BinaryHeap (int capacity) { + // Make sure the size has remainder 1 when divided by D + // This allows us to always guarantee that indices used in the Remove method + // will never throw out of bounds exceptions + capacity = RoundUpToNextMultipleMod1(capacity); + + heap = new Tuple[capacity]; + numberOfItems = 0; + } + + /// Removes all elements from the heap + public void Clear () { +#if DECREASE_KEY + // Clear all heap indices + // This is important to avoid bugs + for (int i = 0; i < numberOfItems; i++) { + heap[i].node.heapIndex = NotInHeap; + } +#endif + + numberOfItems = 0; + } + + internal PathNode GetNode (int i) { + return heap[i].node; + } + + internal void SetF (int i, uint f) { + heap[i].F = f; + } + + /// Expands to a larger backing array when the current one is too small + void Expand () { + // 65533 == 1 mod 4 and slightly smaller than 1<<16 = 65536 + int newSize = System.Math.Max(heap.Length+4, System.Math.Min(65533, (int)System.Math.Round(heap.Length*growthFactor))); + + // Make sure the size has remainder 1 when divided by D + // This allows us to always guarantee that indices used in the Remove method + // will never throw out of bounds exceptions + newSize = RoundUpToNextMultipleMod1(newSize); + + // Check if the heap is really large + // Also note that heaps larger than this are not supported + // since PathNode.heapIndex is a ushort and can only store + // values up to 65535 (NotInHeap = 65535 is reserved however) + if (newSize > (1<<16) - 2) { + throw new System.Exception("Binary Heap Size really large (>65534). A heap size this large is probably the cause of pathfinding running in an infinite loop. "); + } + + var newHeap = new Tuple[newSize]; + heap.CopyTo(newHeap, 0); +#if ASTARDEBUG + UnityEngine.Debug.Log("Resizing binary heap to "+newSize); +#endif + heap = newHeap; + } + + /// Adds a node to the heap + public void Add (PathNode node) { + if (node == null) throw new System.ArgumentNullException("node"); + +#if DECREASE_KEY + // Check if node is already in the heap + if (node.heapIndex != NotInHeap) { + DecreaseKey(heap[node.heapIndex], node.heapIndex); + return; + } +#endif + + if (numberOfItems == heap.Length) { + Expand(); + } + + DecreaseKey(new Tuple(0, node), (ushort)numberOfItems); + numberOfItems++; + } + + void DecreaseKey (Tuple node, ushort index) { + // This is where 'obj' is in the binary heap logically speaking + // (for performance reasons we don't actually store it there until + // we know the final index, that's just a waste of CPU cycles) + int bubbleIndex = index; + // Update F value, it might have changed since the node was originally added to the heap + uint nodeF = node.F = node.node.F; + uint nodeG = node.node.G; + + while (bubbleIndex != 0) { + // Parent node of the bubble node + int parentIndex = (bubbleIndex-1) / D; + + if (nodeF < heap[parentIndex].F || (SortGScores && nodeF == heap[parentIndex].F && nodeG > heap[parentIndex].node.G)) { + // Swap the bubble node and parent node + // (we don't really need to store the bubble node until we know the final index though + // so we do that after the loop instead) + heap[bubbleIndex] = heap[parentIndex]; +#if DECREASE_KEY + heap[bubbleIndex].node.heapIndex = (ushort)bubbleIndex; +#endif + bubbleIndex = parentIndex; + } else { + break; + } + } + + heap[bubbleIndex] = node; +#if DECREASE_KEY + node.node.heapIndex = (ushort)bubbleIndex; +#endif + } + + /// Returns the node with the lowest F score from the heap + public PathNode Remove () { + PathNode returnItem = heap[0].node; + +#if DECREASE_KEY + returnItem.heapIndex = NotInHeap; +#endif + + numberOfItems--; + if (numberOfItems == 0) return returnItem; + + // Last item in the heap array + var swapItem = heap[numberOfItems]; + var swapItemG = swapItem.node.G; + + int swapIndex = 0, parent; + + // Trickle upwards + while (true) { + parent = swapIndex; + uint swapF = swapItem.F; + int pd = parent * D + 1; + + // If this holds, then the indices used + // below are guaranteed to not throw an index out of bounds + // exception since we choose the size of the array in that way + if (pd <= numberOfItems) { + // Loading all F scores here instead of inside the if statements + // reduces data dependencies and improves performance + uint f0 = heap[pd+0].F; + uint f1 = heap[pd+1].F; + uint f2 = heap[pd+2].F; + uint f3 = heap[pd+3].F; + + // The common case is that all children of a node are present + // so the first comparison in each if statement below + // will be extremely well predicted so it is essentially free + // (I tried optimizing for the common case, but it didn't affect performance at all + // at the expense of longer code, the CPU branch predictor is really good) + + if (pd+0 < numberOfItems && (f0 < swapF || (SortGScores && f0 == swapF && heap[pd+0].node.G < swapItemG))) { + swapF = f0; + swapIndex = pd+0; + } + + if (pd+1 < numberOfItems && (f1 < swapF || (SortGScores && f1 == swapF && heap[pd+1].node.G < (swapIndex == parent ? swapItemG : heap[swapIndex].node.G)))) { + swapF = f1; + swapIndex = pd+1; + } + + if (pd+2 < numberOfItems && (f2 < swapF || (SortGScores && f2 == swapF && heap[pd+2].node.G < (swapIndex == parent ? swapItemG : heap[swapIndex].node.G)))) { + swapF = f2; + swapIndex = pd+2; + } + + if (pd+3 < numberOfItems && (f3 < swapF || (SortGScores && f3 == swapF && heap[pd+3].node.G < (swapIndex == parent ? swapItemG : heap[swapIndex].node.G)))) { + swapIndex = pd+3; + } + } + + // One if the parent's children are smaller or equal, swap them + // (actually we are just pretenting we swapped them, we hold the swapData + // in local variable and only assign it once we know the final index) + if (parent != swapIndex) { + heap[parent] = heap[swapIndex]; +#if DECREASE_KEY + heap[parent].node.heapIndex = (ushort)parent; +#endif + } else { + break; + } + } + + // Assign element to the final position + heap[swapIndex] = swapItem; +#if DECREASE_KEY + swapItem.node.heapIndex = (ushort)swapIndex; +#endif + + // For debugging + // Validate (); + + return returnItem; + } + + void Validate () { + for (int i = 1; i < numberOfItems; i++) { + int parentIndex = (i-1)/D; + if (heap[parentIndex].F > heap[i].F) { + throw new System.Exception("Invalid state at " + i + ":" + parentIndex + " ( " + heap[parentIndex].F + " > " + heap[i].F + " ) "); + } +#if DECREASE_KEY + if (heap[i].node.heapIndex != i) { + throw new System.Exception("Invalid heap index"); + } +#endif + } + } + + /// + /// Rebuilds the heap by trickeling down all items. + /// Usually called after the hTarget on a path has been changed + /// + public void Rebuild () { +#if ASTARDEBUG + int changes = 0; +#endif + + for (int i = 2; i < numberOfItems; i++) { + int bubbleIndex = i; + var node = heap[i]; + uint nodeF = node.F; + while (bubbleIndex != 1) { + int parentIndex = bubbleIndex / D; + + if (nodeF < heap[parentIndex].F) { + heap[bubbleIndex] = heap[parentIndex]; +#if DECREASE_KEY + heap[bubbleIndex].node.heapIndex = (ushort)bubbleIndex; +#endif + + heap[parentIndex] = node; +#if DECREASE_KEY + heap[parentIndex].node.heapIndex = (ushort)parentIndex; +#endif + + bubbleIndex = parentIndex; +#if ASTARDEBUG + changes++; +#endif + } else { + break; + } + } + } + +#if ASTARDEBUG + UnityEngine.Debug.Log("+++ Rebuilt Heap - "+changes+" changes +++"); +#endif + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/BinaryHeap.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/BinaryHeap.cs.meta new file mode 100644 index 0000000..0ba2009 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/BinaryHeap.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: eb4299e8747f44ad2b4e086752108ea3 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Draw.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Draw.cs new file mode 100644 index 0000000..9a13b6d --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Draw.cs @@ -0,0 +1,91 @@ +using UnityEngine; + +namespace Pathfinding.Util { + /// Helper methods for drawing gizmos and debug lines + public class Draw { + public static readonly Draw Debug = new Draw { gizmos = false }; + public static readonly Draw Gizmos = new Draw { gizmos = true }; + + bool gizmos; + Matrix4x4 matrix = Matrix4x4.identity; + + void SetColor (Color color) { + if (gizmos && UnityEngine.Gizmos.color != color) UnityEngine.Gizmos.color = color; + } + + public void Polyline (System.Collections.Generic.List points, Color color, bool cycle = false) { + for (int i = 0; i < points.Count - 1; i++) { + Line(points[i], points[i+1], color); + } + if (cycle && points.Count > 1) Line(points[points.Count - 1], points[0], color); + } + + public void Line (Vector3 a, Vector3 b, Color color) { + SetColor(color); + if (gizmos) UnityEngine.Gizmos.DrawLine(matrix.MultiplyPoint3x4(a), matrix.MultiplyPoint3x4(b)); + else UnityEngine.Debug.DrawLine(matrix.MultiplyPoint3x4(a), matrix.MultiplyPoint3x4(b), color); + } + + public void CircleXZ (Vector3 center, float radius, Color color, float startAngle = 0f, float endAngle = 2*Mathf.PI) { + int steps = 40; + +#if UNITY_EDITOR + if (gizmos) steps = (int)Mathf.Clamp(Mathf.Sqrt(radius / UnityEditor.HandleUtility.GetHandleSize((UnityEngine.Gizmos.matrix * matrix).MultiplyPoint3x4(center))) * 25, 4, 40); +#endif + while (startAngle > endAngle) startAngle -= 2*Mathf.PI; + + Vector3 prev = new Vector3(Mathf.Cos(startAngle)*radius, 0, Mathf.Sin(startAngle)*radius); + for (int i = 0; i <= steps; i++) { + Vector3 c = new Vector3(Mathf.Cos(Mathf.Lerp(startAngle, endAngle, i/(float)steps))*radius, 0, Mathf.Sin(Mathf.Lerp(startAngle, endAngle, i/(float)steps))*radius); + Line(center + prev, center + c, color); + prev = c; + } + } + + public void Cylinder (Vector3 position, Vector3 up, float height, float radius, Color color) { + var tangent = Vector3.Cross(up, Vector3.one).normalized; + + matrix = Matrix4x4.TRS(position, Quaternion.LookRotation(tangent, up), new Vector3(radius, height, radius)); + CircleXZ(Vector3.zero, 1, color); + + if (height > 0) { + CircleXZ(Vector3.up, 1, color); + Line(new Vector3(1, 0, 0), new Vector3(1, 1, 0), color); + Line(new Vector3(-1, 0, 0), new Vector3(-1, 1, 0), color); + Line(new Vector3(0, 0, 1), new Vector3(0, 1, 1), color); + Line(new Vector3(0, 0, -1), new Vector3(0, 1, -1), color); + } + + matrix = Matrix4x4.identity; + } + + public void CrossXZ (Vector3 position, Color color, float size = 1) { + size *= 0.5f; + Line(position - Vector3.right*size, position + Vector3.right*size, color); + Line(position - Vector3.forward*size, position + Vector3.forward*size, color); + } + + public void Bezier (Vector3 a, Vector3 b, Color color) { + Vector3 dir = b - a; + + if (dir == Vector3.zero) return; + + Vector3 normal = Vector3.Cross(Vector3.up, dir); + Vector3 normalUp = Vector3.Cross(dir, normal); + + normalUp = normalUp.normalized; + normalUp *= dir.magnitude*0.1f; + + Vector3 p1c = a + normalUp; + Vector3 p2c = b + normalUp; + + Vector3 prev = a; + for (int i = 1; i <= 20; i++) { + float t = i/20.0f; + Vector3 p = AstarSplines.CubicBezier(a, p1c, p2c, b, t); + Line(prev, p, color); + prev = p; + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Draw.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Draw.cs.meta new file mode 100644 index 0000000..394027b --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Draw.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 506739df886ce4ebb9b14b16d86b5e13 +timeCreated: 1492346087 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/EditorResourceHelper.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/EditorResourceHelper.cs new file mode 100644 index 0000000..00bee5f --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/EditorResourceHelper.cs @@ -0,0 +1,97 @@ +namespace Pathfinding { +#if UNITY_EDITOR + using UnityEditor; + using UnityEngine; + using System.Collections.Generic; + + /// Internal utility class for looking up editor resources + public static class EditorResourceHelper { + /// + /// Path to the editor assets folder for the A* Pathfinding Project. If this path turns out to be incorrect, the script will try to find the correct path + /// See: LoadStyles + /// + public static string editorAssets; + + static EditorResourceHelper () { + // Look up editor assets directory when first accessed + LocateEditorAssets(); + } + + static Material surfaceMat, lineMat; + public static Material GizmoSurfaceMaterial { + get { + if (!surfaceMat) surfaceMat = UnityEditor.AssetDatabase.LoadAssetAtPath(EditorResourceHelper.editorAssets + "/Materials/Navmesh.mat", typeof(Material)) as Material; + return surfaceMat; + } + } + + public static Material GizmoLineMaterial { + get { + if (!lineMat) lineMat = UnityEditor.AssetDatabase.LoadAssetAtPath(EditorResourceHelper.editorAssets + "/Materials/NavmeshOutline.mat", typeof(Material)) as Material; + return lineMat; + } + } + + /// Locates the editor assets folder in case the user has moved it + public static bool LocateEditorAssets () { +#if UNITY_2019_3_OR_NEWER + var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(EditorResourceHelper).Assembly); + if (package != null) { + editorAssets = package.assetPath + "/Editor/EditorAssets"; + if (System.IO.File.Exists(package.resolvedPath + "/Editor/EditorAssets/AstarEditorSkinLight.guiskin")) { + return true; + } else { + Debug.LogError("Could not find editor assets folder in package at " + editorAssets + ". Is the package corrupt?"); + return false; + } + } +#endif + + string projectPath = Application.dataPath; + + if (projectPath.EndsWith("/Assets")) { + projectPath = projectPath.Remove(projectPath.Length-("Assets".Length)); + } + + editorAssets = "Assets/AstarPathfindingProject/Editor/EditorAssets"; + if (!System.IO.File.Exists(projectPath + editorAssets + "/AstarEditorSkinLight.guiskin") && !System.IO.File.Exists(projectPath + editorAssets + "/AstarEditorSkin.guiskin")) { + //Initiate search + + var sdir = new System.IO.DirectoryInfo(Application.dataPath); + + var dirQueue = new Queue(); + dirQueue.Enqueue(sdir); + + bool found = false; + while (dirQueue.Count > 0) { + System.IO.DirectoryInfo dir = dirQueue.Dequeue(); + if (System.IO.File.Exists(dir.FullName + "/AstarEditorSkinLight.guiskin") || System.IO.File.Exists(dir.FullName + "/AstarEditorSkin.guiskin")) { + // Handle windows file paths + string path = dir.FullName.Replace('\\', '/'); + found = true; + // Remove data path from string to make it relative + path = path.Replace(projectPath, ""); + + if (path.StartsWith("/")) { + path = path.Remove(0, 1); + } + + editorAssets = path; + return true; + } + var dirs = dir.GetDirectories(); + for (int i = 0; i < dirs.Length; i++) { + dirQueue.Enqueue(dirs[i]); + } + } + + if (!found) { + Debug.LogWarning("Could not locate editor assets folder. Make sure you have imported the package correctly.\nA* Pathfinding Project"); + return false; + } + } + return true; + } + } +#endif +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/EditorResourceHelper.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/EditorResourceHelper.cs.meta new file mode 100644 index 0000000..f194628 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/EditorResourceHelper.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8127bc49e9e2d42dfa7a4e057842f165 +timeCreated: 1480419306 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphEditorBase.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphEditorBase.cs new file mode 100644 index 0000000..6451d13 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphEditorBase.cs @@ -0,0 +1,10 @@ +using Pathfinding.Serialization; + +namespace Pathfinding { + [JsonOptIn] + /// Defined here only so non-editor classes can use the field + public class GraphEditorBase { + /// NavGraph this editor is exposing + public NavGraph target; + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphEditorBase.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphEditorBase.cs.meta new file mode 100644 index 0000000..1a638c9 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphEditorBase.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 704136724bc95455ebe477f42f5c5a84 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphModifier.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphModifier.cs new file mode 100644 index 0000000..ca8af4a --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphModifier.cs @@ -0,0 +1,204 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Pathfinding { + /// + /// GraphModifier is used for modifying graphs or processing graph data based on events. + /// This class is a simple container for a number of events. + /// + /// Warning: Some events will be called both in play mode and in editor mode (at least the scan events). + /// So make sure your code handles both cases well. You may choose to ignore editor events. + /// See: Application.IsPlaying + /// + [ExecuteInEditMode] + public abstract class GraphModifier : VersionedMonoBehaviour { + /// All active graph modifiers + private static GraphModifier root; + + private GraphModifier prev; + private GraphModifier next; + + /// Unique persistent ID for this component, used for serialization + [SerializeField] + [HideInInspector] + protected ulong uniqueID; + + /// Maps persistent IDs to the component that uses it + protected static Dictionary usedIDs = new Dictionary(); + + protected static List GetModifiersOfType() where T : GraphModifier { + var current = root; + var result = new List(); + + while (current != null) { + var cast = current as T; + if (cast != null) result.Add(cast); + current = current.next; + } + return result; + } + + public static void FindAllModifiers () { + var allModifiers = FindObjectsOfType(typeof(GraphModifier)) as GraphModifier[]; + + for (int i = 0; i < allModifiers.Length; i++) { + if (allModifiers[i].enabled) allModifiers[i].OnEnable(); + } + } + + /// GraphModifier event type + public enum EventType { + PostScan = 1 << 0, + PreScan = 1 << 1, + LatePostScan = 1 << 2, + PreUpdate = 1 << 3, + PostUpdate = 1 << 4, + PostCacheLoad = 1 << 5 + } + + /// Triggers an event for all active graph modifiers + public static void TriggerEvent (GraphModifier.EventType type) { + if (!Application.isPlaying) { + FindAllModifiers(); + } + + GraphModifier c = root; + switch (type) { + case EventType.PreScan: + while (c != null) { c.OnPreScan(); c = c.next; } + break; + case EventType.PostScan: + while (c != null) { c.OnPostScan(); c = c.next; } + break; + case EventType.LatePostScan: + while (c != null) { c.OnLatePostScan(); c = c.next; } + break; + case EventType.PreUpdate: + while (c != null) { c.OnGraphsPreUpdate(); c = c.next; } + break; + case EventType.PostUpdate: + while (c != null) { c.OnGraphsPostUpdate(); c = c.next; } + break; + case EventType.PostCacheLoad: + while (c != null) { c.OnPostCacheLoad(); c = c.next; } + break; + } + } + + /// Adds this modifier to list of active modifiers + protected virtual void OnEnable () { + RemoveFromLinkedList(); + AddToLinkedList(); + ConfigureUniqueID(); + } + + /// Removes this modifier from list of active modifiers + protected virtual void OnDisable () { + RemoveFromLinkedList(); + } + + protected override void Awake () { + base.Awake(); + ConfigureUniqueID(); + } + + void ConfigureUniqueID () { + // Check if any other object is using the same uniqueID + // In that case this object may have been duplicated + GraphModifier usedBy; + + if (usedIDs.TryGetValue(uniqueID, out usedBy) && usedBy != this) { + Reset(); + } + + usedIDs[uniqueID] = this; + } + + void AddToLinkedList () { + if (root == null) { + root = this; + } else { + next = root; + root.prev = this; + root = this; + } + } + + void RemoveFromLinkedList () { + if (root == this) { + root = next; + if (root != null) root.prev = null; + } else { + if (prev != null) prev.next = next; + if (next != null) next.prev = prev; + } + prev = null; + next = null; + } + + protected virtual void OnDestroy () { + usedIDs.Remove(uniqueID); + } + + /// + /// Called right after all graphs have been scanned. + /// FloodFill and other post processing has not been done. + /// + /// Warning: Since OnEnable and Awake are called roughly in the same time, the only way + /// to ensure that these scripts get this call when scanning in Awake is to + /// set the Script Execution Order for AstarPath to some time later than default time + /// (see Edit -> Project Settings -> Script Execution Order). + /// TODO: Is this still relevant? A call to FindAllModifiers should have before this method is called + /// so the above warning is probably not relevant anymore. + /// + /// See: OnLatePostScan + /// + public virtual void OnPostScan () {} + + /// + /// Called right before graphs are going to be scanned. + /// + /// Warning: Since OnEnable and Awake are called roughly in the same time, the only way + /// to ensure that these scripts get this call when scanning in Awake is to + /// set the Script Execution Order for AstarPath to some time later than default time + /// (see Edit -> Project Settings -> Script Execution Order). + /// TODO: Is this still relevant? A call to FindAllModifiers should have before this method is called + /// so the above warning is probably not relevant anymore. + /// + /// See: OnLatePostScan + /// + public virtual void OnPreScan () {} + + /// + /// Called at the end of the scanning procedure. + /// This is the absolute last thing done by Scan. + /// + public virtual void OnLatePostScan () {} + + /// + /// Called after cached graphs have been loaded. + /// When using cached startup, this event is analogous to OnLatePostScan and implementing scripts + /// should do roughly the same thing for both events. + /// + public virtual void OnPostCacheLoad () {} + + /// Called before graphs are updated using GraphUpdateObjects + public virtual void OnGraphsPreUpdate () {} + + /// + /// Called after graphs have been updated using GraphUpdateObjects. + /// Eventual flood filling has been done + /// + public virtual void OnGraphsPostUpdate () {} + + protected override void Reset () { + base.Reset(); + // Create a new random 64 bit value (62 bit actually because we skip negative numbers, but that's still enough by a huge margin) + var rnd1 = (ulong)Random.Range(0, int.MaxValue); + var rnd2 = ((ulong)Random.Range(0, int.MaxValue) << 32); + + uniqueID = rnd1 | rnd2; + usedIDs[uniqueID] = this; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphModifier.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphModifier.cs.meta new file mode 100644 index 0000000..e9792b1 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphModifier.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 39897fb482672480a817862c3909a4aa +timeCreated: 1490044676 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: -222 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUpdateProcessor.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUpdateProcessor.cs new file mode 100644 index 0000000..c4fb346 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUpdateProcessor.cs @@ -0,0 +1,354 @@ +using System.Collections.Generic; +using System.Threading; +using UnityEngine; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +namespace Pathfinding { + using UnityEngine.Assertions; + +#if NETFX_CORE + using Thread = Pathfinding.WindowsStore.Thread; +#else + using Thread = System.Threading.Thread; +#endif + + class GraphUpdateProcessor { + public event System.Action OnGraphsUpdated; + + /// Holds graphs that can be updated + readonly AstarPath astar; + +#if !UNITY_WEBGL + /// + /// Reference to the thread which handles async graph updates. + /// See: ProcessGraphUpdatesAsync + /// + Thread graphUpdateThread; +#endif + + /// Used for IsAnyGraphUpdateInProgress + bool anyGraphUpdateInProgress; + +#if UNITY_2017_3_OR_NEWER && !UNITY_WEBGL + CustomSampler asyncUpdateProfilingSampler; +#endif + + /// + /// Queue containing all waiting graph update queries. Add to this queue by using \link AddToQueue \endlink. + /// See: AddToQueue + /// + readonly Queue graphUpdateQueue = new Queue(); + + /// Queue of all async graph updates waiting to be executed + readonly Queue graphUpdateQueueAsync = new Queue(); + + /// Queue of all non-async graph update post events waiting to be executed + readonly Queue graphUpdateQueuePost = new Queue(); + + /// Queue of all non-async graph updates waiting to be executed + readonly Queue graphUpdateQueueRegular = new Queue(); + + readonly System.Threading.ManualResetEvent asyncGraphUpdatesComplete = new System.Threading.ManualResetEvent(true); + +#if !UNITY_WEBGL + readonly System.Threading.AutoResetEvent graphUpdateAsyncEvent = new System.Threading.AutoResetEvent(false); + readonly System.Threading.AutoResetEvent exitAsyncThread = new System.Threading.AutoResetEvent(false); +#endif + + /// Returns if any graph updates are waiting to be applied + public bool IsAnyGraphUpdateQueued { get { return graphUpdateQueue.Count > 0; } } + + /// Returns if any graph updates are in progress + public bool IsAnyGraphUpdateInProgress { get { return anyGraphUpdateInProgress; } } + + /// Order type for updating graphs + enum GraphUpdateOrder { + GraphUpdate, + // FloodFill + } + + /// Holds a single update that needs to be performed on a graph + struct GUOSingle { + public GraphUpdateOrder order; + public IUpdatableGraph graph; + public GraphUpdateObject obj; + } + + public GraphUpdateProcessor (AstarPath astar) { + this.astar = astar; + } + + /// Work item which can be used to apply all queued updates + public AstarWorkItem GetWorkItem () { + return new AstarWorkItem(QueueGraphUpdatesInternal, ProcessGraphUpdates); + } + + public void EnableMultithreading () { +#if !UNITY_WEBGL + if (graphUpdateThread == null || !graphUpdateThread.IsAlive) { +#if UNITY_2017_3_OR_NEWER && !UNITY_WEBGL + asyncUpdateProfilingSampler = CustomSampler.Create("Graph Update"); +#endif + + graphUpdateThread = new Thread(ProcessGraphUpdatesAsync); + graphUpdateThread.IsBackground = true; + + // Set the thread priority for graph updates + // Unless compiling for windows store or windows phone which does not support it +#if !UNITY_WINRT + graphUpdateThread.Priority = System.Threading.ThreadPriority.Lowest; +#endif + graphUpdateThread.Start(); + } +#endif + } + + public void DisableMultithreading () { +#if !UNITY_WEBGL + if (graphUpdateThread != null && graphUpdateThread.IsAlive) { + // Resume graph update thread, will cause it to terminate + exitAsyncThread.Set(); + + if (!graphUpdateThread.Join(5*1000)) { + Debug.LogError("Graph update thread did not exit in 5 seconds"); + } + + graphUpdateThread = null; + } +#endif + } + + /// + /// Update all graphs using the GraphUpdateObject. + /// This can be used to, e.g make all nodes in an area unwalkable, or set them to a higher penalty. + /// The graphs will be updated as soon as possible (with respect to AstarPath.batchGraphUpdates) + /// + /// See: FlushGraphUpdates + /// + public void AddToQueue (GraphUpdateObject ob) { + // Put the GUO in the queue + graphUpdateQueue.Enqueue(ob); + } + + /// Schedules graph updates internally + void QueueGraphUpdatesInternal () { + while (graphUpdateQueue.Count > 0) { + GraphUpdateObject ob = graphUpdateQueue.Dequeue(); + + foreach (IUpdatableGraph g in astar.data.GetUpdateableGraphs()) { + NavGraph gr = g as NavGraph; + if (ob.nnConstraint == null || ob.nnConstraint.SuitableGraph(astar.data.GetGraphIndex(gr), gr)) { + var guo = new GUOSingle(); + guo.order = GraphUpdateOrder.GraphUpdate; + guo.obj = ob; + guo.graph = g; + graphUpdateQueueRegular.Enqueue(guo); + } + } + } + + GraphModifier.TriggerEvent(GraphModifier.EventType.PreUpdate); + anyGraphUpdateInProgress = true; + } + + /// + /// Updates graphs. + /// Will do some graph updates, possibly signal another thread to do them. + /// Will only process graph updates added by QueueGraphUpdatesInternal + /// + /// Returns: True if all graph updates have been done and pathfinding (or other tasks) may resume. + /// False if there are still graph updates being processed or waiting in the queue. + /// + /// If true, all graph updates will be processed before this function returns. The return value + /// will be True. + bool ProcessGraphUpdates (bool force) { + Assert.IsTrue(anyGraphUpdateInProgress); + + if (force) { + asyncGraphUpdatesComplete.WaitOne(); + } else { +#if !UNITY_WEBGL + if (!asyncGraphUpdatesComplete.WaitOne(0)) { + return false; + } +#endif + } + + Assert.AreEqual(graphUpdateQueueAsync.Count, 0, "Queue should be empty at this stage"); + + ProcessPostUpdates(); + if (!ProcessRegularUpdates(force)) { + return false; + } + + GraphModifier.TriggerEvent(GraphModifier.EventType.PostUpdate); + if (OnGraphsUpdated != null) OnGraphsUpdated(); + + Assert.AreEqual(graphUpdateQueueRegular.Count, 0, "QueueRegular should be empty at this stage"); + Assert.AreEqual(graphUpdateQueueAsync.Count, 0, "QueueAsync should be empty at this stage"); + Assert.AreEqual(graphUpdateQueuePost.Count, 0, "QueuePost should be empty at this stage"); + + anyGraphUpdateInProgress = false; + return true; + } + + bool ProcessRegularUpdates (bool force) { + while (graphUpdateQueueRegular.Count > 0) { + GUOSingle s = graphUpdateQueueRegular.Peek(); + + GraphUpdateThreading threading = s.graph.CanUpdateAsync(s.obj); + +#if UNITY_WEBGL + // Never use multithreading in WebGL + threading &= ~GraphUpdateThreading.SeparateThread; +#else + // When not playing or when not using a graph update thread (or if it has crashed), everything runs in the Unity thread + if (force || !Application.isPlaying || graphUpdateThread == null || !graphUpdateThread.IsAlive) { + // Remove the SeparateThread flag + threading &= ~GraphUpdateThreading.SeparateThread; + } +#endif + + if ((threading & GraphUpdateThreading.UnityInit) != 0) { + // Process async graph updates first. + // Next call to this function will process this object so it is not dequeued now + if (StartAsyncUpdatesIfQueued()) { + return false; + } + + s.graph.UpdateAreaInit(s.obj); + } + + if ((threading & GraphUpdateThreading.SeparateThread) != 0) { + // Move GUO to async queue to be updated by another thread + graphUpdateQueueRegular.Dequeue(); + graphUpdateQueueAsync.Enqueue(s); + + // Don't start any more async graph updates because this update + // requires a Unity thread function to run after it has been completed + // but before the next update is started + if ((threading & GraphUpdateThreading.UnityPost) != 0) { + if (StartAsyncUpdatesIfQueued()) { + return false; + } + } + } else { + // Unity Thread + + if (StartAsyncUpdatesIfQueued()) { + return false; + } + + graphUpdateQueueRegular.Dequeue(); + + try { + s.graph.UpdateArea(s.obj); + } catch (System.Exception e) { + Debug.LogError("Error while updating graphs\n"+e); + } + + if ((threading & GraphUpdateThreading.UnityPost) != 0) { + s.graph.UpdateAreaPost(s.obj); + } + } + } + + if (StartAsyncUpdatesIfQueued()) { + return false; + } + + return true; + } + + /// + /// Signal the graph update thread to start processing graph updates if there are any in the queue. + /// Returns: True if the other thread was signaled. + /// + bool StartAsyncUpdatesIfQueued () { + if (graphUpdateQueueAsync.Count > 0) { +#if UNITY_WEBGL + throw new System.Exception("This should not happen in WebGL"); +#else + asyncGraphUpdatesComplete.Reset(); + graphUpdateAsyncEvent.Set(); + return true; +#endif + } + return false; + } + + void ProcessPostUpdates () { + while (graphUpdateQueuePost.Count > 0) { + GUOSingle s = graphUpdateQueuePost.Dequeue(); + + GraphUpdateThreading threading = s.graph.CanUpdateAsync(s.obj); + + if ((threading & GraphUpdateThreading.UnityPost) != 0) { + try { + s.graph.UpdateAreaPost(s.obj); + } catch (System.Exception e) { + Debug.LogError("Error while updating graphs (post step)\n"+e); + } + } + } + } + +#if !UNITY_WEBGL + /// + /// Graph update thread. + /// Async graph updates will be executed by this method in another thread. + /// + void ProcessGraphUpdatesAsync () { +#if UNITY_2017_3_OR_NEWER + Profiler.BeginThreadProfiling("Pathfinding", "Threaded Graph Updates"); +#endif + + var handles = new [] { graphUpdateAsyncEvent, exitAsyncThread }; + + while (true) { + // Wait for the next batch or exit event + var handleIndex = WaitHandle.WaitAny(handles); + + if (handleIndex == 1) { + // Exit even was fired + // Abort thread and clear queue + graphUpdateQueueAsync.Clear(); + asyncGraphUpdatesComplete.Set(); +#if UNITY_2017_3_OR_NEWER + Profiler.EndThreadProfiling(); +#endif + return; + } + + while (graphUpdateQueueAsync.Count > 0) { +#if UNITY_2017_3_OR_NEWER + asyncUpdateProfilingSampler.Begin(); +#endif + // Note that no locking is required here because the main thread + // cannot access it until asyncGraphUpdatesComplete is signaled + GUOSingle aguo = graphUpdateQueueAsync.Dequeue(); + + try { + if (aguo.order == GraphUpdateOrder.GraphUpdate) { + aguo.graph.UpdateArea(aguo.obj); + graphUpdateQueuePost.Enqueue(aguo); + } else { + throw new System.NotSupportedException("" + aguo.order); + } + } catch (System.Exception e) { + Debug.LogError("Exception while updating graphs:\n"+e); + } +#if UNITY_2017_3_OR_NEWER + asyncUpdateProfilingSampler.End(); +#endif + } + + // Done + asyncGraphUpdatesComplete.Set(); + } + } +#endif + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUpdateProcessor.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUpdateProcessor.cs.meta new file mode 100644 index 0000000..b0b0295 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUpdateProcessor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b1798d8e7c7d54972ae8522558cbd27c +timeCreated: 1443114816 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUtilities.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUtilities.cs new file mode 100644 index 0000000..224dd51 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUtilities.cs @@ -0,0 +1,270 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Pathfinding { + using Pathfinding.Util; + + /// + /// Contains utility methods for getting useful information out of graph. + /// This class works a lot with the class, a useful function to get nodes is . + /// + /// See: + /// See: + /// See: + /// + /// \ingroup utils + /// + public static class GraphUtilities { + /// + /// Convenience method to get a list of all segments of the contours of a graph. + /// Returns: A list of segments. Every 2 elements form a line segment. The first segment is (result[0], result[1]), the second one is (result[2], result[3]) etc. + /// The line segments are oriented so that the navmesh is on the right side of the segments when seen from above. + /// + /// This method works for navmesh, recast, grid graphs and layered grid graphs. For other graph types it will return an empty list. + /// + /// If you need more information about how the contours are connected you can take a look at the other variants of this method. + /// + /// + /// // Get the first graph + /// var navmesh = AstarPath.active.graphs[0]; + /// + /// // Get all contours of the graph (works for grid, navmesh and recast graphs) + /// var segments = GraphUtilities.GetContours(navmesh); + /// + /// // Every 2 elements form a line segment. The first segment is (segments[0], segments[1]), the second one is (segments[2], segments[3]) etc. + /// // The line segments are oriented so that the navmesh is on the right side of the segments when seen from above. + /// for (int i = 0; i < segments.Count; i += 2) { + /// var start = segments[i]; + /// var end = segments[i+1]; + /// Debug.DrawLine(start, end, Color.red, 3); + /// } + /// + /// + /// [Open online documentation to see images] + /// [Open online documentation to see images] + /// + public static List GetContours (NavGraph graph) { + List result = ListPool.Claim (); + + if (graph is INavmesh) { + GetContours(graph as INavmesh, (vertices, cycle) => { + for (int j = cycle ? vertices.Count - 1 : 0, i = 0; i < vertices.Count; j = i, i++) { + result.Add((Vector3)vertices[j]); + result.Add((Vector3)vertices[i]); + } + }); +#if !ASTAR_NO_GRID_GRAPH + } else if (graph is GridGraph) { + GetContours(graph as GridGraph, vertices => { + for (int j = vertices.Length - 1, i = 0; i < vertices.Length; j = i, i++) { + result.Add((Vector3)vertices[j]); + result.Add((Vector3)vertices[i]); + } + }, 0); +#endif + } + return result; + } + + /// + /// Traces the contour of a navmesh. + /// + /// [Open online documentation to see images] + /// + /// This image is just used to illustrate the difference between chains and cycles. That it shows a grid graph is not relevant. + /// [Open online documentation to see images] + /// + /// See: + /// + /// The navmesh-like object to trace. This can be a recast or navmesh graph or it could be a single tile in one such graph. + /// Will be called once for each contour with the contour as a parameter as well as a boolean indicating if the contour is a cycle or a chain (see second image). + public static void GetContours (INavmesh navmesh, System.Action, bool> results) { + // Assume 3 vertices per node + var uses = new bool[3]; + + var outline = new Dictionary(); + var vertexPositions = new Dictionary(); + var hasInEdge = new HashSet(); + + navmesh.GetNodes(_node => { + var node = _node as TriangleMeshNode; + + uses[0] = uses[1] = uses[2] = false; + + if (node != null) { + // Find out which edges are shared with other nodes + for (int j = 0; j < node.connections.Length; j++) { + var other = node.connections[j].node as TriangleMeshNode; + + // Not necessarily a TriangleMeshNode + if (other != null) { + int a = node.SharedEdge(other); + if (a != -1) uses[a] = true; + } + } + + // Loop through all edges on the node + for (int j = 0; j < 3; j++) { + // The edge is not shared with any other node + // I.e it is an exterior edge on the mesh + if (!uses[j]) { + var i1 = j; + var i2 = (j+1) % node.GetVertexCount(); + + outline[node.GetVertexIndex(i1)] = node.GetVertexIndex(i2); + hasInEdge.Add(node.GetVertexIndex(i2)); + vertexPositions[node.GetVertexIndex(i1)] = node.GetVertex(i1); + vertexPositions[node.GetVertexIndex(i2)] = node.GetVertex(i2); + } + } + } + }); + + Polygon.TraceContours(outline, hasInEdge, (chain, cycle) => { + List vertices = ListPool.Claim(); + for (int i = 0; i < chain.Count; i++) vertices.Add(vertexPositions[chain[i]]); + results(vertices, cycle); + }); + } + +#if !ASTAR_NO_GRID_GRAPH + /// + /// Finds all contours of a collection of nodes in a grid graph. + /// + /// + /// var grid = AstarPath.active.data.gridGraph; + /// + /// // Find all contours in the graph and draw them using debug lines + /// GraphUtilities.GetContours(grid, vertices => { + /// for (int i = 0; i < vertices.Length; i++) { + /// Debug.DrawLine(vertices[i], vertices[(i+1)%vertices.Length], Color.red, 4); + /// } + /// }, 0); + /// + /// + /// In the image below you can see the contour of a graph. + /// [Open online documentation to see images] + /// + /// In the image below you can see the contour of just a part of a grid graph (when the nodes parameter is supplied) + /// [Open online documentation to see images] + /// + /// Contour of a hexagon graph + /// [Open online documentation to see images] + /// + /// See: + /// + /// The grid to find the contours of + /// The callback will be called once for every contour that is found with the vertices of the contour. The contour always forms a cycle. + /// Contours will be simplified if the y coordinates for adjacent vertices differ by no more than this value. + /// Only these nodes will be searched. If this parameter is null then all nodes in the grid graph will be searched. + public static void GetContours (GridGraph grid, System.Action callback, float yMergeThreshold, GridNodeBase[] nodes = null) { + // Set of all allowed nodes or null if all nodes are allowed + HashSet nodeSet = nodes != null ? new HashSet(nodes) : null; + + // Use all nodes if the nodes parameter is null + nodes = nodes ?? grid.nodes; + int[] neighbourXOffsets = grid.neighbourXOffsets; + int[] neighbourZOffsets = grid.neighbourZOffsets; + var neighbourIndices = grid.neighbours == NumNeighbours.Six ? GridGraph.hexagonNeighbourIndices : new [] { 0, 1, 2, 3 }; + var offsetMultiplier = grid.neighbours == NumNeighbours.Six ? 1/3f : 0.5f; + + if (nodes != null) { + var trace = ListPool.Claim (); + var seenStates = new HashSet(); + + for (int i = 0; i < nodes.Length; i++) { + var startNode = nodes[i]; + // The third check is a fast check for if the node has connections in all grid directions, if it has then we can skip processing it (unless the nodes parameter was used in which case we have to handle the edge cases) + if (startNode != null && startNode.Walkable && (!startNode.HasConnectionsToAllEightNeighbours || nodeSet != null)) { + for (int startDir = 0; startDir < neighbourIndices.Length; startDir++) { + int startState = (startNode.NodeIndex << 4) | startDir; + + // Check if there is an obstacle in that direction + var startNeighbour = startNode.GetNeighbourAlongDirection(neighbourIndices[startDir]); + if ((startNeighbour == null || (nodeSet != null && !nodeSet.Contains(startNeighbour))) && !seenStates.Contains(startState)) { + // Start tracing a contour here + trace.ClearFast(); + int dir = startDir; + GridNodeBase node = startNode; + + while (true) { + int state = (node.NodeIndex << 4) | dir; + if (state == startState && trace.Count > 0) { + break; + } + + seenStates.Add(state); + + var neighbour = node.GetNeighbourAlongDirection(neighbourIndices[dir]); + if (neighbour == null || (nodeSet != null && !nodeSet.Contains(neighbour))) { + // Draw edge + var d0 = neighbourIndices[dir]; + dir = (dir + 1) % neighbourIndices.Length; + var d1 = neighbourIndices[dir]; + + // Position in graph space of the vertex + Vector3 graphSpacePos = new Vector3(node.XCoordinateInGrid + 0.5f, 0, node.ZCoordinateInGrid + 0.5f); + // Offset along diagonal to get the correct XZ coordinates + graphSpacePos.x += (neighbourXOffsets[d0] + neighbourXOffsets[d1]) * offsetMultiplier; + graphSpacePos.z += (neighbourZOffsets[d0] + neighbourZOffsets[d1]) * offsetMultiplier; + graphSpacePos.y = grid.transform.InverseTransform((Vector3)node.position).y; + + if (trace.Count >= 2) { + var v0 = trace[trace.Count-2]; + var v1 = trace[trace.Count-1]; + var v1d = v1 - v0; + var v2d = graphSpacePos - v0; + // Replace the previous point if it is colinear with the point just before it and just after it (the current point), because that point wouldn't add much information, but it would add CPU overhead + if (((Mathf.Abs(v1d.x) > 0.01f || Mathf.Abs(v2d.x) > 0.01f) && (Mathf.Abs(v1d.z) > 0.01f || Mathf.Abs(v2d.z) > 0.01f)) || (Mathf.Abs(v1d.y) > yMergeThreshold || Mathf.Abs(v2d.y) > yMergeThreshold)) { + trace.Add(graphSpacePos); + } else { + trace[trace.Count-1] = graphSpacePos; + } + } else { + trace.Add(graphSpacePos); + } + } else { + // Move + node = neighbour; + dir = (dir + neighbourIndices.Length/2 + 1) % neighbourIndices.Length; + } + } + + // Simplify the contour a bit around the start point. + // Otherwise we might return a cycle which was not as simplified as possible and the number of vertices + // would depend on where in the cycle the algorithm started to traverse the contour. + if (trace.Count >= 3) { + var v0 = trace[trace.Count-2]; + var v1 = trace[trace.Count-1]; + var v1d = v1 - v0; + var v2d = trace[0] - v0; + // Replace the previous point if it is colinear with the point just before it and just after it (the current point), because that point wouldn't add much information, but it would add CPU overhead + if (!(((Mathf.Abs(v1d.x) > 0.01f || Mathf.Abs(v2d.x) > 0.01f) && (Mathf.Abs(v1d.z) > 0.01f || Mathf.Abs(v2d.z) > 0.01f)) || (Mathf.Abs(v1d.y) > yMergeThreshold || Mathf.Abs(v2d.y) > yMergeThreshold))) { + trace.RemoveAt(trace.Count - 1); + } + } + + if (trace.Count >= 3) { + var v0 = trace[trace.Count-1]; + var v1 = trace[0]; + var v1d = v1 - v0; + var v2d = trace[1] - v0; + // Replace the previous point if it is colinear with the point just before it and just after it (the current point), because that point wouldn't add much information, but it would add CPU overhead + if (!(((Mathf.Abs(v1d.x) > 0.01f || Mathf.Abs(v2d.x) > 0.01f) && (Mathf.Abs(v1d.z) > 0.01f || Mathf.Abs(v2d.z) > 0.01f)) || (Mathf.Abs(v1d.y) > yMergeThreshold || Mathf.Abs(v2d.y) > yMergeThreshold))) { + trace.RemoveAt(0); + } + } + var result = trace.ToArray(); + grid.transform.Transform(result); + callback(result); + } + } + } + } + + ListPool.Release (ref trace); + } + } +#endif + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUtilities.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUtilities.cs.meta new file mode 100644 index 0000000..e7d4200 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/GraphUtilities.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fd178834bf6c54cdb8fc5f76a039a91c +timeCreated: 1502889881 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/HierarchicalGraph.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/HierarchicalGraph.cs new file mode 100644 index 0000000..01286db --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/HierarchicalGraph.cs @@ -0,0 +1,346 @@ +using System.Collections.Generic; +using Pathfinding.Util; +using Pathfinding.Serialization; +using System.Linq; +using UnityEngine; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +namespace Pathfinding { + /// + /// Holds a hierarchical graph to speed up certain pathfinding queries. + /// + /// A common type of query that needs to be very fast is on the form 'is this node reachable from this other node'. + /// This is for example used when picking the end node of a path. The end node is determined as the closest node to the end point + /// that can be reached from the start node. + /// + /// This data structure's primary purpose is to keep track of which connected component each node is contained in, in order to make such queries fast. + /// + /// See: https://en.wikipedia.org/wiki/Connected_component_(graph_theory) + /// + /// A connected component is a set of nodes such that there is a valid path between every pair of nodes in that set. + /// Thus the query above can simply be answered by checking if they are in the same connected component. + /// The connected component is exposed on nodes as the property and on this class using the method. + /// + /// In the image below (showing a 200x200 grid graph) each connected component is colored using a separate color. + /// The actual color doesn't signify anything in particular however, only that they are different. + /// [Open online documentation to see images] + /// + /// Prior to version 4.2 the connected components were just a number stored on each node, and when a graph was updated + /// the connected components were completely recalculated. This can be done relatively efficiently using a flood filling + /// algorithm (see https://en.wikipedia.org/wiki/Flood_fill) however it still requires a pass through every single node + /// which can be quite costly on larger graphs. + /// + /// This class instead builds a much smaller graph that still respects the same connectivity as the original graph. + /// Each node in this hierarchical graph represents a larger number of real nodes that are one single connected component. + /// Take a look at the image below for an example. In the image each color is a separate hierarchical node, and the black connections go between the center of each hierarchical node. + /// + /// [Open online documentation to see images] + /// + /// With the hierarchical graph, the connected components can be calculated by flood filling the hierarchical graph instead of the real graph. + /// Then when we need to know which connected component a node belongs to, we look up the connected component of the hierarchical node the node belongs to. + /// + /// The benefit is not immediately obvious. The above is just a bit more complicated way to accomplish the same thing. However the real benefit comes when updating the graph. + /// When the graph is updated, all hierarchical nodes which contain any node that was affected by the update is removed completely and then once all have been removed new hierarchical nodes are recalculated in their place. + /// Once this is done the connected components of the whole graph can be updated by flood filling only the hierarchical graph. Since the hierarchical graph is vastly smaller than the real graph, this is significantly faster. + /// + /// [Open online documentation to see videos] + /// + /// So finally using all of this, the connected components of the graph can be recalculated very quickly as the graph is updated. + /// The effect of this grows larger the larger the graph is, and the smaller the graph update is. Making a small update to a 1000x1000 grid graph is on the order of 40 times faster with these optimizations. + /// When scanning a graph or making updates to the whole graph at the same time there is however no speed boost. In fact due to the extra complexity it is a bit slower, however after profiling the extra time seems to be mostly insignificant compared to the rest of the cost of scanning the graph. + /// + /// [Open online documentation to see videos] + /// + /// See: + /// See: + /// See: + /// + public class HierarchicalGraph { + const int Tiling = 16; + const int MaxChildrenPerNode = Tiling * Tiling; + const int MinChildrenPerNode = MaxChildrenPerNode/2; + + List[] children = new List[0]; + List[] connections = new List[0]; + int[] areas = new int[0]; + byte[] dirty = new byte[0]; + + public int version { get; private set; } + public System.Action onConnectedComponentsChanged; + + System.Action connectionCallback; + + Queue temporaryQueue = new Queue(); + List currentChildren = null; + List currentConnections = null; + int currentHierarchicalNodeIndex; + Stack temporaryStack = new Stack(); + + int numDirtyNodes = 0; + GraphNode[] dirtyNodes = new GraphNode[128]; + + Stack freeNodeIndices = new Stack(); + + int gizmoVersion = 0; + + public HierarchicalGraph () { + // Cache this callback to avoid allocating a new one every time the FindHierarchicalNodeChildren method is called. + // It is a big ugly to have to use member variables for the state information in that method, but I see no better way. + connectionCallback = (GraphNode neighbour) => { + var hIndex = neighbour.HierarchicalNodeIndex; + if (hIndex == 0) { + if (currentChildren.Count < MaxChildrenPerNode && neighbour.Walkable /* && (((GridNode)currentChildren[0]).XCoordinateInGrid/Tiling == ((GridNode)neighbour).XCoordinateInGrid/Tiling) && (((GridNode)currentChildren[0]).ZCoordinateInGrid/Tiling == ((GridNode)neighbour).ZCoordinateInGrid/Tiling)*/) { + neighbour.HierarchicalNodeIndex = currentHierarchicalNodeIndex; + temporaryQueue.Enqueue(neighbour); + currentChildren.Add(neighbour); + } + } else if (hIndex != currentHierarchicalNodeIndex && !currentConnections.Contains(hIndex)) { + // The Contains call can in theory be very slow as an hierarchical node may be adjacent to an arbitrary number of nodes. + // However in practice due to how the nodes are constructed they will only be adjacent to a smallish (≈4-6) number of other nodes. + // So a Contains call will be much faster than say a Set lookup. + currentConnections.Add(hIndex); + } + }; + + Grow(); + } + + void Grow () { + var newChildren = new List[System.Math.Max(64, children.Length*2)]; + var newConnections = new List[newChildren.Length]; + var newAreas = new int[newChildren.Length]; + var newDirty = new byte[newChildren.Length]; + + children.CopyTo(newChildren, 0); + connections.CopyTo(newConnections, 0); + areas.CopyTo(newAreas, 0); + dirty.CopyTo(newDirty, 0); + + for (int i = children.Length; i < newChildren.Length; i++) { + newChildren[i] = ListPool.Claim (MaxChildrenPerNode); + newConnections[i] = new List(); + if (i > 0) freeNodeIndices.Push(i); + } + + children = newChildren; + connections = newConnections; + areas = newAreas; + dirty = newDirty; + } + + int GetHierarchicalNodeIndex () { + if (freeNodeIndices.Count == 0) Grow(); + return freeNodeIndices.Pop(); + } + + internal void OnCreatedNode (GraphNode node) { + if (node.NodeIndex >= dirtyNodes.Length) { + var newDirty = new GraphNode[System.Math.Max(node.NodeIndex + 1, dirtyNodes.Length*2)]; + dirtyNodes.CopyTo(newDirty, 0); + dirtyNodes = newDirty; + } + AddDirtyNode(node); + } + + internal void AddDirtyNode (GraphNode node) { + if (!node.IsHierarchicalNodeDirty) { + node.IsHierarchicalNodeDirty = true; + // While the dirtyNodes array is guaranteed to be large enough to hold all nodes in the graphs + // the array may also end up containing many destroyed nodes. This can in rare cases cause it to go out of bounds. + // In that case we need to go through the array and filter out any destroyed nodes while making sure to mark their + // corresponding hierarchical nodes as being dirty. + if (numDirtyNodes < dirtyNodes.Length) { + dirtyNodes[numDirtyNodes] = node; + numDirtyNodes++; + } else { + int maxIndex = 0; + for (int i = numDirtyNodes - 1; i >= 0; i--) { + if (dirtyNodes[i].Destroyed) { + numDirtyNodes--; + dirty[dirtyNodes[i].HierarchicalNodeIndex] = 1; + dirtyNodes[i] = dirtyNodes[numDirtyNodes]; + dirtyNodes[numDirtyNodes] = null; + } else { + maxIndex = System.Math.Max(maxIndex, dirtyNodes[i].NodeIndex); + } + } + if (numDirtyNodes >= dirtyNodes.Length) throw new System.Exception("Failed to compactify dirty nodes array. This should not happen. " + maxIndex + " " + numDirtyNodes + " " + dirtyNodes.Length); + AddDirtyNode(node); + } + } + } + + public int NumConnectedComponents { get; private set; } + + /// Get the connected component index of a hierarchical node + public uint GetConnectedComponent (int hierarchicalNodeIndex) { + return (uint)areas[hierarchicalNodeIndex]; + } + + void RemoveHierarchicalNode (int hierarchicalNode, bool removeAdjacentSmallNodes) { + freeNodeIndices.Push(hierarchicalNode); + var conns = connections[hierarchicalNode]; + + for (int i = 0; i < conns.Count; i++) { + var adjacentHierarchicalNode = conns[i]; + // If dirty, this node will be removed later anyway, so don't bother doing anything with it. + if (dirty[adjacentHierarchicalNode] != 0) continue; + + if (removeAdjacentSmallNodes && children[adjacentHierarchicalNode].Count < MinChildrenPerNode) { + dirty[adjacentHierarchicalNode] = 2; + RemoveHierarchicalNode(adjacentHierarchicalNode, false); + } else { + // Remove the connection from the other node to this node as we are removing this node. + connections[adjacentHierarchicalNode].Remove(hierarchicalNode); + } + } + conns.Clear(); + + var nodeChildren = children[hierarchicalNode]; + + for (int i = 0; i < nodeChildren.Count; i++) { + AddDirtyNode(nodeChildren[i]); + } + + nodeChildren.ClearFast(); + } + + /// Recalculate the hierarchical graph and the connected components if any nodes have been marked as dirty + public void RecalculateIfNecessary () { + if (numDirtyNodes > 0) { + Profiler.BeginSample("Recalculate Connected Components"); + for (int i = 0; i < numDirtyNodes; i++) { + dirty[dirtyNodes[i].HierarchicalNodeIndex] = 1; + } + + // Remove all hierarchical nodes and then build new hierarchical nodes in their place + // which take into account the new graph data. + for (int i = 1; i < dirty.Length; i++) { + if (dirty[i] == 1) RemoveHierarchicalNode(i, true); + } + for (int i = 1; i < dirty.Length; i++) dirty[i] = 0; + + for (int i = 0; i < numDirtyNodes; i++) { + dirtyNodes[i].HierarchicalNodeIndex = 0; + } + + for (int i = 0; i < numDirtyNodes; i++) { + var node = dirtyNodes[i]; + // Be nice to the GC + dirtyNodes[i] = null; + node.IsHierarchicalNodeDirty = false; + + if (node.HierarchicalNodeIndex == 0 && node.Walkable && !node.Destroyed) { + FindHierarchicalNodeChildren(GetHierarchicalNodeIndex(), node); + } + } + + numDirtyNodes = 0; + // Recalculate the connected components of the hierarchical nodes + FloodFill(); + Profiler.EndSample(); + gizmoVersion++; + } + } + + /// + /// Recalculate everything from scratch. + /// This is primarily to be used for legacy code for compatibility reasons, not for any new code. + /// + /// See: + /// + public void RecalculateAll () { + AstarPath.active.data.GetNodes(node => AddDirtyNode(node)); + RecalculateIfNecessary(); + } + + /// Flood fills the graph of hierarchical nodes and assigns the same area ID to all hierarchical nodes that are in the same connected component + void FloodFill () { + for (int i = 0; i < areas.Length; i++) areas[i] = 0; + + Stack stack = temporaryStack; + int currentArea = 0; + for (int i = 1; i < areas.Length; i++) { + // Already taken care of + if (areas[i] != 0) continue; + + currentArea++; + areas[i] = currentArea; + stack.Push(i); + while (stack.Count > 0) { + int node = stack.Pop(); + var conns = connections[node]; + for (int j = conns.Count - 1; j >= 0; j--) { + var otherNode = conns[j]; + // Note: slightly important that this is != currentArea and not != 0 in case there are some connected, but not stongly connected components in the graph (this will happen in only veeery few types of games) + if (areas[otherNode] != currentArea) { + areas[otherNode] = currentArea; + stack.Push(otherNode); + } + } + } + } + + NumConnectedComponents = System.Math.Max(1, currentArea + 1); + version++; + } + + /// Run a BFS out from a start node and assign up to MaxChildrenPerNode nodes to the specified hierarchical node which are not already assigned to another hierarchical node + void FindHierarchicalNodeChildren (int hierarchicalNode, GraphNode startNode) { + // Set some state for the connectionCallback delegate to use + currentChildren = children[hierarchicalNode]; + currentConnections = connections[hierarchicalNode]; + currentHierarchicalNodeIndex = hierarchicalNode; + + var que = temporaryQueue; + que.Enqueue(startNode); + + startNode.HierarchicalNodeIndex = hierarchicalNode; + currentChildren.Add(startNode); + + while (que.Count > 0) { + que.Dequeue().GetConnections(connectionCallback); + } + + for (int i = 0; i < currentConnections.Count; i++) { + connections[currentConnections[i]].Add(hierarchicalNode); + } + + que.Clear(); + } + + public void OnDrawGizmos (Pathfinding.Util.RetainedGizmos gizmos) { + var hasher = new Pathfinding.Util.RetainedGizmos.Hasher(AstarPath.active); + + hasher.AddHash(gizmoVersion); + + if (!gizmos.Draw(hasher)) { + var builder = ObjectPool.Claim (); + var centers = ArrayPool.Claim (areas.Length); + for (int i = 0; i < areas.Length; i++) { + Int3 center = Int3.zero; + var childs = children[i]; + if (childs.Count > 0) { + for (int j = 0; j < childs.Count; j++) center += childs[j].position; + center /= childs.Count; + centers[i] = (UnityEngine.Vector3)center; + } + } + + for (int i = 0; i < areas.Length; i++) { + if (children[i].Count > 0) { + for (int j = 0; j < connections[i].Count; j++) { + if (connections[i][j] > i) { + builder.DrawLine(centers[i], centers[connections[i][j]], UnityEngine.Color.black); + } + } + } + } + + builder.Submit(gizmos, hasher); + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/HierarchicalGraph.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/HierarchicalGraph.cs.meta new file mode 100644 index 0000000..349d5f1 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/HierarchicalGraph.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: aa5d6e4a6adb04d4ca9b7e735addcbd5 +timeCreated: 1535050640 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Int3.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Int3.cs new file mode 100644 index 0000000..6918e47 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Int3.cs @@ -0,0 +1,314 @@ +using UnityEngine; + +namespace Pathfinding { + /// Holds a coordinate in integers + public struct Int3 : System.IEquatable { + public int x; + public int y; + public int z; + + //These should be set to the same value (only PrecisionFactor should be 1 divided by Precision) + + /// + /// Precision for the integer coordinates. + /// One world unit is divided into [value] pieces. A value of 1000 would mean millimeter precision, a value of 1 would mean meter precision (assuming 1 world unit = 1 meter). + /// This value affects the maximum coordinates for nodes as well as how large the cost values are for moving between two nodes. + /// A higher value means that you also have to set all penalty values to a higher value to compensate since the normal cost of moving will be higher. + /// + public const int Precision = 1000; + + /// as a float + public const float FloatPrecision = 1000F; + + /// 1 divided by + public const float PrecisionFactor = 0.001F; + + public static Int3 zero { get { return new Int3(); } } + + public Int3 (Vector3 position) { + x = (int)System.Math.Round(position.x*FloatPrecision); + y = (int)System.Math.Round(position.y*FloatPrecision); + z = (int)System.Math.Round(position.z*FloatPrecision); + } + + public Int3 (int _x, int _y, int _z) { + x = _x; + y = _y; + z = _z; + } + + public static bool operator == (Int3 lhs, Int3 rhs) { + return lhs.x == rhs.x && + lhs.y == rhs.y && + lhs.z == rhs.z; + } + + public static bool operator != (Int3 lhs, Int3 rhs) { + return lhs.x != rhs.x || + lhs.y != rhs.y || + lhs.z != rhs.z; + } + + public static explicit operator Int3 (Vector3 ob) { + return new Int3( + (int)System.Math.Round(ob.x*FloatPrecision), + (int)System.Math.Round(ob.y*FloatPrecision), + (int)System.Math.Round(ob.z*FloatPrecision) + ); + } + + public static explicit operator Vector3 (Int3 ob) { + return new Vector3(ob.x*PrecisionFactor, ob.y*PrecisionFactor, ob.z*PrecisionFactor); + } + + public static Int3 operator - (Int3 lhs, Int3 rhs) { + lhs.x -= rhs.x; + lhs.y -= rhs.y; + lhs.z -= rhs.z; + return lhs; + } + + public static Int3 operator - (Int3 lhs) { + lhs.x = -lhs.x; + lhs.y = -lhs.y; + lhs.z = -lhs.z; + return lhs; + } + + public static Int3 operator + (Int3 lhs, Int3 rhs) { + lhs.x += rhs.x; + lhs.y += rhs.y; + lhs.z += rhs.z; + return lhs; + } + + public static Int3 operator * (Int3 lhs, int rhs) { + lhs.x *= rhs; + lhs.y *= rhs; + lhs.z *= rhs; + + return lhs; + } + + public static Int3 operator * (Int3 lhs, float rhs) { + lhs.x = (int)System.Math.Round(lhs.x * rhs); + lhs.y = (int)System.Math.Round(lhs.y * rhs); + lhs.z = (int)System.Math.Round(lhs.z * rhs); + + return lhs; + } + + public static Int3 operator * (Int3 lhs, double rhs) { + lhs.x = (int)System.Math.Round(lhs.x * rhs); + lhs.y = (int)System.Math.Round(lhs.y * rhs); + lhs.z = (int)System.Math.Round(lhs.z * rhs); + + return lhs; + } + + public static Int3 operator / (Int3 lhs, float rhs) { + lhs.x = (int)System.Math.Round(lhs.x / rhs); + lhs.y = (int)System.Math.Round(lhs.y / rhs); + lhs.z = (int)System.Math.Round(lhs.z / rhs); + return lhs; + } + + public int this[int i] { + get { + return i == 0 ? x : (i == 1 ? y : z); + } + set { + if (i == 0) x = value; + else if (i == 1) y = value; + else z = value; + } + } + + /// Angle between the vectors in radians + public static float Angle (Int3 lhs, Int3 rhs) { + double cos = Dot(lhs, rhs)/ ((double)lhs.magnitude*(double)rhs.magnitude); + + cos = cos < -1 ? -1 : (cos > 1 ? 1 : cos); + return (float)System.Math.Acos(cos); + } + + public static int Dot (Int3 lhs, Int3 rhs) { + return + lhs.x * rhs.x + + lhs.y * rhs.y + + lhs.z * rhs.z; + } + + public static long DotLong (Int3 lhs, Int3 rhs) { + return + (long)lhs.x * (long)rhs.x + + (long)lhs.y * (long)rhs.y + + (long)lhs.z * (long)rhs.z; + } + + /// + /// Normal in 2D space (XZ). + /// Equivalent to Cross(this, Int3(0,1,0) ) + /// except that the Y coordinate is left unchanged with this operation. + /// + public Int3 Normal2D () { + return new Int3(z, y, -x); + } + + /// + /// Returns the magnitude of the vector. The magnitude is the 'length' of the vector from 0,0,0 to this point. Can be used for distance calculations: + /// Debug.Log ("Distance between 3,4,5 and 6,7,8 is: "+(new Int3(3,4,5) - new Int3(6,7,8)).magnitude); + /// + public float magnitude { + get { + //It turns out that using doubles is just as fast as using ints with Mathf.Sqrt. And this can also handle larger numbers (possibly with small errors when using huge numbers)! + + double _x = x; + double _y = y; + double _z = z; + + return (float)System.Math.Sqrt(_x*_x+_y*_y+_z*_z); + } + } + + /// + /// Magnitude used for the cost between two nodes. The default cost between two nodes can be calculated like this: + /// int cost = (node1.position-node2.position).costMagnitude; + /// + /// This is simply the magnitude, rounded to the nearest integer + /// + public int costMagnitude { + get { + return (int)System.Math.Round(magnitude); + } + } + + /// The squared magnitude of the vector + public float sqrMagnitude { + get { + double _x = x; + double _y = y; + double _z = z; + return (float)(_x*_x+_y*_y+_z*_z); + } + } + + /// The squared magnitude of the vector + public long sqrMagnitudeLong { + get { + long _x = x; + long _y = y; + long _z = z; + return (_x*_x+_y*_y+_z*_z); + } + } + + public static implicit operator string (Int3 obj) { + return obj.ToString(); + } + + /// Returns a nicely formatted string representing the vector + public override string ToString () { + return "( "+x+", "+y+", "+z+")"; + } + + public override bool Equals (System.Object obj) { + if (obj == null) return false; + + var rhs = (Int3)obj; + + return x == rhs.x && + y == rhs.y && + z == rhs.z; + } + + #region IEquatable implementation + + public bool Equals (Int3 other) { + return x == other.x && y == other.y && z == other.z; + } + + #endregion + + public override int GetHashCode () { + return x*73856093 ^ y*19349663 ^ z*83492791; + } + } + + /// Two Dimensional Integer Coordinate Pair + public struct Int2 : System.IEquatable { + public int x; + public int y; + + public Int2 (int x, int y) { + this.x = x; + this.y = y; + } + + public long sqrMagnitudeLong { + get { + return (long)x*(long)x+(long)y*(long)y; + } + } + + public static Int2 operator + (Int2 a, Int2 b) { + return new Int2(a.x+b.x, a.y+b.y); + } + + public static Int2 operator - (Int2 a, Int2 b) { + return new Int2(a.x-b.x, a.y-b.y); + } + + public static bool operator == (Int2 a, Int2 b) { + return a.x == b.x && a.y == b.y; + } + + public static bool operator != (Int2 a, Int2 b) { + return a.x != b.x || a.y != b.y; + } + + /// Dot product of the two coordinates + public static long DotLong (Int2 a, Int2 b) { + return (long)a.x*(long)b.x + (long)a.y*(long)b.y; + } + + public override bool Equals (System.Object o) { + if (o == null) return false; + var rhs = (Int2)o; + + return x == rhs.x && y == rhs.y; + } + + #region IEquatable implementation + + public bool Equals (Int2 other) { + return x == other.x && y == other.y; + } + + #endregion + + public override int GetHashCode () { + return x*49157+y*98317; + } + + public static Int2 Min (Int2 a, Int2 b) { + return new Int2(System.Math.Min(a.x, b.x), System.Math.Min(a.y, b.y)); + } + + public static Int2 Max (Int2 a, Int2 b) { + return new Int2(System.Math.Max(a.x, b.x), System.Math.Max(a.y, b.y)); + } + + public static Int2 FromInt3XZ (Int3 o) { + return new Int2(o.x, o.z); + } + + public static Int3 ToInt3XZ (Int2 o) { + return new Int3(o.x, 0, o.y); + } + + public override string ToString () { + return "("+x+", " +y+")"; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Int3.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Int3.cs.meta new file mode 100644 index 0000000..99f9118 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/Int3.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5826dd4a1809b448291582cd06deadc1 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ListPool.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ListPool.cs new file mode 100644 index 0000000..7646f70 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ListPool.cs @@ -0,0 +1,211 @@ +#if !UNITY_EDITOR +// Extra optimizations when not running in the editor, but less error checking +#define ASTAR_OPTIMIZE_POOLING +#endif + +using System; +using System.Collections.Generic; + +namespace Pathfinding.Util { + /// + /// Lightweight List Pool. + /// Handy class for pooling lists of type T. + /// + /// Usage: + /// - Claim a new list using List foo = ListPool.Claim (); + /// - Use it and do stuff with it + /// - Release it with ListPool.Release (foo); + /// + /// You do not need to clear the list before releasing it. + /// After you have released a list, you should never use it again, if you do use it, you will + /// mess things up quite badly in the worst case. + /// + /// \since Version 3.2 + /// See: Pathfinding.Util.StackPool + /// + public static class ListPool { + /// Internal pool + static readonly List > pool = new List >(); + +#if !ASTAR_NO_POOLING + static readonly List > largePool = new List >(); + static readonly HashSet > inPool = new HashSet >(); +#endif + + /// + /// When requesting a list with a specified capacity, search max this many lists in the pool before giving up. + /// Must be greater or equal to one. + /// + const int MaxCapacitySearchLength = 8; + const int LargeThreshold = 5000; + const int MaxLargePoolSize = 8; + + /// + /// Claim a list. + /// Returns a pooled list if any are in the pool. + /// Otherwise it creates a new one. + /// After usage, this list should be released using the Release function (though not strictly necessary). + /// + public static List Claim () { +#if ASTAR_NO_POOLING + return new List(); +#else + lock (pool) { + if (pool.Count > 0) { + List ls = pool[pool.Count-1]; + pool.RemoveAt(pool.Count-1); + inPool.Remove(ls); + return ls; + } + + return new List(); + } +#endif + } + + static int FindCandidate (List > pool, int capacity) { + // Loop through the last MaxCapacitySearchLength items + // and check if any item has a capacity greater or equal to the one that + // is desired. If so return it. + // Otherwise take the largest one or if there are no lists in the pool + // then allocate a new one with the desired capacity + List list = null; + int listIndex = -1; + + for (int i = 0; i < pool.Count && i < MaxCapacitySearchLength; i++) { + // ith last item + var candidate = pool[pool.Count-1-i]; + + // Find the largest list that is not too large (arbitrary decision to try to prevent some memory bloat if the list was not just a temporary list). + if ((list == null || candidate.Capacity > list.Capacity) && candidate.Capacity < capacity*16) { + list = candidate; + listIndex = pool.Count-1-i; + + if (list.Capacity >= capacity) { + return listIndex; + } + } + } + + return listIndex; + } + + /// + /// Claim a list with minimum capacity + /// Returns a pooled list if any are in the pool. + /// Otherwise it creates a new one. + /// After usage, this list should be released using the Release function (though not strictly necessary). + /// A subset of the pool will be searched for a list with a high enough capacity and one will be returned + /// if possible, otherwise the list with the largest capacity found will be returned. + /// + public static List Claim (int capacity) { +#if ASTAR_NO_POOLING + return new List(capacity); +#else + lock (pool) { + var currentPool = pool; + var listIndex = FindCandidate(pool, capacity); + + if (capacity > LargeThreshold) { + var largeListIndex = FindCandidate(largePool, capacity); + if (largeListIndex != -1) { + currentPool = largePool; + listIndex = largeListIndex; + } + } + + if (listIndex == -1) { + return new List(capacity); + } else { + var list = currentPool[listIndex]; + // Swap current item and last item to enable a more efficient removal + inPool.Remove(list); + currentPool[listIndex] = currentPool[currentPool.Count-1]; + currentPool.RemoveAt(currentPool.Count-1); + return list; + } + } +#endif + } + + /// + /// Makes sure the pool contains at least count pooled items with capacity size. + /// This is good if you want to do all allocations at start. + /// + public static void Warmup (int count, int size) { + lock (pool) { + var tmp = new List[count]; + for (int i = 0; i < count; i++) tmp[i] = Claim(size); + for (int i = 0; i < count; i++) Release(tmp[i]); + } + } + + + /// + /// Releases a list and sets the variable to null. + /// After the list has been released it should not be used anymore. + /// + /// \throws System.InvalidOperationException + /// Releasing a list when it has already been released will cause an exception to be thrown. + /// + /// See: + /// + public static void Release (ref List list) { + Release(list); + list = null; + } + + /// + /// Releases a list. + /// After the list has been released it should not be used anymore. + /// + /// \throws System.InvalidOperationException + /// Releasing a list when it has already been released will cause an exception to be thrown. + /// + /// See: + /// + public static void Release (List list) { +#if !ASTAR_NO_POOLING + list.ClearFast(); + + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING + if (!inPool.Add(list)) { + throw new InvalidOperationException("You are trying to pool a list twice. Please make sure that you only pool it once."); + } +#endif + if (list.Capacity > LargeThreshold) { + largePool.Add(list); + + // Remove the list which was used the longest time ago from the pool if it + // exceeds the maximum size as it probably just contributes to memory bloat + if (largePool.Count > MaxLargePoolSize) { + largePool.RemoveAt(0); + } + } else { + pool.Add(list); + } + } +#endif + } + + /// + /// Clears the pool for lists of this type. + /// This is an O(n) operation, where n is the number of pooled lists. + /// + public static void Clear () { + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING && !ASTAR_NO_POOLING + inPool.Clear(); +#endif + pool.Clear(); + } + } + + /// Number of lists of this type in the pool + public static int GetSize () { + // No lock required since int writes are atomic + return pool.Count; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ListPool.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ListPool.cs.meta new file mode 100644 index 0000000..92e0b9c --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ListPool.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2b76b7593907b44d9a6ef1b186fee0a7 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/MovementUtilities.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/MovementUtilities.cs new file mode 100644 index 0000000..0bdd9b3 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/MovementUtilities.cs @@ -0,0 +1,174 @@ +using UnityEngine; +using System.Collections; + +namespace Pathfinding.Util { + public static class MovementUtilities { + /// + /// Clamps the velocity to the max speed and optionally the forwards direction. + /// + /// Note that all vectors are 2D vectors, not 3D vectors. + /// + /// Returns: The clamped velocity in world units per second. + /// + /// Desired velocity of the character. In world units per second. + /// Max speed of the character. In world units per second. + /// Value between 0 and 1 which determines how much slower the character should move than normal. + /// Normally 1 but should go to 0 when the character approaches the end of the path. + /// Prevent the velocity from being too far away from the forward direction of the character + /// and slow the character down if the desired velocity is not in the same direction as the forward vector. + /// Forward direction of the character. Used together with the slowWhenNotFacingTarget parameter. + public static Vector2 ClampVelocity (Vector2 velocity, float maxSpeed, float slowdownFactor, bool slowWhenNotFacingTarget, Vector2 forward) { + // Max speed to use for this frame + var currentMaxSpeed = maxSpeed * slowdownFactor; + + // Check if the agent should slow down in case it is not facing the direction it wants to move in + if (slowWhenNotFacingTarget && (forward.x != 0 || forward.y != 0)) { + float currentSpeed; + var normalizedVelocity = VectorMath.Normalize(velocity, out currentSpeed); + float dot = Vector2.Dot(normalizedVelocity, forward); + + // Lower the speed when the character's forward direction is not pointing towards the desired velocity + // 1 when velocity is in the same direction as forward + // 0.2 when they point in the opposite directions + float directionSpeedFactor = Mathf.Clamp(dot+0.707f, 0.2f, 1.0f); + currentMaxSpeed *= directionSpeedFactor; + currentSpeed = Mathf.Min(currentSpeed, currentMaxSpeed); + + // Angle between the forwards direction of the character and our desired velocity + float angle = Mathf.Acos(Mathf.Clamp(dot, -1, 1)); + + // Clamp the angle to 20 degrees + // We cannot keep the velocity exactly in the forwards direction of the character + // because we use the rotation to determine in which direction to rotate and if + // the velocity would always be in the forwards direction of the character then + // the character would never rotate. + // Allow larger angles when near the end of the path to prevent oscillations. + angle = Mathf.Min(angle, (20f + 180f*(1 - slowdownFactor*slowdownFactor))*Mathf.Deg2Rad); + + float sin = Mathf.Sin(angle); + float cos = Mathf.Cos(angle); + + // Determine if we should rotate clockwise or counter-clockwise to move towards the current velocity + sin *= Mathf.Sign(normalizedVelocity.x*forward.y - normalizedVelocity.y*forward.x); + // Rotate the #forward vector by #angle radians + // The rotation is done using an inlined rotation matrix. + // See https://en.wikipedia.org/wiki/Rotation_matrix + return new Vector2(forward.x*cos + forward.y*sin, forward.y*cos - forward.x*sin) * currentSpeed; + } else { + return Vector2.ClampMagnitude(velocity, currentMaxSpeed); + } + } + + /// Calculate an acceleration to move deltaPosition units and get there with approximately a velocity of targetVelocity + public static Vector2 CalculateAccelerationToReachPoint (Vector2 deltaPosition, Vector2 targetVelocity, Vector2 currentVelocity, float forwardsAcceleration, float rotationSpeed, float maxSpeed, Vector2 forwardsVector) { + // Guard against div by zero + if (forwardsAcceleration <= 0) return Vector2.zero; + + float currentSpeed = currentVelocity.magnitude; + + // Convert rotation speed to an acceleration + // See https://en.wikipedia.org/wiki/Centripetal_force + var sidewaysAcceleration = currentSpeed * rotationSpeed * Mathf.Deg2Rad; + + // To avoid weird behaviour when the rotation speed is very low we allow the agent to accelerate sideways without rotating much + // if the rotation speed is very small. Also guards against division by zero. + sidewaysAcceleration = Mathf.Max(sidewaysAcceleration, forwardsAcceleration); + + // Transform coordinates to local space where +X is the forwards direction + // This is essentially equivalent to Transform.InverseTransformDirection. + deltaPosition = VectorMath.ComplexMultiplyConjugate(deltaPosition, forwardsVector); + targetVelocity = VectorMath.ComplexMultiplyConjugate(targetVelocity, forwardsVector); + currentVelocity = VectorMath.ComplexMultiplyConjugate(currentVelocity, forwardsVector); + float ellipseSqrFactorX = 1 / (forwardsAcceleration*forwardsAcceleration); + float ellipseSqrFactorY = 1 / (sidewaysAcceleration*sidewaysAcceleration); + + // If the target velocity is zero we can use a more fancy approach + // and calculate a nicer path. + // In particular, this is the case at the end of the path. + if (targetVelocity == Vector2.zero) { + // Run a binary search over the time to get to the target point. + float mn = 0.01f; + float mx = 10; + while (mx - mn > 0.01f) { + var time = (mx + mn) * 0.5f; + + // Given that we want to move deltaPosition units from out current position, that our current velocity is given + // and that when we reach the target we want our velocity to be zero. Also assume that our acceleration will + // vary linearly during the slowdown. Then we can calculate what our acceleration should be during this frame. + + //{ t = time + //{ deltaPosition = vt + at^2/2 + qt^3/6 + //{ 0 = v + at + qt^2/2 + //{ solve for a + // a = acceleration vector + // q = derivative of the acceleration vector + var a = (6*deltaPosition - 4*time*currentVelocity)/(time*time); + var q = 6*(time*currentVelocity - 2*deltaPosition)/(time*time*time); + + // Make sure the acceleration is not greater than our maximum allowed acceleration. + // If it is we increase the time we want to use to get to the target + // and if it is not, we decrease the time to get there faster. + // Since the acceleration is described by acceleration = a + q*t + // we only need to check at t=0 and t=time. + // Note that the acceleration limit is described by an ellipse, not a circle. + var nextA = a + q*time; + if (a.x*a.x*ellipseSqrFactorX + a.y*a.y*ellipseSqrFactorY > 1.0f || nextA.x*nextA.x*ellipseSqrFactorX + nextA.y*nextA.y*ellipseSqrFactorY > 1.0f) { + mn = time; + } else { + mx = time; + } + } + + var finalAcceleration = (6*deltaPosition - 4*mx*currentVelocity)/(mx*mx); + + // Boosting + { + // The trajectory calculated above has a tendency to use very wide arcs + // and that does unfortunately not look particularly good in some cases. + // Here we amplify the component of the acceleration that is perpendicular + // to our current velocity. This will make the agent turn towards the + // target quicker. + // How much amplification to use. Value is unitless. + const float Boost = 1; + finalAcceleration.y *= 1 + Boost; + + // Clamp the velocity to the maximum acceleration. + // Note that the maximum acceleration constraint is shaped like an ellipse, not like a circle. + float ellipseMagnitude = finalAcceleration.x*finalAcceleration.x*ellipseSqrFactorX + finalAcceleration.y*finalAcceleration.y*ellipseSqrFactorY; + if (ellipseMagnitude > 1.0f) finalAcceleration /= Mathf.Sqrt(ellipseMagnitude); + } + + return VectorMath.ComplexMultiply(finalAcceleration, forwardsVector); + } else { + // Here we try to move towards the next waypoint which has been modified slightly using our + // desired velocity at that point so that the agent will more smoothly round the corner. + + // How much to strive for making sure we reach the target point with the target velocity. Unitless. + const float TargetVelocityWeight = 0.5f; + + // Limit to how much to care about the target velocity. Value is in seconds. + // This prevents the character from moving away from the path too much when the target point is far away + const float TargetVelocityWeightLimit = 1.5f; + float targetSpeed; + var normalizedTargetVelocity = VectorMath.Normalize(targetVelocity, out targetSpeed); + + var distance = deltaPosition.magnitude; + var targetPoint = deltaPosition - normalizedTargetVelocity * System.Math.Min(TargetVelocityWeight * distance * targetSpeed / (currentSpeed + targetSpeed), maxSpeed*TargetVelocityWeightLimit); + + // How quickly the agent will try to reach the velocity that we want it to have. + // We need this to prevent oscillations and jitter which is what happens if + // we let the constant go towards zero. Value is in seconds. + const float TimeToReachDesiredVelocity = 0.1f; + // TODO: Clamp to ellipse using more accurate acceleration (use rotation speed as well) + var finalAcceleration = (targetPoint.normalized*maxSpeed - currentVelocity) * (1f/TimeToReachDesiredVelocity); + + // Clamp the velocity to the maximum acceleration. + // Note that the maximum acceleration constraint is shaped like an ellipse, not like a circle. + float ellipseMagnitude = finalAcceleration.x*finalAcceleration.x*ellipseSqrFactorX + finalAcceleration.y*finalAcceleration.y*ellipseSqrFactorY; + if (ellipseMagnitude > 1.0f) finalAcceleration /= Mathf.Sqrt(ellipseMagnitude); + + return VectorMath.ComplexMultiply(finalAcceleration, forwardsVector); + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/MovementUtilities.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/MovementUtilities.cs.meta new file mode 100644 index 0000000..8cc4625 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/MovementUtilities.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0a6cffd9895f94907aa43f18b0904587 +timeCreated: 1490097740 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink.cs new file mode 100644 index 0000000..379b83f --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink.cs @@ -0,0 +1,157 @@ +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace Pathfinding { + using Pathfinding.Util; + + /// + /// Connects two nodes with a direct connection. + /// It is not possible to detect this link when following a path (which may be good or bad), for that you can use NodeLink2. + /// + /// [Open online documentation to see images] + /// + /// See: editing-graphs (view in online documentation for working links) + /// + [AddComponentMenu("Pathfinding/Link")] + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_node_link.php")] + public class NodeLink : GraphModifier { + /// End position of the link + public Transform end; + + /// + /// The connection will be this times harder/slower to traverse. + /// Note that values lower than one will not always make the pathfinder choose this path instead of another path even though this one should + /// lead to a lower total cost unless you also adjust the Heuristic Scale in A* Inspector -> Settings -> Pathfinding or disable the heuristic altogether. + /// + public float costFactor = 1.0f; + + /// Make a one-way connection + public bool oneWay = false; + + /// Delete existing connection instead of adding one + public bool deleteConnection = false; + + public Transform Start { + get { return transform; } + } + + public Transform End { + get { return end; } + } + + public override void OnPostScan () { + if (AstarPath.active.isScanning) { + InternalOnPostScan(); + } else { + AstarPath.active.AddWorkItem(new AstarWorkItem(force => { + InternalOnPostScan(); + return true; + })); + } + } + + public void InternalOnPostScan () { + Apply(); + } + + public override void OnGraphsPostUpdate () { + if (!AstarPath.active.isScanning) { + AstarPath.active.AddWorkItem(new AstarWorkItem(force => { + InternalOnPostScan(); + return true; + })); + } + } + + public virtual void Apply () { + if (Start == null || End == null || AstarPath.active == null) return; + + GraphNode startNode = AstarPath.active.GetNearest(Start.position).node; + GraphNode endNode = AstarPath.active.GetNearest(End.position).node; + + if (startNode == null || endNode == null) return; + + + if (deleteConnection) { + startNode.RemoveConnection(endNode); + if (!oneWay) + endNode.RemoveConnection(startNode); + } else { + uint cost = (uint)System.Math.Round((startNode.position-endNode.position).costMagnitude*costFactor); + + startNode.AddConnection(endNode, cost); + if (!oneWay) + endNode.AddConnection(startNode, cost); + } + } + + public void OnDrawGizmos () { + if (Start == null || End == null) return; + + Draw.Gizmos.Bezier(Start.position, End.position, deleteConnection ? Color.red : Color.green); + } + +#if UNITY_EDITOR + [UnityEditor.MenuItem("Edit/Pathfinding/Link Pair %&l")] + public static void LinkObjects () { + Transform[] tfs = Selection.transforms; + if (tfs.Length == 2) { + LinkObjects(tfs[0], tfs[1], false); + } + SceneView.RepaintAll(); + } + + [UnityEditor.MenuItem("Edit/Pathfinding/Unlink Pair %&u")] + public static void UnlinkObjects () { + Transform[] tfs = Selection.transforms; + if (tfs.Length == 2) { + LinkObjects(tfs[0], tfs[1], true); + } + SceneView.RepaintAll(); + } + + [UnityEditor.MenuItem("Edit/Pathfinding/Delete Links on Selected %&b")] + public static void DeleteLinks () { + Transform[] tfs = Selection.transforms; + for (int i = 0; i < tfs.Length; i++) { + NodeLink[] conns = tfs[i].GetComponents(); + for (int j = 0; j < conns.Length; j++) DestroyImmediate(conns[j]); + } + SceneView.RepaintAll(); + } + + public static void LinkObjects (Transform a, Transform b, bool removeConnection) { + NodeLink connecting = null; + + NodeLink[] conns = a.GetComponents(); + for (int i = 0; i < conns.Length; i++) { + if (conns[i].end == b) { + connecting = conns[i]; + break; + } + } + + conns = b.GetComponents(); + for (int i = 0; i < conns.Length; i++) { + if (conns[i].end == a) { + connecting = conns[i]; + break; + } + } + + if (removeConnection) { + if (connecting != null) DestroyImmediate(connecting); + } else { + if (connecting == null) { + connecting = a.gameObject.AddComponent(); + connecting.end = b; + } else { + connecting.deleteConnection = !connecting.deleteConnection; + } + } + } +#endif + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink.cs.meta new file mode 100644 index 0000000..b4282dc --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ca327ce4e754a4597a70fb963758f8bd +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink2.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink2.cs new file mode 100644 index 0000000..f33e8bd --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink2.cs @@ -0,0 +1,308 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Pathfinding { + using Pathfinding.Util; + + /// + /// Connects two nodes via two intermediate point nodes. + /// In contrast to the NodeLink component, this link type will not connect the nodes directly + /// instead it will create two point nodes at the start and end position of this link and connect + /// through those nodes. + /// + /// If the closest node to this object is called A and the closest node to the end transform is called + /// D, then it will create one point node at this object's position (call it B) and one point node at + /// the position of the end transform (call it C), it will then connect A to B, B to C and C to D. + /// + /// This link type is possible to detect while following since it has these special point nodes in the middle. + /// The link corresponding to one of those intermediate nodes can be retrieved using the method + /// which can be of great use if you want to, for example, play a link specific animation when reaching the link. + /// + /// See: The example scene RecastExample2 contains a few links which you can take a look at to see how they are used. + /// + [AddComponentMenu("Pathfinding/Link2")] + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_node_link2.php")] + public class NodeLink2 : GraphModifier { + protected static Dictionary reference = new Dictionary(); + public static NodeLink2 GetNodeLink (GraphNode node) { + NodeLink2 v; + + reference.TryGetValue(node, out v); + return v; + } + + /// End position of the link + public Transform end; + + /// + /// The connection will be this times harder/slower to traverse. + /// Note that values lower than 1 will not always make the pathfinder choose this path instead of another path even though this one should + /// lead to a lower total cost unless you also adjust the Heuristic Scale in A* Inspector -> Settings -> Pathfinding or disable the heuristic altogether. + /// + public float costFactor = 1.0f; + + /// Make a one-way connection + public bool oneWay = false; + + public Transform StartTransform { + get { return transform; } + } + + public Transform EndTransform { + get { return end; } + } + + public PointNode startNode { get; private set; } + public PointNode endNode { get; private set; } + GraphNode connectedNode1, connectedNode2; + Vector3 clamped1, clamped2; + bool postScanCalled = false; + + [System.Obsolete("Use startNode instead (lowercase s)")] + public GraphNode StartNode { + get { return startNode; } + } + + [System.Obsolete("Use endNode instead (lowercase e)")] + public GraphNode EndNode { + get { return endNode; } + } + + public override void OnPostScan () { + InternalOnPostScan(); + } + + public void InternalOnPostScan () { + if (EndTransform == null || StartTransform == null) return; + +#if ASTAR_NO_POINT_GRAPH + throw new System.Exception("Point graph is not included. Check your A* optimization settings."); +#else + if (AstarPath.active.data.pointGraph == null) { + var graph = AstarPath.active.data.AddGraph(typeof(PointGraph)) as PointGraph; + graph.name = "PointGraph (used for node links)"; + } + + if (startNode != null && startNode.Destroyed) { + reference.Remove(startNode); + startNode = null; + } + + if (endNode != null && endNode.Destroyed) { + reference.Remove(endNode); + endNode = null; + } + + // Create new nodes on the point graph + if (startNode == null) startNode = AstarPath.active.data.pointGraph.AddNode((Int3)StartTransform.position); + if (endNode == null) endNode = AstarPath.active.data.pointGraph.AddNode((Int3)EndTransform.position); + + connectedNode1 = null; + connectedNode2 = null; + + if (startNode == null || endNode == null) { + startNode = null; + endNode = null; + return; + } + + postScanCalled = true; + reference[startNode] = this; + reference[endNode] = this; + Apply(true); +#endif + } + + public override void OnGraphsPostUpdate () { + // Don't bother running it now since OnPostScan will be called later anyway + if (AstarPath.active.isScanning) + return; + + if (connectedNode1 != null && connectedNode1.Destroyed) { + connectedNode1 = null; + } + if (connectedNode2 != null && connectedNode2.Destroyed) { + connectedNode2 = null; + } + + if (!postScanCalled) { + OnPostScan(); + } else { + Apply(false); + } + } + + protected override void OnEnable () { + base.OnEnable(); + +#if !ASTAR_NO_POINT_GRAPH + if (Application.isPlaying && AstarPath.active != null && AstarPath.active.data != null && AstarPath.active.data.pointGraph != null && !AstarPath.active.isScanning) { + // Call OnGraphsPostUpdate as soon as possible when it is safe to update the graphs + AstarPath.active.AddWorkItem(OnGraphsPostUpdate); + } +#endif + } + + protected override void OnDisable () { + base.OnDisable(); + + postScanCalled = false; + + if (startNode != null) reference.Remove(startNode); + if (endNode != null) reference.Remove(endNode); + + if (startNode != null && endNode != null) { + startNode.RemoveConnection(endNode); + endNode.RemoveConnection(startNode); + + if (connectedNode1 != null && connectedNode2 != null) { + startNode.RemoveConnection(connectedNode1); + connectedNode1.RemoveConnection(startNode); + + endNode.RemoveConnection(connectedNode2); + connectedNode2.RemoveConnection(endNode); + } + } + } + + void RemoveConnections (GraphNode node) { + //TODO, might be better to replace connection + node.ClearConnections(true); + } + + [ContextMenu("Recalculate neighbours")] + void ContextApplyForce () { + if (Application.isPlaying) { + Apply(true); + } + } + + public void Apply (bool forceNewCheck) { + //TODO + //This function assumes that connections from the n1,n2 nodes never need to be removed in the future (e.g because the nodes move or something) + NNConstraint nn = NNConstraint.None; + int graph = (int)startNode.GraphIndex; + + //Search all graphs but the one which start and end nodes are on + nn.graphMask = ~(1 << graph); + + startNode.SetPosition((Int3)StartTransform.position); + endNode.SetPosition((Int3)EndTransform.position); + + RemoveConnections(startNode); + RemoveConnections(endNode); + + uint cost = (uint)Mathf.RoundToInt(((Int3)(StartTransform.position-EndTransform.position)).costMagnitude*costFactor); + startNode.AddConnection(endNode, cost); + endNode.AddConnection(startNode, cost); + + if (connectedNode1 == null || forceNewCheck) { + var info = AstarPath.active.GetNearest(StartTransform.position, nn); + connectedNode1 = info.node; + clamped1 = info.position; + } + + if (connectedNode2 == null || forceNewCheck) { + var info = AstarPath.active.GetNearest(EndTransform.position, nn); + connectedNode2 = info.node; + clamped2 = info.position; + } + + if (connectedNode2 == null || connectedNode1 == null) return; + + //Add connections between nodes, or replace old connections if existing + connectedNode1.AddConnection(startNode, (uint)Mathf.RoundToInt(((Int3)(clamped1 - StartTransform.position)).costMagnitude*costFactor)); + if (!oneWay) connectedNode2.AddConnection(endNode, (uint)Mathf.RoundToInt(((Int3)(clamped2 - EndTransform.position)).costMagnitude*costFactor)); + + if (!oneWay) startNode.AddConnection(connectedNode1, (uint)Mathf.RoundToInt(((Int3)(clamped1 - StartTransform.position)).costMagnitude*costFactor)); + endNode.AddConnection(connectedNode2, (uint)Mathf.RoundToInt(((Int3)(clamped2 - EndTransform.position)).costMagnitude*costFactor)); + } + + private readonly static Color GizmosColor = new Color(206.0f/255.0f, 136.0f/255.0f, 48.0f/255.0f, 0.5f); + private readonly static Color GizmosColorSelected = new Color(235.0f/255.0f, 123.0f/255.0f, 32.0f/255.0f, 1.0f); + + public virtual void OnDrawGizmosSelected () { + OnDrawGizmos(true); + } + + public void OnDrawGizmos () { + OnDrawGizmos(false); + } + + public void OnDrawGizmos (bool selected) { + Color color = selected ? GizmosColorSelected : GizmosColor; + + if (StartTransform != null) { + Draw.Gizmos.CircleXZ(StartTransform.position, 0.4f, color); + } + if (EndTransform != null) { + Draw.Gizmos.CircleXZ(EndTransform.position, 0.4f, color); + } + + if (StartTransform != null && EndTransform != null) { + Draw.Gizmos.Bezier(StartTransform.position, EndTransform.position, color); + if (selected) { + Vector3 cross = Vector3.Cross(Vector3.up, (EndTransform.position-StartTransform.position)).normalized; + Draw.Gizmos.Bezier(StartTransform.position+cross*0.1f, EndTransform.position+cross*0.1f, color); + Draw.Gizmos.Bezier(StartTransform.position-cross*0.1f, EndTransform.position-cross*0.1f, color); + } + } + } + + internal static void SerializeReferences (Pathfinding.Serialization.GraphSerializationContext ctx) { + var links = GetModifiersOfType(); + + ctx.writer.Write(links.Count); + foreach (var link in links) { + ctx.writer.Write(link.uniqueID); + ctx.SerializeNodeReference(link.startNode); + ctx.SerializeNodeReference(link.endNode); + ctx.SerializeNodeReference(link.connectedNode1); + ctx.SerializeNodeReference(link.connectedNode2); + ctx.SerializeVector3(link.clamped1); + ctx.SerializeVector3(link.clamped2); + ctx.writer.Write(link.postScanCalled); + } + } + + internal static void DeserializeReferences (Pathfinding.Serialization.GraphSerializationContext ctx) { + int count = ctx.reader.ReadInt32(); + + for (int i = 0; i < count; i++) { + var linkID = ctx.reader.ReadUInt64(); + var startNode = ctx.DeserializeNodeReference(); + var endNode = ctx.DeserializeNodeReference(); + var connectedNode1 = ctx.DeserializeNodeReference(); + var connectedNode2 = ctx.DeserializeNodeReference(); + var clamped1 = ctx.DeserializeVector3(); + var clamped2 = ctx.DeserializeVector3(); + var postScanCalled = ctx.reader.ReadBoolean(); + + GraphModifier link; + if (usedIDs.TryGetValue(linkID, out link)) { + var link2 = link as NodeLink2; + if (link2 != null) { + if (startNode != null) reference[startNode] = link2; + if (endNode != null) reference[endNode] = link2; + + // If any nodes happened to be registered right now + if (link2.startNode != null) reference.Remove(link2.startNode); + if (link2.endNode != null) reference.Remove(link2.endNode); + + link2.startNode = startNode as PointNode; + link2.endNode = endNode as PointNode; + link2.connectedNode1 = connectedNode1; + link2.connectedNode2 = connectedNode2; + link2.postScanCalled = postScanCalled; + link2.clamped1 = clamped1; + link2.clamped2 = clamped2; + } else { + throw new System.Exception("Tried to deserialize a NodeLink2 reference, but the link was not of the correct type or it has been destroyed.\nIf a NodeLink2 is included in serialized graph data, the same NodeLink2 component must be present in the scene when loading the graph data."); + } + } else { + throw new System.Exception("Tried to deserialize a NodeLink2 reference, but the link could not be found in the scene.\nIf a NodeLink2 is included in serialized graph data, the same NodeLink2 component must be present in the scene when loading the graph data."); + } + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink2.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink2.cs.meta new file mode 100644 index 0000000..8bdc69c --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink2.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bd2cfff5dfa8c4244aa00fea9675adb2 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink3.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink3.cs new file mode 100644 index 0000000..2dd34f9 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink3.cs @@ -0,0 +1,313 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Pathfinding { + using Pathfinding.Util; + + public class NodeLink3Node : PointNode { + public NodeLink3 link; + public Vector3 portalA; + public Vector3 portalB; + + public NodeLink3Node (AstarPath active) : base(active) {} + + public override bool GetPortal (GraphNode other, List left, List right, bool backwards) { + if (this.connections.Length < 2) return false; + + if (this.connections.Length != 2) throw new System.Exception("Invalid NodeLink3Node. Expected 2 connections, found " + this.connections.Length); + + if (left != null) { + left.Add(portalA); + right.Add(portalB); + } + + return true; + } + + public GraphNode GetOther (GraphNode a) { + if (this.connections.Length < 2) return null; + if (this.connections.Length != 2) throw new System.Exception("Invalid NodeLink3Node. Expected 2 connections, found " + this.connections.Length); + + return a == connections[0].node ? (connections[1].node as NodeLink3Node).GetOtherInternal(this) : (connections[0].node as NodeLink3Node).GetOtherInternal(this); + } + + GraphNode GetOtherInternal (GraphNode a) { + if (this.connections.Length < 2) return null; + return a == connections[0].node ? connections[1].node : connections[0].node; + } + } + + /// + /// Connects two TriangleMeshNodes (recast/navmesh graphs) as if they had shared an edge. + /// Note: Usually you do not want to use this type of link, you want to use NodeLink2 or NodeLink (sorry for the not so descriptive names). + /// + [AddComponentMenu("Pathfinding/Link3")] + [HelpURL("http://arongranberg.com/astar/docs/class_pathfinding_1_1_node_link3.php")] + public class NodeLink3 : GraphModifier { + protected static Dictionary reference = new Dictionary(); + public static NodeLink3 GetNodeLink (GraphNode node) { + NodeLink3 v; + + reference.TryGetValue(node, out v); + return v; + } + + /// End position of the link + public Transform end; + + /// + /// The connection will be this times harder/slower to traverse. + /// Note that values lower than one will not always make the pathfinder choose this path instead of another path even though this one should + /// lead to a lower total cost unless you also adjust the Heuristic Scale in A* Inspector -> Settings -> Pathfinding or disable the heuristic altogether. + /// + public float costFactor = 1.0f; + + /// Make a one-way connection + public bool oneWay = false; + + public Transform StartTransform { + get { return transform; } + } + + public Transform EndTransform { + get { return end; } + } + + NodeLink3Node startNode; + NodeLink3Node endNode; + MeshNode connectedNode1, connectedNode2; + Vector3 clamped1, clamped2; + bool postScanCalled = false; + + public GraphNode StartNode { + get { return startNode; } + } + + public GraphNode EndNode { + get { return endNode; } + } + + public override void OnPostScan () { + if (AstarPath.active.isScanning) { + InternalOnPostScan(); + } else { + AstarPath.active.AddWorkItem(new AstarWorkItem(force => { + InternalOnPostScan(); + return true; + })); + } + } + + public void InternalOnPostScan () { +#if !ASTAR_NO_POINT_GRAPH + if (AstarPath.active.data.pointGraph == null) { + AstarPath.active.data.AddGraph(typeof(PointGraph)); + } + + //Get nearest nodes from the first point graph, assuming both start and end transforms are nodes + startNode = AstarPath.active.data.pointGraph.AddNode(new NodeLink3Node(AstarPath.active), (Int3)StartTransform.position); + startNode.link = this; + endNode = AstarPath.active.data.pointGraph.AddNode(new NodeLink3Node(AstarPath.active), (Int3)EndTransform.position); + endNode.link = this; +#else + throw new System.Exception("Point graphs are not included. Check your A* Optimization settings."); +#endif + connectedNode1 = null; + connectedNode2 = null; + + if (startNode == null || endNode == null) { + startNode = null; + endNode = null; + return; + } + + postScanCalled = true; + reference[startNode] = this; + reference[endNode] = this; + Apply(true); + } + + public override void OnGraphsPostUpdate () { + if (!AstarPath.active.isScanning) { + if (connectedNode1 != null && connectedNode1.Destroyed) { + connectedNode1 = null; + } + if (connectedNode2 != null && connectedNode2.Destroyed) { + connectedNode2 = null; + } + + if (!postScanCalled) { + OnPostScan(); + } else { + //OnPostScan will also call this method + Apply(false); + } + } + } + + protected override void OnEnable () { + base.OnEnable(); + +#if !ASTAR_NO_POINT_GRAPH + if (Application.isPlaying && AstarPath.active != null && AstarPath.active.data != null && AstarPath.active.data.pointGraph != null) { + OnGraphsPostUpdate(); + } +#endif + } + + protected override void OnDisable () { + base.OnDisable(); + + postScanCalled = false; + + if (startNode != null) reference.Remove(startNode); + if (endNode != null) reference.Remove(endNode); + + if (startNode != null && endNode != null) { + startNode.RemoveConnection(endNode); + endNode.RemoveConnection(startNode); + + if (connectedNode1 != null && connectedNode2 != null) { + startNode.RemoveConnection(connectedNode1); + connectedNode1.RemoveConnection(startNode); + + endNode.RemoveConnection(connectedNode2); + connectedNode2.RemoveConnection(endNode); + } + } + } + + void RemoveConnections (GraphNode node) { + //TODO, might be better to replace connection + node.ClearConnections(true); + } + + [ContextMenu("Recalculate neighbours")] + void ContextApplyForce () { + if (Application.isPlaying) { + Apply(true); + } + } + + public void Apply (bool forceNewCheck) { + //TODO + //This function assumes that connections from the n1,n2 nodes never need to be removed in the future (e.g because the nodes move or something) + NNConstraint nn = NNConstraint.None; + + nn.distanceXZ = true; + int graph = (int)startNode.GraphIndex; + + //Search all graphs but the one which start and end nodes are on + nn.graphMask = ~(1 << graph); + + bool same = true; + + { + var info = AstarPath.active.GetNearest(StartTransform.position, nn); + same &= info.node == connectedNode1 && info.node != null; + connectedNode1 = info.node as MeshNode; + clamped1 = info.position; + if (connectedNode1 != null) Debug.DrawRay((Vector3)connectedNode1.position, Vector3.up*5, Color.red); + } + + { + var info = AstarPath.active.GetNearest(EndTransform.position, nn); + same &= info.node == connectedNode2 && info.node != null; + connectedNode2 = info.node as MeshNode; + clamped2 = info.position; + if (connectedNode2 != null) Debug.DrawRay((Vector3)connectedNode2.position, Vector3.up*5, Color.cyan); + } + + if (connectedNode2 == null || connectedNode1 == null) return; + + startNode.SetPosition((Int3)StartTransform.position); + endNode.SetPosition((Int3)EndTransform.position); + + if (same && !forceNewCheck) return; + + RemoveConnections(startNode); + RemoveConnections(endNode); + + uint cost = (uint)Mathf.RoundToInt(((Int3)(StartTransform.position-EndTransform.position)).costMagnitude*costFactor); + startNode.AddConnection(endNode, cost); + endNode.AddConnection(startNode, cost); + + Int3 dir = connectedNode2.position - connectedNode1.position; + + for (int a = 0; a < connectedNode1.GetVertexCount(); a++) { + Int3 va1 = connectedNode1.GetVertex(a); + Int3 va2 = connectedNode1.GetVertex((a+1) % connectedNode1.GetVertexCount()); + + if (Int3.DotLong((va2-va1).Normal2D(), dir) > 0) continue; + + for (int b = 0; b < connectedNode2.GetVertexCount(); b++) { + Int3 vb1 = connectedNode2.GetVertex(b); + Int3 vb2 = connectedNode2.GetVertex((b+1) % connectedNode2.GetVertexCount()); + + if (Int3.DotLong((vb2-vb1).Normal2D(), dir) < 0) continue; + + if (Int3.Angle((vb2-vb1), (va2-va1)) > (170.0/360.0f)*Mathf.PI*2) { + float t1 = 0; + float t2 = 1; + + t2 = System.Math.Min(t2, VectorMath.ClosestPointOnLineFactor(va1, va2, vb1)); + t1 = System.Math.Max(t1, VectorMath.ClosestPointOnLineFactor(va1, va2, vb2)); + + if (t2 < t1) { + Debug.LogError("Something went wrong! " + t1 + " " + t2 + " " + va1 + " " + va2 + " " + vb1 + " " + vb2+"\nTODO, how can this happen?"); + } else { + Vector3 pa = (Vector3)(va2-va1)*t1 + (Vector3)va1; + Vector3 pb = (Vector3)(va2-va1)*t2 + (Vector3)va1; + + startNode.portalA = pa; + startNode.portalB = pb; + + endNode.portalA = pb; + endNode.portalB = pa; + + //Add connections between nodes, or replace old connections if existing + connectedNode1.AddConnection(startNode, (uint)Mathf.RoundToInt(((Int3)(clamped1 - StartTransform.position)).costMagnitude*costFactor)); + connectedNode2.AddConnection(endNode, (uint)Mathf.RoundToInt(((Int3)(clamped2 - EndTransform.position)).costMagnitude*costFactor)); + + startNode.AddConnection(connectedNode1, (uint)Mathf.RoundToInt(((Int3)(clamped1 - StartTransform.position)).costMagnitude*costFactor)); + endNode.AddConnection(connectedNode2, (uint)Mathf.RoundToInt(((Int3)(clamped2 - EndTransform.position)).costMagnitude*costFactor)); + + return; + } + } + } + } + } + + private readonly static Color GizmosColor = new Color(206.0f/255.0f, 136.0f/255.0f, 48.0f/255.0f, 0.5f); + private readonly static Color GizmosColorSelected = new Color(235.0f/255.0f, 123.0f/255.0f, 32.0f/255.0f, 1.0f); + + public virtual void OnDrawGizmosSelected () { + OnDrawGizmos(true); + } + + public void OnDrawGizmos () { + OnDrawGizmos(false); + } + + public void OnDrawGizmos (bool selected) { + Color col = selected ? GizmosColorSelected : GizmosColor; + + if (StartTransform != null) { + Draw.Gizmos.CircleXZ(StartTransform.position, 0.4f, col); + } + if (EndTransform != null) { + Draw.Gizmos.CircleXZ(EndTransform.position, 0.4f, col); + } + + if (StartTransform != null && EndTransform != null) { + Draw.Gizmos.Bezier(StartTransform.position, EndTransform.position, col); + if (selected) { + Vector3 cross = Vector3.Cross(Vector3.up, (EndTransform.position-StartTransform.position)).normalized; + Draw.Gizmos.Bezier(StartTransform.position+cross*0.1f, EndTransform.position+cross*0.1f, col); + Draw.Gizmos.Bezier(StartTransform.position-cross*0.1f, EndTransform.position-cross*0.1f, col); + } + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink3.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink3.cs.meta new file mode 100644 index 0000000..d8b4f52 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/NodeLink3.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3850d0dc2bead45568e6b5bbcc011606 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ObjectPool.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ObjectPool.cs new file mode 100644 index 0000000..70acd73 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ObjectPool.cs @@ -0,0 +1,131 @@ +#if !UNITY_EDITOR +// Extra optimizations when not running in the editor, but less error checking +#define ASTAR_OPTIMIZE_POOLING +#endif + +using System; +using System.Collections.Generic; + +namespace Pathfinding.Util { + public interface IAstarPooledObject { + void OnEnterPool (); + } + + /// + /// Lightweight object Pool for IAstarPooledObject. + /// Handy class for pooling objects of type T which implements the IAstarPooledObject interface. + /// + /// Usage: + /// - Claim a new object using SomeClass foo = ObjectPool.Claim (); + /// - Use it and do stuff with it + /// - Release it with ObjectPool.Release (foo); + /// + /// After you have released a object, you should never use it again. + /// + /// \since Version 3.2 + /// Version: Since 3.7.6 this class is thread safe + /// See: Pathfinding.Util.ListPool + /// See: ObjectPoolSimple + /// + public static class ObjectPool where T : class, IAstarPooledObject, new(){ + public static T Claim () { + return ObjectPoolSimple.Claim (); + } + + public static void Release (ref T obj) { + obj.OnEnterPool(); + ObjectPoolSimple.Release (ref obj); + } + } + + /// + /// Lightweight object Pool. + /// Handy class for pooling objects of type T. + /// + /// Usage: + /// - Claim a new object using SomeClass foo = ObjectPool.Claim (); + /// - Use it and do stuff with it + /// - Release it with ObjectPool.Release (foo); + /// + /// After you have released a object, you should never use it again. + /// + /// \since Version 3.2 + /// Version: Since 3.7.6 this class is thread safe + /// See: Pathfinding.Util.ListPool + /// See: ObjectPool + /// + public static class ObjectPoolSimple where T : class, new(){ + /// Internal pool + static List pool = new List(); + +#if !ASTAR_NO_POOLING + static readonly HashSet inPool = new HashSet(); +#endif + + /// + /// Claim a object. + /// Returns a pooled object if any are in the pool. + /// Otherwise it creates a new one. + /// After usage, this object should be released using the Release function (though not strictly necessary). + /// + public static T Claim () { +#if ASTAR_NO_POOLING + return new T(); +#else + lock (pool) { + if (pool.Count > 0) { + T ls = pool[pool.Count-1]; + pool.RemoveAt(pool.Count-1); + inPool.Remove(ls); + return ls; + } else { + return new T(); + } + } +#endif + } + + /// + /// Releases an object. + /// After the object has been released it should not be used anymore. + /// The variable will be set to null to prevent silly mistakes. + /// + /// \throws System.InvalidOperationException + /// Releasing an object when it has already been released will cause an exception to be thrown. + /// However enabling ASTAR_OPTIMIZE_POOLING will prevent this check. + /// + /// See: Claim + /// + public static void Release (ref T obj) { +#if !ASTAR_NO_POOLING + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING + if (!inPool.Add(obj)) { + throw new InvalidOperationException("You are trying to pool an object twice. Please make sure that you only pool it once."); + } +#endif + pool.Add(obj); + } +#endif + obj = null; + } + + /// + /// Clears the pool for objects of this type. + /// This is an O(n) operation, where n is the number of pooled objects. + /// + public static void Clear () { + lock (pool) { +#if !ASTAR_OPTIMIZE_POOLING && !ASTAR_NO_POOLING + inPool.Clear(); +#endif + pool.Clear(); + } + } + + /// Number of objects of this type in the pool + public static int GetSize () { + return pool.Count; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ObjectPool.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ObjectPool.cs.meta new file mode 100644 index 0000000..6240b52 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ObjectPool.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 10837c5b030bd47a2a0e6e213fea0868 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathInterpolator.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathInterpolator.cs new file mode 100644 index 0000000..4eda961 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathInterpolator.cs @@ -0,0 +1,206 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Pathfinding.Util { + /// Interpolates along a sequence of points + public class PathInterpolator { + List path; + + float distanceToSegmentStart; + float currentDistance; + float currentSegmentLength = float.PositiveInfinity; + float totalDistance = float.PositiveInfinity; + + /// Current position + public virtual Vector3 position { + get { + float t = currentSegmentLength > 0.0001f ? (currentDistance - distanceToSegmentStart) / currentSegmentLength : 0f; + return Vector3.Lerp(path[segmentIndex], path[segmentIndex+1], t); + } + } + + /// Last point in the path + public Vector3 endPoint { + get { + return path[path.Count-1]; + } + } + + /// Tangent of the curve at the current position + public Vector3 tangent { + get { + return path[segmentIndex+1] - path[segmentIndex]; + } + } + + /// Remaining distance until the end of the path + public float remainingDistance { + get { + return totalDistance - distance; + } + set { + distance = totalDistance - value; + } + } + + /// Traversed distance from the start of the path + public float distance { + get { + return currentDistance; + } + set { + currentDistance = value; + + while (currentDistance < distanceToSegmentStart && segmentIndex > 0) PrevSegment(); + while (currentDistance > distanceToSegmentStart + currentSegmentLength && segmentIndex < path.Count - 2) NextSegment(); + } + } + + /// + /// Current segment. + /// The start and end points of the segment are path[value] and path[value+1]. + /// + public int segmentIndex { get; private set; } + + /// + /// True if this instance has a path set. + /// See: SetPath + /// + public bool valid { + get { + return path != null; + } + } + + /// Appends the remaining path between and to buffer + public void GetRemainingPath (List buffer) { + if (!valid) throw new System.Exception("PathInterpolator is not valid"); + buffer.Add(position); + for (int i = segmentIndex+1; i < path.Count; i++) { + buffer.Add(path[i]); + } + } + + /// + /// Set the path to interpolate along. + /// This will reset all interpolation variables. + /// + public void SetPath (List path) { + this.path = path; + currentDistance = 0; + segmentIndex = 0; + distanceToSegmentStart = 0; + + if (path == null) { + totalDistance = float.PositiveInfinity; + currentSegmentLength = float.PositiveInfinity; + return; + } + + if (path.Count < 2) throw new System.ArgumentException("Path must have a length of at least 2"); + + currentSegmentLength = (path[1] - path[0]).magnitude; + totalDistance = 0f; + + var prev = path[0]; + for (int i = 1; i < path.Count; i++) { + var current = path[i]; + totalDistance += (current - prev).magnitude; + prev = current; + } + } + + /// Move to the specified segment and move a fraction of the way to the next segment + public void MoveToSegment (int index, float fractionAlongSegment) { + if (path == null) return; + if (index < 0 || index >= path.Count - 1) throw new System.ArgumentOutOfRangeException("index"); + while (segmentIndex > index) PrevSegment(); + while (segmentIndex < index) NextSegment(); + distance = distanceToSegmentStart + Mathf.Clamp01(fractionAlongSegment) * currentSegmentLength; + } + + /// Move as close as possible to the specified point + public void MoveToClosestPoint (Vector3 point) { + if (path == null) return; + + float bestDist = float.PositiveInfinity; + float bestFactor = 0f; + int bestIndex = 0; + + for (int i = 0; i < path.Count-1; i++) { + float factor = VectorMath.ClosestPointOnLineFactor(path[i], path[i+1], point); + Vector3 closest = Vector3.Lerp(path[i], path[i+1], factor); + float dist = (point - closest).sqrMagnitude; + + if (dist < bestDist) { + bestDist = dist; + bestFactor = factor; + bestIndex = i; + } + } + + MoveToSegment(bestIndex, bestFactor); + } + + public void MoveToLocallyClosestPoint (Vector3 point, bool allowForwards = true, bool allowBackwards = true) { + if (path == null) return; + + while (allowForwards && segmentIndex < path.Count - 2 && (path[segmentIndex+1] - point).sqrMagnitude <= (path[segmentIndex] - point).sqrMagnitude) { + NextSegment(); + } + + while (allowBackwards && segmentIndex > 0 && (path[segmentIndex-1] - point).sqrMagnitude <= (path[segmentIndex] - point).sqrMagnitude) { + PrevSegment(); + } + + // Check the distances to the two segments extending from the vertex path[segmentIndex] + // and pick the position on those segments that is closest to the #point parameter. + float factor1 = 0, factor2 = 0, d1 = float.PositiveInfinity, d2 = float.PositiveInfinity; + if (segmentIndex > 0) { + factor1 = VectorMath.ClosestPointOnLineFactor(path[segmentIndex-1], path[segmentIndex], point); + d1 = (Vector3.Lerp(path[segmentIndex-1], path[segmentIndex], factor1) - point).sqrMagnitude; + } + + if (segmentIndex < path.Count - 1) { + factor2 = VectorMath.ClosestPointOnLineFactor(path[segmentIndex], path[segmentIndex+1], point); + d2 = (Vector3.Lerp(path[segmentIndex], path[segmentIndex+1], factor2) - point).sqrMagnitude; + } + + if (d1 < d2) MoveToSegment(segmentIndex - 1, factor1); + else MoveToSegment(segmentIndex, factor2); + } + + public void MoveToCircleIntersection2D (Vector3 circleCenter3D, float radius, IMovementPlane transform) { + if (path == null) return; + + // Move forwards as long as we are getting closer to circleCenter3D + while (segmentIndex < path.Count - 2 && VectorMath.ClosestPointOnLineFactor(path[segmentIndex], path[segmentIndex+1], circleCenter3D) > 1) { + NextSegment(); + } + + var circleCenter = transform.ToPlane(circleCenter3D); + + // Move forwards as long as the current segment endpoint is within the circle + while (segmentIndex < path.Count - 2 && (transform.ToPlane(path[segmentIndex+1]) - circleCenter).sqrMagnitude <= radius*radius) { + NextSegment(); + } + + // Calculate the intersection with the circle. This involves some math. + var factor = VectorMath.LineCircleIntersectionFactor(circleCenter, transform.ToPlane(path[segmentIndex]), transform.ToPlane(path[segmentIndex+1]), radius); + // Move to the intersection point + MoveToSegment(segmentIndex, factor); + } + + protected virtual void PrevSegment () { + segmentIndex--; + currentSegmentLength = (path[segmentIndex+1] - path[segmentIndex]).magnitude; + distanceToSegmentStart -= currentSegmentLength; + } + + protected virtual void NextSegment () { + segmentIndex++; + distanceToSegmentStart += currentSegmentLength; + currentSegmentLength = (path[segmentIndex+1] - path[segmentIndex]).magnitude; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathInterpolator.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathInterpolator.cs.meta new file mode 100644 index 0000000..329bd79 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathInterpolator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9c0392dbc5e744ee28e7b9ee81aea1e2 +timeCreated: 1490125383 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathPool.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathPool.cs new file mode 100644 index 0000000..9fa75d1 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathPool.cs @@ -0,0 +1,88 @@ +//#define ASTAR_NO_POOLING // Disable pooling for some reason. Maybe for debugging or just for measuring the difference. +using System; +using System.Collections.Generic; + +namespace Pathfinding { + /// Pools path objects to reduce load on the garbage collector + public static class PathPool { + static readonly Dictionary > pool = new Dictionary >(); + static readonly Dictionary totalCreated = new Dictionary(); + + /// + /// Adds a path to the pool. + /// This function should not be used directly. Instead use the Path.Claim and Path.Release functions. + /// + public static void Pool (Path path) { +#if !ASTAR_NO_POOLING + lock (pool) { + if (((IPathInternals)path).Pooled) { + throw new System.ArgumentException("The path is already pooled."); + } + + Stack poolStack; + if (!pool.TryGetValue(path.GetType(), out poolStack)) { + poolStack = new Stack(); + pool[path.GetType()] = poolStack; + } + + ((IPathInternals)path).Pooled = true; + ((IPathInternals)path).OnEnterPool(); + poolStack.Push(path); + } +#endif + } + + /// Total created instances of paths of the specified type + public static int GetTotalCreated (Type type) { + int created; + + if (totalCreated.TryGetValue(type, out created)) { + return created; + } else { + return 0; + } + } + + /// Number of pooled instances of a path of the specified type + public static int GetSize (Type type) { + Stack poolStack; + + if (pool.TryGetValue(type, out poolStack)) { + return poolStack.Count; + } else { + return 0; + } + } + + /// Get a path from the pool or create a new one if the pool is empty + public static T GetPath() where T : Path, new() { +#if ASTAR_NO_POOLING + T result = new T(); + ((IPathInternals)result).Reset(); + return result; +#else + lock (pool) { + T result; + Stack poolStack; + if (pool.TryGetValue(typeof(T), out poolStack) && poolStack.Count > 0) { + // Guaranteed to have the correct type + result = poolStack.Pop() as T; + } else { + result = new T(); + + // Make sure an entry for the path type exists + if (!totalCreated.ContainsKey(typeof(T))) { + totalCreated[typeof(T)] = 0; + } + + totalCreated[typeof(T)]++; + } + + ((IPathInternals)result).Pooled = false; + ((IPathInternals)result).Reset(); + return result; + } +#endif + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathPool.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathPool.cs.meta new file mode 100644 index 0000000..9ff4458 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathPool.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cefe1014ab62848a89016fb97b1f8f7b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs new file mode 100644 index 0000000..d6b42fe --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs @@ -0,0 +1,587 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +namespace Pathfinding { +#if NETFX_CORE + using Thread = Pathfinding.WindowsStore.Thread; +#else + using Thread = System.Threading.Thread; +#endif + + public class PathProcessor { + public event System.Action OnPathPreSearch; + public event System.Action OnPathPostSearch; + public event System.Action OnQueueUnblocked; + + internal readonly ThreadControlQueue queue; + readonly AstarPath astar; + readonly PathReturnQueue returnQueue; + + readonly PathHandler[] pathHandlers; + + /// References to each of the pathfinding threads + readonly Thread[] threads; + + /// + /// When no multithreading is used, the IEnumerator is stored here. + /// When no multithreading is used, a coroutine is used instead. It is not directly called with StartCoroutine + /// but a separate function has just a while loop which increments the main IEnumerator. + /// This is done so other functions can step the thread forward at any time, without having to wait for Unity to update it. + /// See: CalculatePaths + /// See: CalculatePathsHandler + /// + IEnumerator threadCoroutine; + + /// + /// Holds the next node index which has not been used by any previous node. + /// See: nodeIndexPool + /// + int nextNodeIndex = 1; + + /// + /// Holds indices for nodes that have been destroyed. + /// To avoid trashing a lot of memory structures when nodes are + /// frequently deleted and created, node indices are reused. + /// + readonly Stack nodeIndexPool = new Stack(); + + readonly List locks = new List(); + int nextLockID = 0; + +#if UNITY_2017_3_OR_NEWER + CustomSampler profilingSampler; +#endif + + /// + /// Number of parallel pathfinders. + /// Returns the number of concurrent processes which can calculate paths at once. + /// When using multithreading, this will be the number of threads, if not using multithreading it is always 1 (since only 1 coroutine is used). + /// See: threadInfos + /// See: IsUsingMultithreading + /// + public int NumThreads { + get { + return pathHandlers.Length; + } + } + + /// Returns whether or not multithreading is used + public bool IsUsingMultithreading { + get { + return threads != null; + } + } + + internal PathProcessor (AstarPath astar, PathReturnQueue returnQueue, int processors, bool multithreaded) { + this.astar = astar; + this.returnQueue = returnQueue; + + if (processors < 0) { + throw new System.ArgumentOutOfRangeException("processors"); + } + + if (!multithreaded && processors != 1) { + throw new System.Exception("Only a single non-multithreaded processor is allowed"); + } + + // Set up path queue with the specified number of receivers + queue = new ThreadControlQueue(processors); + pathHandlers = new PathHandler[processors]; + + for (int i = 0; i < processors; i++) { + pathHandlers[i] = new PathHandler(i, processors); + } + + if (multithreaded) { +#if UNITY_2017_3_OR_NEWER + profilingSampler = CustomSampler.Create("Calculating Path"); +#endif + + threads = new Thread[processors]; + + // Start lots of threads + for (int i = 0; i < processors; i++) { + var pathHandler = pathHandlers[i]; + threads[i] = new Thread(() => CalculatePathsThreaded(pathHandler)); +#if !UNITY_SWITCH || UNITY_EDITOR + // Note: Setting the thread name seems to crash when deploying for Switch: https://forum.arongranberg.com/t/path-processor-crashing-nintendo-switch-build/6584 + threads[i].Name = "Pathfinding Thread " + i; +#endif + threads[i].IsBackground = true; + threads[i].Start(); + } + } else { + // Start coroutine if not using multithreading + threadCoroutine = CalculatePaths(pathHandlers[0]); + } + } + + /// Prevents pathfinding from running while held + public struct GraphUpdateLock { + PathProcessor pathProcessor; + int id; + + public GraphUpdateLock (PathProcessor pathProcessor, bool block) { + this.pathProcessor = pathProcessor; + id = pathProcessor.Lock(block); + } + + /// + /// True while this lock is preventing the pathfinding threads from processing more paths. + /// Note that the pathfinding threads may not be paused yet (if this lock was obtained using PausePathfinding(false)). + /// + public bool Held { + get { + return pathProcessor != null && pathProcessor.locks.Contains(id); + } + } + + /// Allow pathfinding to start running again if no other locks are still held + public void Release () { + pathProcessor.Unlock(id); + } + } + + int Lock (bool block) { + queue.Block(); + + if (block) { + while (!queue.AllReceiversBlocked) { + if (IsUsingMultithreading) { + Thread.Sleep(1); + } else { + TickNonMultithreaded(); + } + } + } + + nextLockID++; + locks.Add(nextLockID); + return nextLockID; + } + + void Unlock (int id) { + if (!locks.Remove(id)) { + throw new System.ArgumentException("This lock has already been released"); + } + + // Check if there are no remaining active locks + if (locks.Count == 0) { + if (OnQueueUnblocked != null) OnQueueUnblocked(); + + queue.Unblock(); + } + } + + /// + /// Prevents pathfinding threads from starting to calculate any new paths. + /// + /// Returns: A lock object. You need to call Unlock on that object to allow pathfinding to resume. + /// + /// Note: In most cases this should not be called from user code. + /// + /// If true, this call will block until all pathfinding threads are paused. + /// otherwise the threads will be paused as soon as they are done with what they are currently doing. + public GraphUpdateLock PausePathfinding (bool block) { + return new GraphUpdateLock(this, block); + } + + public void TickNonMultithreaded () { + // Process paths + if (threadCoroutine != null) { + try { + threadCoroutine.MoveNext(); + } catch (System.Exception e) { + //This will kill pathfinding + threadCoroutine = null; + + // Queue termination exceptions should be ignored, they are supposed to kill the thread + if (!(e is ThreadControlQueue.QueueTerminationException)) { + Debug.LogException(e); + Debug.LogError("Unhandled exception during pathfinding. Terminating."); + queue.TerminateReceivers(); + + // This will throw an exception supposed to kill the thread + try { + queue.PopNoBlock(false); + } catch {} + } + } + } + } + + /// Calls 'Join' on each of the threads to block until they have completed + public void JoinThreads () { + if (threads != null) { + for (int i = 0; i < threads.Length; i++) { + if (!threads[i].Join(200)) { + Debug.LogError("Could not terminate pathfinding thread["+i+"] in 200ms, trying Thread.Abort"); + threads[i].Abort(); + } + } + } + } + + /// Calls 'Abort' on each of the threads + public void AbortThreads () { + if (threads == null) return; + for (int i = 0; i < threads.Length; i++) { + if (threads[i] != null && threads[i].IsAlive) threads[i].Abort(); + } + } + + /// + /// Returns a new global node index. + /// Warning: This method should not be called directly. It is used by the GraphNode constructor. + /// + public int GetNewNodeIndex () { + return nodeIndexPool.Count > 0 ? nodeIndexPool.Pop() : nextNodeIndex++; + } + + /// + /// Initializes temporary path data for a node. + /// Warning: This method should not be called directly. It is used by the GraphNode constructor. + /// + public void InitializeNode (GraphNode node) { + if (!queue.AllReceiversBlocked) { + throw new System.Exception("Trying to initialize a node when it is not safe to initialize any nodes. Must be done during a graph update. See http://arongranberg.com/astar/docs/graph-updates.php#direct"); + } + + for (int i = 0; i < pathHandlers.Length; i++) { + pathHandlers[i].InitializeNode(node); + } + + astar.hierarchicalGraph.OnCreatedNode(node); + } + + /// + /// Destroyes the given node. + /// This is to be called after the node has been disconnected from the graph so that it cannot be reached from any other nodes. + /// It should only be called during graph updates, that is when the pathfinding threads are either not running or paused. + /// + /// Warning: This method should not be called by user code. It is used internally by the system. + /// + public void DestroyNode (GraphNode node) { + if (node.NodeIndex == -1) return; + + nodeIndexPool.Push(node.NodeIndex); + + for (int i = 0; i < pathHandlers.Length; i++) { + pathHandlers[i].DestroyNode(node); + } + + astar.hierarchicalGraph.AddDirtyNode(node); + } + + /// + /// Main pathfinding method (multithreaded). + /// This method will calculate the paths in the pathfinding queue when multithreading is enabled. + /// + /// See: CalculatePaths + /// See: StartPath + /// + void CalculatePathsThreaded (PathHandler pathHandler) { +#if UNITY_2017_3_OR_NEWER + UnityEngine.Profiling.Profiler.BeginThreadProfiling("Pathfinding", "Pathfinding thread #" + (pathHandler.threadID+1)); +#endif + +#if !ASTAR_FAST_BUT_NO_EXCEPTIONS + try { +#endif + + // Max number of ticks we are allowed to continue working in one run. + // One tick is 1/10000 of a millisecond. + // We need to check once in a while if the thread should be stopped. + long maxTicks = (long)(10*10000); + long targetTick = System.DateTime.UtcNow.Ticks + maxTicks; + while (true) { + // The path we are currently calculating + Path path = queue.Pop(); +#if UNITY_2017_3_OR_NEWER + profilingSampler.Begin(); +#endif + // Access the internal implementation methods + IPathInternals ipath = (IPathInternals)path; + + // Trying to prevent simple modding to allow more than one thread + if (pathHandler.threadID > 0) { + throw new System.Exception("Thread Error"); + } + + AstarProfiler.StartFastProfile(0); + ipath.PrepareBase(pathHandler); + + // Now processing the path + // Will advance to Processing + ipath.AdvanceState(PathState.Processing); + + // Call some callbacks + if (OnPathPreSearch != null) { + OnPathPreSearch(path); + } + + // Tick for when the path started, used for calculating how long time the calculation took + long startTicks = System.DateTime.UtcNow.Ticks; + + // Prepare the path + ipath.Prepare(); + + AstarProfiler.EndFastProfile(0); + + if (path.CompleteState == PathCompleteState.NotCalculated) { + // For visualization purposes, we set the last computed path to p, so we can view debug info on it in the editor (scene view). + astar.debugPathData = ipath.PathHandler; + astar.debugPathID = path.pathID; + + AstarProfiler.StartFastProfile(1); + + // Initialize the path, now ready to begin search + ipath.Initialize(); + + AstarProfiler.EndFastProfile(1); + + // Loop while the path has not been fully calculated + while (path.CompleteState == PathCompleteState.NotCalculated) { + // Do some work on the path calculation. + // The function will return when it has taken too much time + // or when it has finished calculation + AstarProfiler.StartFastProfile(2); + ipath.CalculateStep(targetTick); + AstarProfiler.EndFastProfile(2); + + targetTick = System.DateTime.UtcNow.Ticks + maxTicks; + + // Cancel function (and thus the thread) if no more paths should be accepted. + // This is done when the A* object is about to be destroyed + // The path is returned and then this function will be terminated + if (queue.IsTerminating) { + path.FailWithError("AstarPath object destroyed"); + } + } + + path.duration = (System.DateTime.UtcNow.Ticks - startTicks)*0.0001F; + +#if ProfileAstar + System.Threading.Interlocked.Increment(ref AstarPath.PathsCompleted); + System.Threading.Interlocked.Add(ref AstarPath.TotalSearchTime, System.DateTime.UtcNow.Ticks - startTicks); +#endif + } + + // Cleans up node tagging and other things + ipath.Cleanup(); + + AstarProfiler.StartFastProfile(9); + + if (path.immediateCallback != null) path.immediateCallback(path); + + if (OnPathPostSearch != null) { + OnPathPostSearch(path); + } + + // Push the path onto the return stack + // It will be detected by the main Unity thread and returned as fast as possible (the next late update hopefully) + returnQueue.Enqueue(path); + + // Will advance to ReturnQueue + ipath.AdvanceState(PathState.ReturnQueue); + + AstarProfiler.EndFastProfile(9); +#if UNITY_2017_3_OR_NEWER + profilingSampler.End(); +#endif + } +#if !ASTAR_FAST_BUT_NO_EXCEPTIONS + } + catch (System.Exception e) { +#if !NETFX_CORE + if (e is ThreadAbortException || e is ThreadControlQueue.QueueTerminationException) +#else + if (e is ThreadControlQueue.QueueTerminationException) +#endif + { + if (astar.logPathResults == PathLog.Heavy) + Debug.LogWarning("Shutting down pathfinding thread #" + pathHandler.threadID); + return; + } + Debug.LogException(e); + Debug.LogError("Unhandled exception during pathfinding. Terminating."); + // Unhandled exception, kill pathfinding + queue.TerminateReceivers(); + } finally { +#if UNITY_2017_3_OR_NEWER + UnityEngine.Profiling.Profiler.EndThreadProfiling(); +#endif + } +#endif + + Debug.LogError("Error : This part should never be reached."); + queue.ReceiverTerminated(); + } + + /// + /// Main pathfinding method. + /// This method will calculate the paths in the pathfinding queue. + /// + /// See: CalculatePathsThreaded + /// See: StartPath + /// + IEnumerator CalculatePaths (PathHandler pathHandler) { + // Max number of ticks before yielding/sleeping + long maxTicks = (long)(astar.maxFrameTime*10000); + long targetTick = System.DateTime.UtcNow.Ticks + maxTicks; + + while (true) { + // The path we are currently calculating + Path p = null; + + AstarProfiler.StartProfile("Path Queue"); + + // Try to get the next path to be calculated + bool blockedBefore = false; + while (p == null) { + try { + p = queue.PopNoBlock(blockedBefore); + blockedBefore |= p == null; + } catch (ThreadControlQueue.QueueTerminationException) { + yield break; + } + + if (p == null) { + AstarProfiler.EndProfile(); + yield return null; + AstarProfiler.StartProfile("Path Queue"); + } + } + + AstarProfiler.EndProfile(); + + AstarProfiler.StartProfile("Path Calc"); + + IPathInternals ip = (IPathInternals)p; + + // Max number of ticks we are allowed to continue working in one run + // One tick is 1/10000 of a millisecond + maxTicks = (long)(astar.maxFrameTime*10000); + + ip.PrepareBase(pathHandler); + + // Now processing the path + // Will advance to Processing + ip.AdvanceState(PathState.Processing); + + // Call some callbacks + // It needs to be stored in a local variable to avoid race conditions + var tmpOnPathPreSearch = OnPathPreSearch; + if (tmpOnPathPreSearch != null) tmpOnPathPreSearch(p); + + // Tick for when the path started, used for calculating how long time the calculation took + long startTicks = System.DateTime.UtcNow.Ticks; + long totalTicks = 0; + + AstarProfiler.StartFastProfile(8); + + AstarProfiler.StartFastProfile(0); + //Prepare the path + AstarProfiler.StartProfile("Path Prepare"); + ip.Prepare(); + AstarProfiler.EndProfile("Path Prepare"); + AstarProfiler.EndFastProfile(0); + + // Check if the Prepare call caused the path to complete + // If this happens the path usually failed + if (p.CompleteState == PathCompleteState.NotCalculated) { + // For debug uses, we set the last computed path to p, so we can view debug info on it in the editor (scene view). + astar.debugPathData = ip.PathHandler; + astar.debugPathID = p.pathID; + + // Initialize the path, now ready to begin search + AstarProfiler.StartProfile("Path Initialize"); + ip.Initialize(); + AstarProfiler.EndProfile(); + + // The error can turn up in the Init function + while (p.CompleteState == PathCompleteState.NotCalculated) { + // Do some work on the path calculation. + // The function will return when it has taken too much time + // or when it has finished calculation + AstarProfiler.StartFastProfile(2); + + AstarProfiler.StartProfile("Path Calc Step"); + ip.CalculateStep(targetTick); + AstarProfiler.EndFastProfile(2); + + AstarProfiler.EndProfile(); + + // If the path has finished calculation, we can break here directly instead of sleeping + // Improves latency + if (p.CompleteState != PathCompleteState.NotCalculated) break; + + AstarProfiler.EndFastProfile(8); + totalTicks += System.DateTime.UtcNow.Ticks-startTicks; + // Yield/sleep so other threads can work + + AstarProfiler.EndProfile(); + yield return null; + AstarProfiler.StartProfile("Path Calc"); + + startTicks = System.DateTime.UtcNow.Ticks; + AstarProfiler.StartFastProfile(8); + + // Cancel function (and thus the thread) if no more paths should be accepted. + // This is done when the A* object is about to be destroyed + // The path is returned and then this function will be terminated (see similar IF statement higher up in the function) + if (queue.IsTerminating) { + p.FailWithError("AstarPath object destroyed"); + } + + targetTick = System.DateTime.UtcNow.Ticks + maxTicks; + } + + totalTicks += System.DateTime.UtcNow.Ticks-startTicks; + p.duration = totalTicks*0.0001F; + +#if ProfileAstar + System.Threading.Interlocked.Increment(ref AstarPath.PathsCompleted); +#endif + } + + // Cleans up node tagging and other things + ip.Cleanup(); + + AstarProfiler.EndFastProfile(8); + + // Call the immediate callback + // It needs to be stored in a local variable to avoid race conditions + var tmpImmediateCallback = p.immediateCallback; + if (tmpImmediateCallback != null) tmpImmediateCallback(p); + + AstarProfiler.StartFastProfile(13); + + // It needs to be stored in a local variable to avoid race conditions + var tmpOnPathPostSearch = OnPathPostSearch; + if (tmpOnPathPostSearch != null) tmpOnPathPostSearch(p); + + AstarProfiler.EndFastProfile(13); + + // Push the path onto the return stack + // It will be detected by the main Unity thread and returned as fast as possible (the next late update) + returnQueue.Enqueue(p); + + ip.AdvanceState(PathState.ReturnQueue); + + AstarProfiler.EndProfile(); + + // Wait a bit if we have calculated a lot of paths + if (System.DateTime.UtcNow.Ticks > targetTick) { + yield return null; + targetTick = System.DateTime.UtcNow.Ticks + maxTicks; + } + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs.meta new file mode 100644 index 0000000..4016a80 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f1b519f8b6e2c450aaae5cb2df6c73be +timeCreated: 1443114816 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathReturnQueue.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathReturnQueue.cs new file mode 100644 index 0000000..7d49ca4 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathReturnQueue.cs @@ -0,0 +1,73 @@ +using UnityEngine; +using System.Collections.Generic; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +namespace Pathfinding { + class PathReturnQueue { + /// + /// Holds all paths which are waiting to be flagged as completed. + /// See: + /// + Queue pathReturnQueue = new Queue(); + + /// + /// Paths are claimed silently by some object to prevent them from being recycled while still in use. + /// This will be set to the AstarPath object. + /// + System.Object pathsClaimedSilentlyBy; + + public PathReturnQueue (System.Object pathsClaimedSilentlyBy) { + this.pathsClaimedSilentlyBy = pathsClaimedSilentlyBy; + } + + public void Enqueue (Path path) { + lock (pathReturnQueue) { + pathReturnQueue.Enqueue(path); + } + } + + /// + /// Returns all paths in the return stack. + /// Paths which have been processed are put in the return stack. + /// This function will pop all items from the stack and return them to e.g the Seeker requesting them. + /// + /// Do not return all paths at once if it takes a long time, instead return some and wait until the next call. + public void ReturnPaths (bool timeSlice) { + Profiler.BeginSample("Calling Path Callbacks"); + + // Hard coded limit on 1.0 ms + long targetTick = timeSlice ? System.DateTime.UtcNow.Ticks + 1 * 10000 : 0; + + int counter = 0; + // Loop through the linked list and return all paths + while (true) { + // Move to the next path + Path path; + lock (pathReturnQueue) { + if (pathReturnQueue.Count == 0) break; + path = pathReturnQueue.Dequeue(); + } + + // Return the path + ((IPathInternals)path).ReturnPath(); + + // Will increment path state to Returned + ((IPathInternals)path).AdvanceState(PathState.Returned); + + path.Release(pathsClaimedSilentlyBy, true); + + counter++; + // At least 5 paths will be returned, even if timeSlice is enabled + if (counter > 5 && timeSlice) { + counter = 0; + if (System.DateTime.UtcNow.Ticks >= targetTick) { + break; + } + } + } + Profiler.EndSample(); + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathReturnQueue.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathReturnQueue.cs.meta new file mode 100644 index 0000000..0185e94 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/PathReturnQueue.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 70ca370ae38794366be2fa32880f362e +timeCreated: 1443114816 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/StackPool.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/StackPool.cs new file mode 100644 index 0000000..204bbc8 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/StackPool.cs @@ -0,0 +1,98 @@ +//#define ASTAR_NO_POOLING //@SHOWINEDITOR Disable pooling for some reason. Could be debugging or just for measuring the difference. + +using System.Collections.Generic; + +namespace Pathfinding.Util { + /// + /// Lightweight Stack Pool. + /// Handy class for pooling stacks of type T. + /// + /// Usage: + /// - Claim a new stack using Stack foo = StackPool.Claim (); + /// - Use it and do stuff with it + /// - Release it with StackPool.Release (foo); + /// + /// You do not need to clear the stack before releasing it. + /// After you have released a stack, you should never use it again. + /// + /// Warning: This class is not thread safe + /// + /// \since Version 3.2 + /// See: Pathfinding.Util.ListPool + /// + public static class StackPool { + /// Internal pool + static readonly List > pool; + + /// Static constructor + static StackPool () { + pool = new List >(); + } + + /// + /// Claim a stack. + /// Returns a pooled stack if any are in the pool. + /// Otherwise it creates a new one. + /// After usage, this stack should be released using the Release function (though not strictly necessary). + /// + public static Stack Claim () { +#if ASTAR_NO_POOLING + return new Stack(); +#else + lock (pool) { + if (pool.Count > 0) { + Stack ls = pool[pool.Count-1]; + pool.RemoveAt(pool.Count-1); + return ls; + } + } + + return new Stack(); +#endif + } + + /// + /// Makes sure the pool contains at least count pooled items. + /// This is good if you want to do all allocations at start. + /// + public static void Warmup (int count) { + var tmp = new Stack[count]; + + for (int i = 0; i < count; i++) tmp[i] = Claim(); + for (int i = 0; i < count; i++) Release(tmp[i]); + } + + /// + /// Releases a stack. + /// After the stack has been released it should not be used anymore. + /// Releasing a stack twice will cause an error. + /// + public static void Release (Stack stack) { +#if !ASTAR_NO_POOLING + stack.Clear(); + + lock (pool) { + for (int i = 0; i < pool.Count; i++) + if (pool[i] == stack) UnityEngine.Debug.LogError("The Stack is released even though it is inside the pool"); + + pool.Add(stack); + } +#endif + } + + /// + /// Clears all pooled stacks of this type. + /// This is an O(n) operation, where n is the number of pooled stacks + /// + public static void Clear () { + lock (pool) { + pool.Clear(); + } + } + + /// Number of stacks of this type in the pool + public static int GetSize () { + return pool.Count; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/StackPool.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/StackPool.cs.meta new file mode 100644 index 0000000..1c8a3b5 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/StackPool.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: de467bbbb1ff84668ae8262caad00941 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ThreadControlQueue.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ThreadControlQueue.cs new file mode 100644 index 0000000..5efc339 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ThreadControlQueue.cs @@ -0,0 +1,283 @@ +using System.Threading; + +namespace Pathfinding { + /// Queue of paths to be processed by the system + class ThreadControlQueue { + public class QueueTerminationException : System.Exception { + } + + Path head; + Path tail; + + readonly System.Object lockObj = new System.Object(); + + readonly int numReceivers; + + bool blocked; + + /// + /// Number of receiver threads that are currently blocked. + /// This is only modified while a thread has a lock on lockObj + /// + int blockedReceivers; + + /// + /// True while head == null. + /// This is only modified while a thread has a lock on lockObj + /// + bool starving; + + /// + /// True after TerminateReceivers has been called. + /// All receivers will be terminated when they next call Pop. + /// + bool terminate; + + ManualResetEvent block = new ManualResetEvent(true); + + /// + /// Create a new queue with the specified number of receivers. + /// It is important that the number of receivers is fixed. + /// Properties like AllReceiversBlocked rely on knowing the exact number of receivers using the Pop (or PopNoBlock) methods. + /// + public ThreadControlQueue (int numReceivers) { + this.numReceivers = numReceivers; + } + + /// True if the queue is empty + public bool IsEmpty { + get { + return head == null; + } + } + + /// True if TerminateReceivers has been called + public bool IsTerminating { + get { + return terminate; + } + } + + /// Block queue, all calls to Pop will block until Unblock is called + public void Block () { + lock (lockObj) { + blocked = true; + block.Reset(); + } + } + + /// + /// Unblock queue. + /// Calls to Pop will not block anymore. + /// See: Block + /// + public void Unblock () { + lock (lockObj) { + blocked = false; + block.Set(); + } + } + + /// + /// Aquires a lock on this queue. + /// Must be paired with a call to + /// + public void Lock () { + Monitor.Enter(lockObj); + } + + /// Releases the lock on this queue + public void Unlock () { + Monitor.Exit(lockObj); + } + + /// True if blocking and all receivers are waiting for unblocking + public bool AllReceiversBlocked { + get { + lock (lockObj) { + return blocked && blockedReceivers == numReceivers; + } + } + } + + /// Push a path to the front of the queue + public void PushFront (Path path) { + lock (lockObj) { + // If termination is due, why add stuff to a queue which will not be read from anyway + if (terminate) return; + + if (tail == null) {// (tail == null) ==> (head == null) + head = path; + tail = path; + + if (starving && !blocked) { + starving = false; + block.Set(); + } else { + starving = false; + } + } else { + path.next = head; + head = path; + } + } + } + + /// Push a path to the end of the queue + public void Push (Path path) { + lock (lockObj) { + // If termination is due, why add stuff to a queue which will not be read from anyway + if (terminate) return; + + if (tail == null) {// (tail == null) ==> (head == null) + head = path; + tail = path; + + if (starving && !blocked) { + starving = false; + block.Set(); + } else { + starving = false; + } + } else { + tail.next = path; + tail = path; + } + } + } + + void Starving () { + starving = true; + block.Reset(); + } + + /// All calls to Pop and PopNoBlock will now generate exceptions + public void TerminateReceivers () { + lock (lockObj) { + terminate = true; + block.Set(); + } + } + + /// + /// Pops the next item off the queue. + /// This call will block if there are no items in the queue or if the queue is currently blocked. + /// + /// Returns: A Path object, guaranteed to be not null. + /// \throws QueueTerminationException if has been called. + /// \throws System.InvalidOperationException if more receivers get blocked than the fixed count sent to the constructor + /// + public Path Pop () { + Monitor.Enter(lockObj); + try { + if (terminate) { + blockedReceivers++; + throw new QueueTerminationException(); + } + + if (head == null) { + Starving(); + } + + while (blocked || starving) { + blockedReceivers++; + + if (blockedReceivers > numReceivers) { + throw new System.InvalidOperationException("More receivers are blocked than specified in constructor ("+blockedReceivers + " > " + numReceivers+")"); + } + + Monitor.Exit(lockObj); + + block.WaitOne(); + + Monitor.Enter(lockObj); + + if (terminate) { + throw new QueueTerminationException(); + } + + blockedReceivers--; + + if (head == null) { + Starving(); + } + } + Path p = head; + + var newHead = head.next; + if (newHead == null) { + tail = null; + } + head.next = null; + head = newHead; + return p; + } finally { + Monitor.Exit(lockObj); + } + } + + /// + /// Call when a receiver was terminated in other ways than by a QueueTerminationException. + /// + /// After this call, the receiver should be dead and not call anything else in this class. + /// + public void ReceiverTerminated () { + Monitor.Enter(lockObj); + blockedReceivers++; + Monitor.Exit(lockObj); + } + + /// + /// Pops the next item off the queue, this call will not block. + /// To ensure stability, the caller must follow this pattern. + /// 1. Call PopNoBlock(false), if a null value is returned, wait for a bit (e.g yield return null in a Unity coroutine) + /// 2. try again with PopNoBlock(true), if still null, wait for a bit + /// 3. Repeat from step 2. + /// + /// \throws QueueTerminationException if has been called. + /// \throws System.InvalidOperationException if more receivers get blocked than the fixed count sent to the constructor + /// + public Path PopNoBlock (bool blockedBefore) { + Monitor.Enter(lockObj); + try { + if (terminate) { + blockedReceivers++; + throw new QueueTerminationException(); + } + + if (head == null) { + Starving(); + } + if (blocked || starving) { + if (!blockedBefore) { + blockedReceivers++; + + if (terminate) throw new QueueTerminationException(); + + if (blockedReceivers == numReceivers) { + //Last alive + } else if (blockedReceivers > numReceivers) { + throw new System.InvalidOperationException("More receivers are blocked than specified in constructor ("+blockedReceivers + " > " + numReceivers+")"); + } + } + return null; + } + if (blockedBefore) { + blockedReceivers--; + } + + Path p = head; + + var newHead = head.next; + if (newHead == null) { + tail = null; + } + head.next = null; + head = newHead; + return p; + } finally { + Monitor.Exit(lockObj); + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ThreadControlQueue.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ThreadControlQueue.cs.meta new file mode 100644 index 0000000..cad1e5f --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/ThreadControlQueue.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e6a8344fd0d1c453cbaf5c5eb8f55ca5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WindowsStoreCompatibility.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WindowsStoreCompatibility.cs new file mode 100644 index 0000000..06640cf --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WindowsStoreCompatibility.cs @@ -0,0 +1,148 @@ +#if NETFX_CORE +using System.Threading; +using System.Threading.Tasks; +using System.Reflection; +using System.IO; +using TP = System.Reflection.TypeInfo; +#else +using TP = System.Type; +#endif + +namespace Pathfinding.WindowsStore { + public static class WindowsStoreCompatibility { + public static System.Type GetTypeFromInfo (TP type) { +#if NETFX_CORE + return type.AsType(); +#else + return type; +#endif + } + + public static TP GetTypeInfo (System.Type type) { +#if NETFX_CORE + return type.GetTypeInfo(); +#else + return type; +#endif + } + +#if NETFX_CORE + public static void Close (this BinaryWriter stream) { + stream.Dispose(); + } + + public static void Close (this BinaryReader stream) { + stream.Dispose(); + } + + public static void Close (this StreamWriter stream) { + stream.Dispose(); + } +#endif + } + +#if NETFX_CORE + public delegate void ParameterizedThreadStart (System.Object ob); + public delegate void ThreadStart (); + + public class Thread { + // + // Fields + // + private Pathfinding.WindowsStore.ParameterizedThreadStart _paramThreadStart; + + private CancellationTokenSource _taskCancellationTokenSource; + + private Task _task = null; + + private Pathfinding.WindowsStore.ThreadStart _threadStart; + + private static ManualResetEvent SleepEvent = new ManualResetEvent(false); + + // + // Properties + // + public bool IsAlive { + get { + return this._task != null && !this._task.IsCompleted; + } + set { + throw new System.NotImplementedException(); + } + } + + public bool IsBackground { + get { + return false; + } + set { + } + } + + public string Name { + get; + set; + } + + // + // Constructors + // + public Thread (Pathfinding.WindowsStore.ParameterizedThreadStart start) { + this._taskCancellationTokenSource = new CancellationTokenSource(); + this._paramThreadStart = start; + } + + public Thread (Pathfinding.WindowsStore.ThreadStart start) { + this._taskCancellationTokenSource = new CancellationTokenSource(); + this._threadStart = start; + } + + // + // Static Methods + // + public static void Sleep (int ms) { + SleepEvent.WaitOne(ms); + } + + // + // Methods + // + public void Abort () { + if (this._taskCancellationTokenSource != null) { + this._taskCancellationTokenSource.Cancel(); + } + } + + private void EnsureTask (object paramThreadStartParam = null) { + if (this._task == null) { + if (this._paramThreadStart != null) { + this._task = new Task(delegate { + this._paramThreadStart(paramThreadStartParam); + }, this._taskCancellationTokenSource.Token); + } else { + if (this._threadStart != null) { + this._task = new Task(delegate { + this._threadStart(); + }, this._taskCancellationTokenSource.Token); + } + } + } + } + + public bool Join (int ms) { + this.EnsureTask(); + return this._task.Wait(ms, this._taskCancellationTokenSource.Token); + } + + public void Start () { + this.EnsureTask(); + this._task.Start(TaskScheduler.Default); + } + + public void Start (object param) { + this.EnsureTask(param); + this._task.Start(TaskScheduler.Default); + } + } +#endif +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WindowsStoreCompatibility.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WindowsStoreCompatibility.cs.meta new file mode 100644 index 0000000..d99c0c7 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WindowsStoreCompatibility.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 810a4e97f5ccb4b5184c4c3206492974 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WorkItemProcessor.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WorkItemProcessor.cs new file mode 100644 index 0000000..1523801 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WorkItemProcessor.cs @@ -0,0 +1,337 @@ +using UnityEngine; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +namespace Pathfinding { + using UnityEngine; + + /// + /// An item of work that can be executed when graphs are safe to update. + /// See: + /// See: + /// + public struct AstarWorkItem { + /// + /// Init function. + /// May be null if no initialization is needed. + /// Will be called once, right before the first call to . + /// + public System.Action init; + + /// + /// Init function. + /// May be null if no initialization is needed. + /// Will be called once, right before the first call to . + /// + /// A context object is sent as a parameter. This can be used + /// to for example queue a flood fill that will be executed either + /// when a work item calls EnsureValidFloodFill or all work items have + /// been completed. If multiple work items are updating nodes + /// so that they need a flood fill afterwards, using the QueueFloodFill + /// method is preferred since then only a single flood fill needs + /// to be performed for all of the work items instead of one + /// per work item. + /// + public System.Action initWithContext; + + /// + /// Update function, called once per frame when the work item executes. + /// Takes a param force. If that is true, the work item should try to complete the whole item in one go instead + /// of spreading it out over multiple frames. + /// Returns: True when the work item is completed. + /// + public System.Func update; + + /// + /// Update function, called once per frame when the work item executes. + /// Takes a param force. If that is true, the work item should try to complete the whole item in one go instead + /// of spreading it out over multiple frames. + /// Returns: True when the work item is completed. + /// + /// A context object is sent as a parameter. This can be used + /// to for example queue a flood fill that will be executed either + /// when a work item calls EnsureValidFloodFill or all work items have + /// been completed. If multiple work items are updating nodes + /// so that they need a flood fill afterwards, using the QueueFloodFill + /// method is preferred since then only a single flood fill needs + /// to be performed for all of the work items instead of one + /// per work item. + /// + public System.Func updateWithContext; + + public AstarWorkItem (System.Func update) { + this.init = null; + this.initWithContext = null; + this.updateWithContext = null; + this.update = update; + } + + public AstarWorkItem (System.Func update) { + this.init = null; + this.initWithContext = null; + this.updateWithContext = update; + this.update = null; + } + + public AstarWorkItem (System.Action init, System.Func update = null) { + this.init = init; + this.initWithContext = null; + this.update = update; + this.updateWithContext = null; + } + + public AstarWorkItem (System.Action init, System.Func update = null) { + this.init = null; + this.initWithContext = init; + this.update = null; + this.updateWithContext = update; + } + } + + /// Interface to expose a subset of the WorkItemProcessor functionality + public interface IWorkItemContext { + /// + /// Call during work items to queue a flood fill. + /// An instant flood fill can be done via FloodFill() + /// but this method can be used to batch several updates into one + /// to increase performance. + /// WorkItems which require a valid Flood Fill in their execution can call EnsureValidFloodFill + /// to ensure that a flood fill is done if any earlier work items queued one. + /// + /// Once a flood fill is queued it will be done after all WorkItems have been executed. + /// + /// Deprecated: Avoid using. This will force a full recalculation of the connected components. In most cases the HierarchicalGraph class takes care of things automatically behind the scenes now. In pretty much all cases you should be able to remove the call to this function. + /// + [System.Obsolete("Avoid using. This will force a full recalculation of the connected components. In most cases the HierarchicalGraph class takes care of things automatically behind the scenes now. In pretty much all cases you should be able to remove the call to this function.")] + void QueueFloodFill (); + + /// + /// If a WorkItem needs to have a valid area information during execution, call this method to ensure there are no pending flood fills. + /// If you are using the property or the method in your work items, then you might want to call this method before you use them + /// to ensure that the data is up to date. + /// + /// See: + /// + /// + /// AstarPath.active.AddWorkItem(new AstarWorkItem((IWorkItemContext ctx) => { + /// ctx.EnsureValidFloodFill(); + /// + /// // The above call guarantees that this method has up to date information about the graph + /// if (PathUtilities.IsPathPossible(someNode, someOtherNode)) { + /// // Do something + /// } + /// })); + /// + /// + void EnsureValidFloodFill (); + + /// + /// Trigger a graph modification event. + /// This will cause a event to be issued after all graph updates have finished. + /// Some scripts listen for this event. For example off-mesh links listen to it and will recalculate which nodes they are connected to when it it sent. + /// If a graph is dirtied multiple times, or even if multiple graphs are dirtied, the event will only be sent once. + /// + void SetGraphDirty (NavGraph graph); + } + + class WorkItemProcessor : IWorkItemContext { + /// Used to prevent waiting for work items to complete inside other work items as that will cause the program to hang + public bool workItemsInProgressRightNow { get; private set; } + + readonly AstarPath astar; + readonly IndexedQueue workItems = new IndexedQueue(); + + /// True if any work items are queued right now + public bool anyQueued { + get { return workItems.Count > 0; } + } + + /// + /// True if any work items have queued a flood fill. + /// See: QueueWorkItemFloodFill + /// + bool queuedWorkItemFloodFill = false; + + bool anyGraphsDirty = true; + + /// + /// True while a batch of work items are being processed. + /// Set to true when a work item is started to be processed, reset to false when all work items are complete. + /// + /// Work item updates are often spread out over several frames, this flag will be true during the whole time the + /// updates are in progress. + /// + public bool workItemsInProgress { get; private set; } + + /// Similar to Queue but allows random access + class IndexedQueue { + T[] buffer = new T[4]; + int start; + + public T this[int index] { + get { + if (index < 0 || index >= Count) throw new System.IndexOutOfRangeException(); + return buffer[(start + index) % buffer.Length]; + } + set { + if (index < 0 || index >= Count) throw new System.IndexOutOfRangeException(); + buffer[(start + index) % buffer.Length] = value; + } + } + + public int Count { get; private set; } + + public void Enqueue (T item) { + if (Count == buffer.Length) { + var newBuffer = new T[buffer.Length*2]; + for (int i = 0; i < Count; i++) { + newBuffer[i] = this[i]; + } + buffer = newBuffer; + start = 0; + } + + buffer[(start + Count) % buffer.Length] = item; + Count++; + } + + public T Dequeue () { + if (Count == 0) throw new System.InvalidOperationException(); + var item = buffer[start]; + start = (start + 1) % buffer.Length; + Count--; + return item; + } + } + + /// + /// Call during work items to queue a flood fill. + /// An instant flood fill can be done via FloodFill() + /// but this method can be used to batch several updates into one + /// to increase performance. + /// WorkItems which require a valid Flood Fill in their execution can call EnsureValidFloodFill + /// to ensure that a flood fill is done if any earlier work items queued one. + /// + /// Once a flood fill is queued it will be done after all WorkItems have been executed. + /// + void IWorkItemContext.QueueFloodFill () { + queuedWorkItemFloodFill = true; + } + + void IWorkItemContext.SetGraphDirty (NavGraph graph) { + anyGraphsDirty = true; + } + + /// If a WorkItem needs to have a valid area information during execution, call this method to ensure there are no pending flood fills + public void EnsureValidFloodFill () { + if (queuedWorkItemFloodFill) { + astar.hierarchicalGraph.RecalculateAll(); + } else { + astar.hierarchicalGraph.RecalculateIfNecessary(); + } + } + + public WorkItemProcessor (AstarPath astar) { + this.astar = astar; + } + + public void OnFloodFill () { + queuedWorkItemFloodFill = false; + } + + /// + /// Add a work item to be processed when pathfinding is paused. + /// + /// See: ProcessWorkItems + /// + public void AddWorkItem (AstarWorkItem item) { + workItems.Enqueue(item); + } + + /// + /// Process graph updating work items. + /// Process all queued work items, e.g graph updates and the likes. + /// + /// Returns: + /// - false if there are still items to be processed. + /// - true if the last work items was processed and pathfinding threads are ready to be resumed. + /// + /// See: AddWorkItem + /// See: threadSafeUpdateState + /// See: Update + /// + public bool ProcessWorkItems (bool force) { + if (workItemsInProgressRightNow) throw new System.Exception("Processing work items recursively. Please do not wait for other work items to be completed inside work items. " + + "If you think this is not caused by any of your scripts, this might be a bug."); + + workItemsInProgressRightNow = true; + astar.data.LockGraphStructure(true); + while (workItems.Count > 0) { + // Working on a new batch + if (!workItemsInProgress) { + workItemsInProgress = true; + queuedWorkItemFloodFill = false; + } + + // Peek at first item in the queue + AstarWorkItem itm = workItems[0]; + bool status; + + try { + // Call init the first time the item is seen + if (itm.init != null) { + itm.init(); + itm.init = null; + } + + if (itm.initWithContext != null) { + itm.initWithContext(this); + itm.initWithContext = null; + } + + // Make sure the item in the queue is up to date + workItems[0] = itm; + + if (itm.update != null) { + status = itm.update(force); + } else if (itm.updateWithContext != null) { + status = itm.updateWithContext(this, force); + } else { + status = true; + } + } catch { + workItems.Dequeue(); + workItemsInProgressRightNow = false; + astar.data.UnlockGraphStructure(); + throw; + } + + if (!status) { + if (force) { + Debug.LogError("Misbehaving WorkItem. 'force'=true but the work item did not complete.\nIf force=true is passed to a WorkItem it should always return true."); + } + + // Still work items to process + workItemsInProgressRightNow = false; + astar.data.UnlockGraphStructure(); + return false; + } else { + workItems.Dequeue(); + } + } + + EnsureValidFloodFill(); + + Profiler.BeginSample("PostUpdate"); + if (anyGraphsDirty) GraphModifier.TriggerEvent(GraphModifier.EventType.PostUpdate); + Profiler.EndSample(); + + anyGraphsDirty = false; + workItemsInProgressRightNow = false; + workItemsInProgress = false; + astar.data.UnlockGraphStructure(); + return true; + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WorkItemProcessor.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WorkItemProcessor.cs.meta new file mode 100644 index 0000000..b9af85f --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Misc/WorkItemProcessor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4236764d0fc1041abaac13b858be5118 +timeCreated: 1443114816 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Nodes.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Nodes.meta new file mode 100644 index 0000000..76b5eb8 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Nodes.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b41391edefb794969b06b1e03993abf4 diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Nodes/GraphNode.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Nodes/GraphNode.cs new file mode 100644 index 0000000..343bcc2 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Nodes/GraphNode.cs @@ -0,0 +1,811 @@ +using UnityEngine; +using System.Collections.Generic; +using Pathfinding.Serialization; + +namespace Pathfinding { + using Pathfinding.Util; + + /// Represents a connection to another node + public struct Connection { + /// Node which this connection goes to + public GraphNode node; + + /// + /// Cost of moving along this connection. + /// A cost of 1000 corresponds approximately to the cost of moving one world unit. + /// + public uint cost; + + /// + /// Side of the node shape which this connection uses. + /// Used for mesh nodes. + /// A value of 0 corresponds to using the side for vertex 0 and vertex 1 on the node. 1 corresponds to vertex 1 and 2, etc. + /// A negative value means that this connection does not use any side at all (this is mostly used for off-mesh links). + /// + /// Note: Due to alignment, the and fields use 12 bytes which will be padded + /// to 16 bytes when used in an array even if this field would be removed. + /// So this field does not contribute to increased memory usage. + /// + /// See: TriangleMeshNode + /// See: TriangleMeshNode.AddConnection + /// + public byte shapeEdge; + + public Connection (GraphNode node, uint cost, byte shapeEdge = 0xFF) { + this.node = node; + this.cost = cost; + this.shapeEdge = shapeEdge; + } + + public override int GetHashCode () { + return node.GetHashCode() ^ (int)cost; + } + + public override bool Equals (object obj) { + if (obj == null) return false; + var conn = (Connection)obj; + return conn.node == node && conn.cost == cost && conn.shapeEdge == shapeEdge; + } + } + + /// Base class for all nodes + public abstract class GraphNode { + /// Internal unique index. Also stores some bitpacked values such as and . + private int nodeIndex; + + /// + /// Bitpacked field holding several pieces of data. + /// See: Walkable + /// See: Area + /// See: GraphIndex + /// See: Tag + /// + protected uint flags; + +#if !ASTAR_NO_PENALTY + /// + /// Penalty cost for walking on this node. + /// This can be used to make it harder/slower to walk over certain nodes. + /// + /// A penalty of 1000 (Int3.Precision) corresponds to the cost of walking one world unit. + /// + /// See: graph-updates (view in online documentation for working links) + /// + private uint penalty; +#endif + + /// + /// Graph which this node belongs to. + /// + /// If you know the node belongs to a particular graph type, you can cast it to that type: + /// + /// GraphNode node = ...; + /// GridGraph graph = node.Graph as GridGraph; + /// + /// + /// Will return null if the node has been destroyed. + /// + public NavGraph Graph { + get { + return Destroyed ? null : AstarData.GetGraph(this); + } + } + + /// Constructor for a graph node. + protected GraphNode (AstarPath astar) { + if (!System.Object.ReferenceEquals(astar, null)) { + this.nodeIndex = astar.GetNewNodeIndex(); + astar.InitializeNode(this); + } else { + throw new System.Exception("No active AstarPath object to bind to"); + } + } + + /// + /// Destroys the node. + /// Cleans up any temporary pathfinding data used for this node. + /// The graph is responsible for calling this method on nodes when they are destroyed, including when the whole graph is destoyed. + /// Otherwise memory leaks might present themselves. + /// + /// Once called the property will return true and subsequent calls to this method will not do anything. + /// + /// Note: Assumes the current active AstarPath instance is the same one that created this node. + /// + /// Warning: Should only be called by graph classes on their own nodes + /// + public void Destroy () { + if (Destroyed) return; + + ClearConnections(true); + + if (AstarPath.active != null) { + AstarPath.active.DestroyNode(this); + } + NodeIndex = DestroyedNodeIndex; + } + + public bool Destroyed { + get { + return NodeIndex == DestroyedNodeIndex; + } + } + + // If anyone creates more than about 200 million nodes then things will not go so well, however at that point one will certainly have more pressing problems, such as having run out of RAM + const int NodeIndexMask = 0xFFFFFFF; + const int DestroyedNodeIndex = NodeIndexMask - 1; + const int TemporaryFlag1Mask = 0x10000000; + const int TemporaryFlag2Mask = 0x20000000; + + /// + /// Internal unique index. + /// Every node will get a unique index. + /// This index is not necessarily correlated with e.g the position of the node in the graph. + /// + public int NodeIndex { get { return nodeIndex & NodeIndexMask; } private set { nodeIndex = (nodeIndex & ~NodeIndexMask) | value; } } + + /// + /// Temporary flag for internal purposes. + /// May only be used in the Unity thread. Must be reset to false after every use. + /// + internal bool TemporaryFlag1 { get { return (nodeIndex & TemporaryFlag1Mask) != 0; } set { nodeIndex = (nodeIndex & ~TemporaryFlag1Mask) | (value ? TemporaryFlag1Mask : 0); } } + + /// + /// Temporary flag for internal purposes. + /// May only be used in the Unity thread. Must be reset to false after every use. + /// + internal bool TemporaryFlag2 { get { return (nodeIndex & TemporaryFlag2Mask) != 0; } set { nodeIndex = (nodeIndex & ~TemporaryFlag2Mask) | (value ? TemporaryFlag2Mask : 0); } } + + /// + /// Position of the node in world space. + /// Note: The position is stored as an Int3, not a Vector3. + /// You can convert an Int3 to a Vector3 using an explicit conversion. + /// var v3 = (Vector3)node.position; + /// + public Int3 position; + + #region Constants + /// Position of the walkable bit. See: + const int FlagsWalkableOffset = 0; + /// Mask of the walkable bit. See: + const uint FlagsWalkableMask = 1 << FlagsWalkableOffset; + + /// Start of hierarchical node index bits. See: + const int FlagsHierarchicalIndexOffset = 1; + /// Mask of hierarchical node index bits. See: + const uint HierarchicalIndexMask = (131072-1) << FlagsHierarchicalIndexOffset; + + /// Start of bits. See: + const int HierarchicalDirtyOffset = 18; + + /// Mask of the bit. See: + const uint HierarchicalDirtyMask = 1 << HierarchicalDirtyOffset; + + /// Start of graph index bits. See: + const int FlagsGraphOffset = 24; + /// Mask of graph index bits. See: + const uint FlagsGraphMask = (256u-1) << FlagsGraphOffset; + + public const uint MaxHierarchicalNodeIndex = HierarchicalIndexMask >> FlagsHierarchicalIndexOffset; + + /// Max number of graphs-1 + public const uint MaxGraphIndex = FlagsGraphMask >> FlagsGraphOffset; + + /// Start of tag bits. See: + const int FlagsTagOffset = 19; + /// Mask of tag bits. See: + const uint FlagsTagMask = (32-1) << FlagsTagOffset; + + #endregion + + #region Properties + + /// + /// Holds various bitpacked variables. + /// + /// Bit 0: + /// Bits 1 through 17: + /// Bit 18: + /// Bits 19 through 23: + /// Bits 24 through 31: + /// + /// Warning: You should pretty much never modify this property directly. Use the other properties instead. + /// + public uint Flags { + get { + return flags; + } + set { + flags = value; + } + } + + /// + /// Penalty cost for walking on this node. + /// This can be used to make it harder/slower to walk over certain nodes. + /// A cost of 1000 () corresponds to the cost of moving 1 world unit. + /// + /// See: graph-updates (view in online documentation for working links) + /// + public uint Penalty { +#if !ASTAR_NO_PENALTY + get { + return penalty; + } + set { + if (value > 0xFFFFFF) + Debug.LogWarning("Very high penalty applied. Are you sure negative values haven't underflowed?\n" + + "Penalty values this high could with long paths cause overflows and in some cases infinity loops because of that.\n" + + "Penalty value applied: "+value); + penalty = value; + } +#else + get { return 0U; } + set {} +#endif + } + + /// + /// True if the node is traversable. + /// + /// See: graph-updates (view in online documentation for working links) + /// + public bool Walkable { + get { + return (flags & FlagsWalkableMask) != 0; + } + set { + flags = flags & ~FlagsWalkableMask | (value ? 1U : 0U) << FlagsWalkableOffset; + AstarPath.active.hierarchicalGraph.AddDirtyNode(this); + } + } + + /// + /// Hierarchical Node that contains this node. + /// The graph is divided into clusters of small hierarchical nodes in which there is a path from every node to every other node. + /// This structure is used to speed up connected component calculations which is used to quickly determine if a node is reachable from another node. + /// + /// See: + /// + /// Warning: This is an internal property and you should most likely not change it. + /// + internal int HierarchicalNodeIndex { + get { + return (int)((flags & HierarchicalIndexMask) >> FlagsHierarchicalIndexOffset); + } + set { + flags = (flags & ~HierarchicalIndexMask) | (uint)(value << FlagsHierarchicalIndexOffset); + } + } + + /// Some internal bookkeeping + internal bool IsHierarchicalNodeDirty { + get { + return (flags & HierarchicalDirtyMask) != 0; + } + set { + flags = flags & ~HierarchicalDirtyMask | (value ? 1U : 0U) << HierarchicalDirtyOffset; + } + } + + /// + /// Connected component that contains the node. + /// This is visualized in the scene view as differently colored nodes (if the graph coloring mode is set to 'Areas'). + /// Each area represents a set of nodes such that there is no valid path between nodes of different colors. + /// + /// See: https://en.wikipedia.org/wiki/Connected_component_(graph_theory) + /// See: + /// + public uint Area { + get { + return AstarPath.active.hierarchicalGraph.GetConnectedComponent(HierarchicalNodeIndex); + } + } + + /// + /// Graph which contains this node. + /// See: + /// See: + /// + public uint GraphIndex { + get { + return (flags & FlagsGraphMask) >> FlagsGraphOffset; + } + set { + flags = flags & ~FlagsGraphMask | value << FlagsGraphOffset; + } + } + + /// + /// Node tag. + /// See: tags (view in online documentation for working links) + /// See: graph-updates (view in online documentation for working links) + /// + public uint Tag { + get { + return (flags & FlagsTagMask) >> FlagsTagOffset; + } + set { + flags = flags & ~FlagsTagMask | ((value << FlagsTagOffset) & FlagsTagMask); + } + } + + #endregion + + /// + /// Inform the system that the node's connectivity has changed. + /// This is used for recalculating the connected components of the graph. + /// + /// See: + /// + /// You must call this method if you change the connectivity or walkability of the node without going through the high level methods + /// such as the property or the method. For example if your manually change the array you need to call this method. + /// + public void SetConnectivityDirty () { + AstarPath.active.hierarchicalGraph.AddDirtyNode(this); + } + + /// + /// Recalculates a node's connection costs. + /// Deprecated: This method is deprecated because it never did anything, you can safely remove any calls to this method. + /// + [System.Obsolete("This method is deprecated because it never did anything, you can safely remove any calls to this method")] + public void RecalculateConnectionCosts () { + } + + public virtual void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) { + //Simple but slow default implementation + pathNode.UpdateG(path); + + handler.heap.Add(pathNode); + + GetConnections((GraphNode other) => { + PathNode otherPN = handler.GetPathNode(other); + if (otherPN.parent == pathNode && otherPN.pathID == handler.PathID) other.UpdateRecursiveG(path, otherPN, handler); + }); + } + + /// + /// Calls the delegate with all connections from this node. + /// + /// node.GetConnections(connectedTo => { + /// Debug.DrawLine((Vector3)node.position, (Vector3)connectedTo.position, Color.red); + /// }); + /// + /// + /// You can add all connected nodes to a list like this + /// + /// var connections = new List(); + /// node.GetConnections(connections.Add); + /// + /// + public abstract void GetConnections (System.Action action); + + /// + /// Add a connection from this node to the specified node. + /// If the connection already exists, the cost will simply be updated and + /// no extra connection added. + /// + /// Note: Only adds a one-way connection. Consider calling the same function on the other node + /// to get a two-way connection. + /// + /// + /// AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => { + /// // Connect two nodes + /// var node1 = AstarPath.active.GetNearest(transform.position, NNConstraint.None).node; + /// var node2 = AstarPath.active.GetNearest(transform.position + Vector3.right, NNConstraint.None).node; + /// var cost = (uint)(node2.position - node1.position).costMagnitude; + /// node1.AddConnection(node2, cost); + /// node2.AddConnection(node1, cost); + /// + /// node1.ContainsConnection(node2); // True + /// + /// node1.RemoveConnection(node2); + /// node2.RemoveConnection(node1); + /// })); + /// + /// + public abstract void AddConnection (GraphNode node, uint cost); + + /// + /// Removes any connection from this node to the specified node. + /// If no such connection exists, nothing will be done. + /// + /// Note: This only removes the connection from this node to the other node. + /// You may want to call the same function on the other node to remove its possible connection + /// to this node. + /// + /// + /// AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => { + /// // Connect two nodes + /// var node1 = AstarPath.active.GetNearest(transform.position, NNConstraint.None).node; + /// var node2 = AstarPath.active.GetNearest(transform.position + Vector3.right, NNConstraint.None).node; + /// var cost = (uint)(node2.position - node1.position).costMagnitude; + /// node1.AddConnection(node2, cost); + /// node2.AddConnection(node1, cost); + /// + /// node1.ContainsConnection(node2); // True + /// + /// node1.RemoveConnection(node2); + /// node2.RemoveConnection(node1); + /// })); + /// + /// + public abstract void RemoveConnection (GraphNode node); + + /// Remove all connections from this node. + /// if true, neighbours will be requested to remove connections to this node. + public abstract void ClearConnections (bool alsoReverse); + + /// + /// Checks if this node has a connection to the specified node. + /// + /// + /// AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => { + /// // Connect two nodes + /// var node1 = AstarPath.active.GetNearest(transform.position, NNConstraint.None).node; + /// var node2 = AstarPath.active.GetNearest(transform.position + Vector3.right, NNConstraint.None).node; + /// var cost = (uint)(node2.position - node1.position).costMagnitude; + /// node1.AddConnection(node2, cost); + /// node2.AddConnection(node1, cost); + /// + /// node1.ContainsConnection(node2); // True + /// + /// node1.RemoveConnection(node2); + /// node2.RemoveConnection(node1); + /// })); + /// + /// + public virtual bool ContainsConnection (GraphNode node) { + // Simple but slow default implementation + bool contains = false; + + GetConnections(neighbour => { + contains |= neighbour == node; + }); + return contains; + } + + /// + /// Add a portal from this node to the specified node. + /// This function should add a portal to the left and right lists which is connecting the two nodes (this and other). + /// + /// Returns: True if the call was deemed successful. False if some unknown case was encountered and no portal could be added. + /// If both calls to node1.GetPortal (node2,...) and node2.GetPortal (node1,...) return false, the funnel modifier will fall back to adding to the path + /// the positions of the node. + /// + /// The default implementation simply returns false. + /// + /// This function may add more than one portal if necessary. + /// + /// See: http://digestingduck.blogspot.se/2010/03/simple-stupid-funnel-algorithm.html + /// + /// The node which is on the other side of the portal (strictly speaking it does not actually have to be on the other side of the portal though). + /// List of portal points on the left side of the funnel + /// List of portal points on the right side of the funnel + /// If this is true, the call was made on a node with the other node as the node before this one in the path. + /// In this case you may choose to do nothing since a similar call will be made to the other node with this node referenced as other (but then with backwards = true). + /// You do not have to care about switching the left and right lists, that is done for you already. + public virtual bool GetPortal (GraphNode other, List left, List right, bool backwards) { + return false; + } + + /// + /// Open the node. + /// Used internally for the A* algorithm. + /// + public abstract void Open (Path path, PathNode pathNode, PathHandler handler); + + /// The surface area of the node in square world units + public virtual float SurfaceArea () { + return 0; + } + + /// + /// A random point on the surface of the node. + /// For point nodes and other nodes which do not have a surface, this will always return the position of the node. + /// + public virtual Vector3 RandomPointOnSurface () { + return (Vector3)position; + } + + /// + /// Hash code used for checking if the gizmos need to be updated. + /// Will change when the gizmos for the node might change. + /// + public virtual int GetGizmoHashCode () { + // Some hashing, the constants are just some arbitrary prime numbers. #flags contains the info for #Tag and #Walkable + return position.GetHashCode() ^ (19 * (int)Penalty) ^ (41 * (int)(flags & ~(HierarchicalIndexMask | HierarchicalDirtyMask))); + } + + /// Serialized the node data to a byte array + public virtual void SerializeNode (GraphSerializationContext ctx) { + //Write basic node data. + ctx.writer.Write(Penalty); + // Save all flags except the hierarchical node index and the dirty bit + ctx.writer.Write(Flags & ~(HierarchicalIndexMask | HierarchicalDirtyMask)); + } + + /// Deserializes the node data from a byte array + public virtual void DeserializeNode (GraphSerializationContext ctx) { + Penalty = ctx.reader.ReadUInt32(); + // Load all flags except the hierarchical node index and the dirty bit (they aren't saved in newer versions and older data should just be cleared) + // Note that the dirty bit needs to be preserved here because it may already be set (due to the node being created) + Flags = (ctx.reader.ReadUInt32() & ~(HierarchicalIndexMask | HierarchicalDirtyMask)) | (Flags & (HierarchicalIndexMask | HierarchicalDirtyMask)); + + // Set the correct graph index (which might have changed, e.g if loading additively) + GraphIndex = ctx.graphIndex; + } + + /// + /// Used to serialize references to other nodes e.g connections. + /// Use the GraphSerializationContext.GetNodeIdentifier and + /// GraphSerializationContext.GetNodeFromIdentifier methods + /// for serialization and deserialization respectively. + /// + /// Nodes must override this method and serialize their connections. + /// Graph generators do not need to call this method, it will be called automatically on all + /// nodes at the correct time by the serializer. + /// + public virtual void SerializeReferences (GraphSerializationContext ctx) { + } + + /// + /// Used to deserialize references to other nodes e.g connections. + /// Use the GraphSerializationContext.GetNodeIdentifier and + /// GraphSerializationContext.GetNodeFromIdentifier methods + /// for serialization and deserialization respectively. + /// + /// Nodes must override this method and serialize their connections. + /// Graph generators do not need to call this method, it will be called automatically on all + /// nodes at the correct time by the serializer. + /// + public virtual void DeserializeReferences (GraphSerializationContext ctx) { + } + } + + public abstract class MeshNode : GraphNode { + protected MeshNode (AstarPath astar) : base(astar) { + } + + /// + /// All connections from this node. + /// See: + /// See: + /// + /// Note: If you modify this array or the contents of it you must call . + /// + public Connection[] connections; + + /// Get a vertex of this node. + /// vertex index. Must be between 0 and #GetVertexCount (exclusive). + public abstract Int3 GetVertex (int i); + + /// + /// Number of corner vertices that this node has. + /// For example for a triangle node this will return 3. + /// + public abstract int GetVertexCount (); + + /// Closest point on the surface of this node to the point p + public abstract Vector3 ClosestPointOnNode (Vector3 p); + + /// + /// Closest point on the surface of this node when seen from above. + /// This is usually very similar to but when the node is in a slope this can be significantly different. + /// [Open online documentation to see images] + /// When the blue point in the above image is used as an argument this method call will return the green point while the method will return the red point. + /// + public abstract Vector3 ClosestPointOnNodeXZ (Vector3 p); + + public override void ClearConnections (bool alsoReverse) { + // Remove all connections to this node from our neighbours + if (alsoReverse && connections != null) { + for (int i = 0; i < connections.Length; i++) { + // Null check done here because NavmeshTile.Destroy + // requires it for some optimizations it does + // Normally connection elements are never null + if (connections[i].node != null) { + connections[i].node.RemoveConnection(this); + } + } + } + + ArrayPool.Release (ref connections, true); + AstarPath.active.hierarchicalGraph.AddDirtyNode(this); + } + + public override void GetConnections (System.Action action) { + if (connections == null) return; + for (int i = 0; i < connections.Length; i++) action(connections[i].node); + } + + public override bool ContainsConnection (GraphNode node) { + for (int i = 0; i < connections.Length; i++) if (connections[i].node == node) return true; + return false; + } + + public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) { + pathNode.UpdateG(path); + + handler.heap.Add(pathNode); + + for (int i = 0; i < connections.Length; i++) { + GraphNode other = connections[i].node; + PathNode otherPN = handler.GetPathNode(other); + if (otherPN.parent == pathNode && otherPN.pathID == handler.PathID) { + other.UpdateRecursiveG(path, otherPN, handler); + } + } + } + + /// + /// Add a connection from this node to the specified node. + /// + /// If the connection already exists, the cost will simply be updated and + /// no extra connection added. + /// + /// Note: Only adds a one-way connection. Consider calling the same function on the other node + /// to get a two-way connection. + /// + /// Node to add a connection to + /// Cost of traversing the connection. A cost of 1000 corresponds approximately to the cost of moving 1 world unit. + public override void AddConnection (GraphNode node, uint cost) { + AddConnection(node, cost, -1); + } + + /// + /// Add a connection from this node to the specified node. + /// See: Pathfinding.Connection.edge + /// + /// If the connection already exists, the cost will simply be updated and + /// no extra connection added. + /// + /// Note: Only adds a one-way connection. Consider calling the same function on the other node + /// to get a two-way connection. + /// + /// Node to add a connection to + /// Cost of traversing the connection. A cost of 1000 corresponds approximately to the cost of moving 1 world unit. + /// Which edge on the shape of this node to use or -1 if no edge is used. + public void AddConnection (GraphNode node, uint cost, int shapeEdge) { + if (node == null) throw new System.ArgumentNullException(); + + // Check if we already have a connection to the node + if (connections != null) { + for (int i = 0; i < connections.Length; i++) { + if (connections[i].node == node) { + // Just update the cost for the existing connection + connections[i].cost = cost; + // Update edge only if it was a definite edge, otherwise reuse the existing one + // This makes it possible to use the AddConnection(node,cost) overload to only update the cost + // without changing the edge which is required for backwards compatibility. + connections[i].shapeEdge = shapeEdge >= 0 ? (byte)shapeEdge : connections[i].shapeEdge; + return; + } + } + } + + // Create new arrays which include the new connection + int connLength = connections != null ? connections.Length : 0; + + var newconns = ArrayPool.ClaimWithExactLength (connLength+1); + for (int i = 0; i < connLength; i++) { + newconns[i] = connections[i]; + } + + newconns[connLength] = new Connection(node, cost, (byte)shapeEdge); + + if (connections != null) { + ArrayPool.Release (ref connections, true); + } + + connections = newconns; + AstarPath.active.hierarchicalGraph.AddDirtyNode(this); + } + + /// + /// Removes any connection from this node to the specified node. + /// If no such connection exists, nothing will be done. + /// + /// Note: This only removes the connection from this node to the other node. + /// You may want to call the same function on the other node to remove its eventual connection + /// to this node. + /// + public override void RemoveConnection (GraphNode node) { + if (connections == null) return; + + // Iterate through all connections and check if there are any to the node + for (int i = 0; i < connections.Length; i++) { + if (connections[i].node == node) { + // Create new arrays which have the specified node removed + int connLength = connections.Length; + + var newconns = ArrayPool.ClaimWithExactLength (connLength-1); + for (int j = 0; j < i; j++) { + newconns[j] = connections[j]; + } + for (int j = i+1; j < connLength; j++) { + newconns[j-1] = connections[j]; + } + + if (connections != null) { + ArrayPool.Release (ref connections, true); + } + + connections = newconns; + AstarPath.active.hierarchicalGraph.AddDirtyNode(this); + return; + } + } + } + + /// Checks if point is inside the node when seen from above + public virtual bool ContainsPoint (Int3 point) { + return ContainsPoint((Vector3)point); + } + + /// + /// Checks if point is inside the node when seen from above. + /// + /// Note that is faster than this method as it avoids + /// some coordinate transformations. If you are repeatedly calling this method + /// on many different nodes but with the same point then you should consider + /// transforming the point first and then calling ContainsPointInGraphSpace. + /// + /// Int3 p = (Int3)graph.transform.InverseTransform(point); + /// + /// node.ContainsPointInGraphSpace(p); + /// + /// + public abstract bool ContainsPoint (Vector3 point); + + /// + /// Checks if point is inside the node in graph space. + /// + /// In graph space the up direction is always the Y axis so in principle + /// we project the triangle down on the XZ plane and check if the point is inside the 2D triangle there. + /// + public abstract bool ContainsPointInGraphSpace (Int3 point); + + public override int GetGizmoHashCode () { + var hash = base.GetGizmoHashCode(); + + if (connections != null) { + for (int i = 0; i < connections.Length; i++) { + hash ^= 17 * connections[i].GetHashCode(); + } + } + return hash; + } + + public override void SerializeReferences (GraphSerializationContext ctx) { + if (connections == null) { + ctx.writer.Write(-1); + } else { + ctx.writer.Write(connections.Length); + for (int i = 0; i < connections.Length; i++) { + ctx.SerializeNodeReference(connections[i].node); + ctx.writer.Write(connections[i].cost); + ctx.writer.Write(connections[i].shapeEdge); + } + } + } + + public override void DeserializeReferences (GraphSerializationContext ctx) { + int count = ctx.reader.ReadInt32(); + + if (count == -1) { + connections = null; + } else { + connections = ArrayPool.ClaimWithExactLength (count); + + for (int i = 0; i < count; i++) { + connections[i] = new Connection( + ctx.DeserializeNodeReference(), + ctx.reader.ReadUInt32(), + ctx.meta.version < AstarSerializer.V4_1_0 ? (byte)0xFF : ctx.reader.ReadByte() + ); + } + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Nodes/GraphNode.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Nodes/GraphNode.cs.meta new file mode 100644 index 0000000..b6bd9a4 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Nodes/GraphNode.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a660e0d66fb3b49d19d438de51ac6744 +timeCreated: 1486987492 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Path.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Path.cs new file mode 100644 index 0000000..3cfae46 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Path.cs @@ -0,0 +1,840 @@ +//#define ASTAR_POOL_DEBUG //@SHOWINEDITOR Enables debugging of path pooling. Will log warnings and info messages about paths not beeing pooled correctly. + +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace Pathfinding { + /// + /// Provides additional traversal information to a path request. + /// See: turnbased (view in online documentation for working links) + /// + public interface ITraversalProvider { + bool CanTraverse (Path path, GraphNode node); + uint GetTraversalCost (Path path, GraphNode node); + } + + /// Convenience class to access the default implementation of the ITraversalProvider + public static class DefaultITraversalProvider { + public static bool CanTraverse (Path path, GraphNode node) { + return node.Walkable && (path.enabledTags >> (int)node.Tag & 0x1) != 0; + } + + public static uint GetTraversalCost (Path path, GraphNode node) { + return path.GetTagPenalty((int)node.Tag) + node.Penalty; + } + } + + /// Base class for all path types + public abstract class Path : IPathInternals { +#if ASTAR_POOL_DEBUG + private string pathTraceInfo = ""; + private List claimInfo = new List(); + ~Path() { + Debug.Log("Destroying " + GetType().Name + " instance"); + if (claimed.Count > 0) { + Debug.LogWarning("Pool Is Leaking. See list of claims:\n" + + "Each message below will list what objects are currently claiming the path." + + " These objects have removed their reference to the path object but has not called .Release on it (which is bad).\n" + pathTraceInfo+"\n"); + for (int i = 0; i < claimed.Count; i++) { + Debug.LogWarning("- Claim "+ (i+1) + " is by a " + claimed[i].GetType().Name + "\n"+claimInfo[i]); + } + } else { + Debug.Log("Some scripts are not using pooling.\n" + pathTraceInfo + "\n"); + } + } +#endif + + /// Data for the thread calculating this path + protected PathHandler pathHandler; + + /// + /// Callback to call when the path is complete. + /// This is usually sent to the Seeker component which post processes the path and then calls a callback to the script which requested the path + /// + public OnPathDelegate callback; + + /// + /// Immediate callback to call when the path is complete. + /// Warning: This may be called from a separate thread. Usually you do not want to use this one. + /// + /// See: callback + /// + public OnPathDelegate immediateCallback; + + /// Returns the state of the path in the pathfinding pipeline + public PathState PipelineState { get; private set; } + System.Object stateLock = new object(); + + /// + /// Provides additional traversal information to a path request. + /// See: turnbased (view in online documentation for working links) + /// + public ITraversalProvider traversalProvider; + + + /// Backing field for + protected PathCompleteState completeState; + + /// + /// Current state of the path. + /// \bug This may currently be set to Complete before the path has actually been fully calculated. In particular the vectorPath and path lists may not have been fully constructed. + /// This can lead to race conditions when using multithreading. Try to avoid using this method to check for if the path is calculated right now, use instead. + /// + public PathCompleteState CompleteState { + get { return completeState; } + protected set { + // Locking is used to avoid multithreading race conditions + // in which the error state is set on the main thread to cancel the path and then a pathfinding thread marks the path as + // completed which would replace the error state (if a lock and check would not have been used). + lock (stateLock) { + // Once the path is put in the error state, it cannot be set to any other state + if (completeState != PathCompleteState.Error) completeState = value; + } + } + } + + /// + /// If the path failed, this is true. + /// See: + /// See: This is equivalent to checking path.CompleteState == PathCompleteState.Error + /// + public bool error { get { return CompleteState == PathCompleteState.Error; } } + + /// + /// Additional info on why a path failed. + /// See: + /// + public string errorLog { get; private set; } + + /// + /// Holds the path as a Node array. All nodes the path traverses. + /// This may not be the same nodes as the post processed path traverses. + /// + public List path; + + /// Holds the (possibly post processed) path as a Vector3 list + public List vectorPath; + + /// The node currently being processed + protected PathNode currentR; + + /// How long it took to calculate this path in milliseconds + public float duration; + + /// Number of nodes this path has searched + internal int searchedNodes; + + /// + /// True if the path is currently pooled. + /// Do not set this value. Only read. It is used internally. + /// + /// See: PathPool + /// Version: Was named 'recycled' in 3.7.5 and earlier. + /// + bool IPathInternals.Pooled { get; set; } + + /// + /// True if the path is currently recycled (i.e in the path pool). + /// Do not set this value. Only read. It is used internally. + /// + /// Deprecated: Has been renamed to 'pooled' to use more widely underestood terminology + /// + [System.Obsolete("Has been renamed to 'Pooled' to use more widely underestood terminology", true)] + internal bool recycled { get { return false; } } + + /// + /// True if the Reset function has been called. + /// Used to alert users when they are doing something wrong. + /// + protected bool hasBeenReset; + + /// Constraint for how to search for nodes + public NNConstraint nnConstraint = PathNNConstraint.Default; + + /// + /// Internal linked list implementation. + /// Warning: This is used internally by the system. You should never change this. + /// + internal Path next; + + /// Determines which heuristic to use + public Heuristic heuristic; + + /// + /// Scale of the heuristic values. + /// See: AstarPath.heuristicScale + /// + public float heuristicScale = 1F; + + /// ID of this path. Used to distinguish between different paths + public ushort pathID { get; private set; } + + /// Target to use for H score calculation. Used alongside . + protected GraphNode hTargetNode; + + /// Target to use for H score calculations. See: Pathfinding.Node.H + protected Int3 hTarget; + + /// + /// Which graph tags are traversable. + /// This is a bitmask so -1 = all bits set = all tags traversable. + /// For example, to set bit 5 to true, you would do + /// myPath.enabledTags |= 1 << 5; + /// To set it to false, you would do + /// myPath.enabledTags &= ~(1 << 5); + /// + /// The Seeker has a popup field where you can set which tags to use. + /// Note: If you are using a Seeker. The Seeker will set this value to what is set in the inspector field on StartPath. + /// So you need to change the Seeker value via script, not set this value if you want to change it via script. + /// + /// See: CanTraverse + /// + public int enabledTags = -1; + + /// List of zeroes to use as default tag penalties + static readonly int[] ZeroTagPenalties = new int[32]; + + /// + /// The tag penalties that are actually used. + /// If manualTagPenalties is null, this will be ZeroTagPenalties + /// See: tagPenalties + /// + protected int[] internalTagPenalties; + + /// + /// Tag penalties set by other scripts + /// See: tagPenalties + /// + protected int[] manualTagPenalties; + + /// + /// Penalties for each tag. + /// Tag 0 which is the default tag, will have added a penalty of tagPenalties[0]. + /// These should only be positive values since the A* algorithm cannot handle negative penalties. + /// + /// When assigning an array to this property it must have a length of 32. + /// + /// Note: Setting this to null, or trying to assign an array which does not have a length of 32, will make all tag penalties be treated as if they are zero. + /// + /// Note: If you are using a Seeker. The Seeker will set this value to what is set in the inspector field when you call seeker.StartPath. + /// So you need to change the Seeker's value via script, not set this value. + /// + /// See: Seeker.tagPenalties + /// + public int[] tagPenalties { + get { + return manualTagPenalties; + } + set { + if (value == null || value.Length != 32) { + manualTagPenalties = null; + internalTagPenalties = ZeroTagPenalties; + } else { + manualTagPenalties = value; + internalTagPenalties = value; + } + } + } + + /// + /// True for paths that want to search all nodes and not jump over nodes as optimizations. + /// This disables Jump Point Search when that is enabled to prevent e.g ConstantPath and FloodPath + /// to become completely useless. + /// + internal virtual bool FloodingPath { + get { + return false; + } + } + + /// + /// Total Length of the path. + /// Calculates the total length of the . + /// Cache this rather than call this function every time since it will calculate the length every time, not just return a cached value. + /// Returns: Total length of , if is null positive infinity is returned. + /// + public float GetTotalLength () { + if (vectorPath == null) return float.PositiveInfinity; + float tot = 0; + for (int i = 0; i < vectorPath.Count-1; i++) tot += Vector3.Distance(vectorPath[i], vectorPath[i+1]); + return tot; + } + + /// + /// Waits until this path has been calculated and returned. + /// Allows for very easy scripting. + /// + /// IEnumerator Start () { + /// var path = seeker.StartPath(transform.position, transform.position + transform.forward*10, null); + /// yield return StartCoroutine(path.WaitForPath()); + /// // The path is calculated now + /// } + /// + /// + /// Note: Do not confuse this with AstarPath.WaitForPath. This one will wait using yield until it has been calculated + /// while AstarPath.WaitForPath will halt all operations until the path has been calculated. + /// + /// \throws System.InvalidOperationException if the path is not started. Send the path to Seeker.StartPath or AstarPath.StartPath before calling this function. + /// + /// See: + /// See: https://docs.unity3d.com/Manual/Coroutines.html + /// + public IEnumerator WaitForPath () { + if (PipelineState == PathState.Created) throw new System.InvalidOperationException("This path has not been started yet"); + + while (PipelineState != PathState.Returned) yield return null; + } + + /// + /// Blocks until this path has been calculated and returned. + /// Normally it takes a few frames for a path to be calculated and returned. + /// This function will ensure that the path will be calculated when this function returns + /// and that the callback for that path has been called. + /// + /// Use this function only if you really need to. + /// There is a point to spreading path calculations out over several frames. + /// It smoothes out the framerate and makes sure requesting a large + /// number of paths at the same time does not cause lag. + /// + /// Note: Graph updates and other callbacks might get called during the execution of this function. + /// + /// + /// Path p = seeker.StartPath (transform.position, transform.position + Vector3.forward * 10); + /// p.BlockUntilCalculated(); + /// // The path is calculated now + /// + /// + /// See: This is equivalent to calling AstarPath.BlockUntilCalculated(Path) + /// See: WaitForPath + /// + public void BlockUntilCalculated () { + AstarPath.BlockUntilCalculated(this); + } + + /// + /// Estimated cost from the specified node to the target. + /// See: https://en.wikipedia.org/wiki/A*_search_algorithm + /// + internal uint CalculateHScore (GraphNode node) { + uint h; + + switch (heuristic) { + case Heuristic.Euclidean: + h = (uint)(((GetHTarget() - node.position).costMagnitude)*heuristicScale); + return h; + case Heuristic.Manhattan: + Int3 p2 = node.position; + h = (uint)((System.Math.Abs(hTarget.x-p2.x) + System.Math.Abs(hTarget.y-p2.y) + System.Math.Abs(hTarget.z-p2.z))*heuristicScale); + return h; + case Heuristic.DiagonalManhattan: + Int3 p = GetHTarget() - node.position; + p.x = System.Math.Abs(p.x); + p.y = System.Math.Abs(p.y); + p.z = System.Math.Abs(p.z); + int diag = System.Math.Min(p.x, p.z); + int diag2 = System.Math.Max(p.x, p.z); + h = (uint)((((14*diag)/10) + (diag2-diag) + p.y) * heuristicScale); + return h; + } + return 0U; + } + + /// Returns penalty for the given tag. + /// A value between 0 (inclusive) and 32 (exclusive). + public uint GetTagPenalty (int tag) { + return (uint)internalTagPenalties[tag]; + } + + internal Int3 GetHTarget () { + return hTarget; + } + + /// + /// Returns if the node can be traversed. + /// This per default equals to if the node is walkable and if the node's tag is included in + /// + internal bool CanTraverse (GraphNode node) { + // Use traversal provider if set, otherwise fall back on default behaviour + // This method is hot, but this branch is extremely well predicted so it + // doesn't affect performance much (profiling indicates it is just above + // the noise level, somewhere around 0%-0.3%) + if (traversalProvider != null) + return traversalProvider.CanTraverse(this, node); + + unchecked { return node.Walkable && (enabledTags >> (int)node.Tag & 0x1) != 0; } + } + + internal uint GetTraversalCost (GraphNode node) { +#if ASTAR_NO_TRAVERSAL_COST + return 0; +#else + // Use traversal provider if set, otherwise fall back on default behaviour + if (traversalProvider != null) + return traversalProvider.GetTraversalCost(this, node); + + unchecked { return GetTagPenalty((int)node.Tag) + node.Penalty; } +#endif + } + + /// + /// May be called by graph nodes to get a special cost for some connections. + /// Nodes may call it when PathNode.flag2 is set to true, for example mesh nodes, which have + /// a very large area can be marked on the start and end nodes, this method will be called + /// to get the actual cost for moving from the start position to its neighbours instead + /// of as would otherwise be the case, from the start node's position to its neighbours. + /// The position of a node and the actual start point on the node can vary quite a lot. + /// + /// The default behaviour of this method is to return the previous cost of the connection, + /// essentiall making no change at all. + /// + /// This method should return the same regardless of the order of a and b. + /// That is f(a,b) == f(b,a) should hold. + /// + /// Moving from this node + /// Moving to this node + /// The cost of moving between the nodes. Return this value if there is no meaningful special cost to return. + internal virtual uint GetConnectionSpecialCost (GraphNode a, GraphNode b, uint currentCost) { + return currentCost; + } + + /// + /// Returns if this path is done calculating. + /// + /// Note: The callback for the path might not have been called yet. + /// + /// \since Added in 3.0.8 + /// + /// See: which also takes into account if the %path %callback has been called and had modifiers applied. + /// + public bool IsDone () { + return PipelineState > PathState.Processing; + } + + /// Threadsafe increment of the state + void IPathInternals.AdvanceState (PathState s) { + lock (stateLock) { + PipelineState = (PathState)System.Math.Max((int)PipelineState, (int)s); + } + } + + /// + /// Returns the state of the path in the pathfinding pipeline. + /// Deprecated: Use the property instead + /// + [System.Obsolete("Use the 'PipelineState' property instead")] + public PathState GetState () { + return PipelineState; + } + + /// Causes the path to fail and sets to msg + internal void FailWithError (string msg) { + Error(); + if (errorLog != "") errorLog += "\n" + msg; + else errorLog = msg; + } + + /// + /// Logs an error. + /// Deprecated: Use instead + /// + [System.Obsolete("Use FailWithError instead")] + internal void LogError (string msg) { + Log(msg); + } + + /// + /// Appends a message to the . + /// Nothing is logged to the console. + /// + /// Note: If AstarPath.logPathResults is PathLog.None and this is a standalone player, nothing will be logged as an optimization. + /// + /// Deprecated: Use instead + /// + [System.Obsolete("Use FailWithError instead")] + internal void Log (string msg) { + errorLog += msg; + } + + /// + /// Aborts the path because of an error. + /// Sets to true. + /// This function is called when an error has occurred (e.g a valid path could not be found). + /// See: + /// + public void Error () { + CompleteState = PathCompleteState.Error; + } + + /// + /// Performs some error checking. + /// Makes sure the user isn't using old code paths and that no major errors have been made. + /// + /// Causes the path to fail if any errors are found. + /// + private void ErrorCheck () { + if (!hasBeenReset) FailWithError("Please use the static Construct function for creating paths, do not use the normal constructors."); + if (((IPathInternals)this).Pooled) FailWithError("The path is currently in a path pool. Are you sending the path for calculation twice?"); + if (pathHandler == null) FailWithError("Field pathHandler is not set. Please report this bug."); + if (PipelineState > PathState.Processing) FailWithError("This path has already been processed. Do not request a path with the same path object twice."); + } + + /// + /// Called when the path enters the pool. + /// This method should release e.g pooled lists and other pooled resources + /// The base version of this method releases vectorPath and path lists. + /// Reset() will be called after this function, not before. + /// Warning: Do not call this function manually. + /// + protected virtual void OnEnterPool () { + if (vectorPath != null) Pathfinding.Util.ListPool.Release (ref vectorPath); + if (path != null) Pathfinding.Util.ListPool.Release (ref path); + // Clear the callback to remove a potential memory leak + // while the path is in the pool (which it could be for a long time). + callback = null; + immediateCallback = null; + traversalProvider = null; + } + + /// + /// Reset all values to their default values. + /// + /// Note: All inheriting path types (e.g ConstantPath, RandomPath, etc.) which declare their own variables need to + /// override this function, resetting ALL their variables to enable pooling of paths. + /// If this is not done, trying to use that path type for pooling could result in weird behaviour. + /// The best way is to reset to default values the variables declared in the extended path type and then + /// call the base function in inheriting types with base.Reset(). + /// + protected virtual void Reset () { +#if ASTAR_POOL_DEBUG + pathTraceInfo = "This path was got from the pool or created from here (stacktrace):\n"; + pathTraceInfo += System.Environment.StackTrace; +#endif + + if (System.Object.ReferenceEquals(AstarPath.active, null)) + throw new System.NullReferenceException("No AstarPath object found in the scene. " + + "Make sure there is one or do not create paths in Awake"); + + hasBeenReset = true; + PipelineState = (int)PathState.Created; + releasedNotSilent = false; + + pathHandler = null; + callback = null; + immediateCallback = null; + errorLog = ""; + completeState = PathCompleteState.NotCalculated; + + path = Pathfinding.Util.ListPool.Claim (); + vectorPath = Pathfinding.Util.ListPool.Claim (); + + currentR = null; + + duration = 0; + searchedNodes = 0; + + nnConstraint = PathNNConstraint.Default; + next = null; + + heuristic = AstarPath.active.heuristic; + heuristicScale = AstarPath.active.heuristicScale; + + enabledTags = -1; + tagPenalties = null; + + pathID = AstarPath.active.GetNextPathID(); + + hTarget = Int3.zero; + hTargetNode = null; + + traversalProvider = null; + } + + /// List of claims on this path with reference objects + private List claimed = new List(); + + /// + /// True if the path has been released with a non-silent call yet. + /// + /// See: Release + /// See: Claim + /// + private bool releasedNotSilent; + + /// + /// Claim this path (pooling). + /// A claim on a path will ensure that it is not pooled. + /// If you are using a path, you will want to claim it when you first get it and then release it when you will not + /// use it anymore. When there are no claims on the path, it will be reset and put in a pool. + /// + /// This is essentially just reference counting. + /// + /// The object passed to this method is merely used as a way to more easily detect when pooling is not done correctly. + /// It can be any object, when used from a movement script you can just pass "this". This class will throw an exception + /// if you try to call Claim on the same path twice with the same object (which is usually not what you want) or + /// if you try to call Release with an object that has not been used in a Claim call for that path. + /// The object passed to the Claim method needs to be the same as the one you pass to this method. + /// + /// See: Release + /// See: Pool + /// See: pooling (view in online documentation for working links) + /// See: https://en.wikipedia.org/wiki/Reference_counting + /// + public void Claim (System.Object o) { + if (System.Object.ReferenceEquals(o, null)) throw new System.ArgumentNullException("o"); + + for (int i = 0; i < claimed.Count; i++) { + // Need to use ReferenceEquals because it might be called from another thread + if (System.Object.ReferenceEquals(claimed[i], o)) + throw new System.ArgumentException("You have already claimed the path with that object ("+o+"). Are you claiming the path with the same object twice?"); + } + + claimed.Add(o); +#if ASTAR_POOL_DEBUG + claimInfo.Add(o.ToString() + "\n\nClaimed from:\n" + System.Environment.StackTrace); +#endif + } + + /// + /// Releases the path silently (pooling). + /// Deprecated: Use Release(o, true) instead + /// + [System.Obsolete("Use Release(o, true) instead")] + internal void ReleaseSilent (System.Object o) { + Release(o, true); + } + + /// + /// Releases a path claim (pooling). + /// Removes the claim of the path by the specified object. + /// When the claim count reaches zero, the path will be pooled, all variables will be cleared and the path will be put in a pool to be used again. + /// This is great for performance since fewer allocations are made. + /// + /// If the silent parameter is true, this method will remove the claim by the specified object + /// but the path will not be pooled if the claim count reches zero unless a Release call (not silent) has been made earlier. + /// This is used by the internal pathfinding components such as Seeker and AstarPath so that they will not cause paths to be pooled. + /// This enables users to skip the claim/release calls if they want without the path being pooled by the Seeker or AstarPath and + /// thus causing strange bugs. + /// + /// See: Claim + /// See: PathPool + /// + public void Release (System.Object o, bool silent = false) { + if (o == null) throw new System.ArgumentNullException("o"); + + for (int i = 0; i < claimed.Count; i++) { + // Need to use ReferenceEquals because it might be called from another thread + if (System.Object.ReferenceEquals(claimed[i], o)) { + claimed.RemoveAt(i); +#if ASTAR_POOL_DEBUG + claimInfo.RemoveAt(i); +#endif + if (!silent) { + releasedNotSilent = true; + } + + if (claimed.Count == 0 && releasedNotSilent) { + PathPool.Pool(this); + } + return; + } + } + if (claimed.Count == 0) { + throw new System.ArgumentException("You are releasing a path which is not claimed at all (most likely it has been pooled already). " + + "Are you releasing the path with the same object ("+o+") twice?" + + "\nCheck out the documentation on path pooling for help."); + } + throw new System.ArgumentException("You are releasing a path which has not been claimed with this object ("+o+"). " + + "Are you releasing the path with the same object twice?\n" + + "Check out the documentation on path pooling for help."); + } + + /// + /// Traces the calculated path from the end node to the start. + /// This will build an array ( of the nodes this path will pass through and also set the array to the arrays positions. + /// Assumes the and are empty and not null (which will be the case for a correctly initialized path). + /// + protected virtual void Trace (PathNode from) { + // Current node we are processing + PathNode c = from; + int count = 0; + + while (c != null) { + c = c.parent; + count++; + if (count > 2048) { + Debug.LogWarning("Infinite loop? >2048 node path. Remove this message if you really have that long paths (Path.cs, Trace method)"); + break; + } + } + + // Ensure capacities for lists + AstarProfiler.StartProfile("Check List Capacities"); + + if (path.Capacity < count) path.Capacity = count; + if (vectorPath.Capacity < count) vectorPath.Capacity = count; + + AstarProfiler.EndProfile(); + + c = from; + + for (int i = 0; i < count; i++) { + path.Add(c.node); + c = c.parent; + } + + // Reverse + int half = count/2; + for (int i = 0; i < half; i++) { + var tmp = path[i]; + path[i] = path[count-i-1]; + path[count - i - 1] = tmp; + } + + for (int i = 0; i < count; i++) { + vectorPath.Add((Vector3)path[i].position); + } + } + + /// + /// Writes text shared for all overrides of DebugString to the string builder. + /// See: DebugString + /// + protected void DebugStringPrefix (PathLog logMode, System.Text.StringBuilder text) { + text.Append(error ? "Path Failed : " : "Path Completed : "); + text.Append("Computation Time "); + text.Append(duration.ToString(logMode == PathLog.Heavy ? "0.000 ms " : "0.00 ms ")); + + text.Append("Searched Nodes ").Append(searchedNodes); + + if (!error) { + text.Append(" Path Length "); + text.Append(path == null ? "Null" : path.Count.ToString()); + } + } + + /// + /// Writes text shared for all overrides of DebugString to the string builder. + /// See: DebugString + /// + protected void DebugStringSuffix (PathLog logMode, System.Text.StringBuilder text) { + if (error) { + text.Append("\nError: ").Append(errorLog); + } + + // Can only print this from the Unity thread + // since otherwise an exception might be thrown + if (logMode == PathLog.Heavy && !AstarPath.active.IsUsingMultithreading) { + text.Append("\nCallback references "); + if (callback != null) text.Append(callback.Target.GetType().FullName).AppendLine(); + else text.AppendLine("NULL"); + } + + text.Append("\nPath Number ").Append(pathID).Append(" (unique id)"); + } + + /// + /// Returns a string with information about it. + /// More information is emitted when logMode == Heavy. + /// An empty string is returned if logMode == None + /// or logMode == OnlyErrors and this path did not fail. + /// + internal virtual string DebugString (PathLog logMode) { + if (logMode == PathLog.None || (!error && logMode == PathLog.OnlyErrors)) { + return ""; + } + + // Get a cached string builder for this thread + System.Text.StringBuilder text = pathHandler.DebugStringBuilder; + text.Length = 0; + + DebugStringPrefix(logMode, text); + DebugStringSuffix(logMode, text); + + return text.ToString(); + } + + /// Calls callback to return the calculated path. See: + protected virtual void ReturnPath () { + if (callback != null) { + callback(this); + } + } + + /// + /// Prepares low level path variables for calculation. + /// Called before a path search will take place. + /// Always called before the Prepare, Initialize and CalculateStep functions + /// + protected void PrepareBase (PathHandler pathHandler) { + //Path IDs have overflowed 65K, cleanup is needed + //Since pathIDs are handed out sequentially, we can do this + if (pathHandler.PathID > pathID) { + pathHandler.ClearPathIDs(); + } + + //Make sure the path has a reference to the pathHandler + this.pathHandler = pathHandler; + //Assign relevant path data to the pathHandler + pathHandler.InitializeForPath(this); + + // Make sure that internalTagPenalties is an array which has the length 32 + if (internalTagPenalties == null || internalTagPenalties.Length != 32) + internalTagPenalties = ZeroTagPenalties; + + try { + ErrorCheck(); + } catch (System.Exception e) { + FailWithError(e.Message); + } + } + + /// + /// Called before the path is started. + /// Called right before Initialize + /// + protected abstract void Prepare (); + + /// + /// Always called after the path has been calculated. + /// Guaranteed to be called before other paths have been calculated on + /// the same thread. + /// Use for cleaning up things like node tagging and similar. + /// + protected virtual void Cleanup () {} + + /// + /// Initializes the path. + /// Sets up the open list and adds the first node to it + /// + protected abstract void Initialize (); + + /// Calculates the until it is complete or the time has progressed past targetTick + protected abstract void CalculateStep (long targetTick); + + PathHandler IPathInternals.PathHandler { get { return pathHandler; } } + void IPathInternals.OnEnterPool () { OnEnterPool(); } + void IPathInternals.Reset () { Reset(); } + void IPathInternals.ReturnPath () { ReturnPath(); } + void IPathInternals.PrepareBase (PathHandler handler) { PrepareBase(handler); } + void IPathInternals.Prepare () { Prepare(); } + void IPathInternals.Cleanup () { Cleanup(); } + void IPathInternals.Initialize () { Initialize(); } + void IPathInternals.CalculateStep (long targetTick) { CalculateStep(targetTick); } + } + + /// Used for hiding internal methods of the Path class + internal interface IPathInternals { + PathHandler PathHandler { get; } + bool Pooled { get; set; } + void AdvanceState (PathState s); + void OnEnterPool (); + void Reset (); + void ReturnPath (); + void PrepareBase (PathHandler handler); + void Prepare (); + void Initialize (); + void Cleanup (); + void CalculateStep (long targetTick); + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Path.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Path.cs.meta new file mode 100644 index 0000000..17cffcf --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Path.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b179046d83ec84f0c91efc5335f78a30 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/PathHandler.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/PathHandler.cs new file mode 100644 index 0000000..07c1f98 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/PathHandler.cs @@ -0,0 +1,209 @@ +#define DECREASE_KEY +using System.Collections.Generic; + +namespace Pathfinding { + /// + /// Stores temporary node data for a single pathfinding request. + /// Every node has one PathNode per thread used. + /// It stores e.g G score, H score and other temporary variables needed + /// for path calculation, but which are not part of the graph structure. + /// + /// See: Pathfinding.PathHandler + /// See: https://en.wikipedia.org/wiki/A*_search_algorithm + /// + public class PathNode { + /// Reference to the actual graph node + public GraphNode node; + + /// Parent node in the search tree + public PathNode parent; + + /// The path request (in this thread, if multithreading is used) which last used this node + public ushort pathID; + +#if DECREASE_KEY + /// + /// Index of the node in the binary heap. + /// The open list in the A* algorithm is backed by a binary heap. + /// To support fast 'decrease key' operations, the index of the node + /// is saved here. + /// + public ushort heapIndex = BinaryHeap.NotInHeap; +#endif + + /// Bitpacked variable which stores several fields + private uint flags; + + /// Cost uses the first 28 bits + private const uint CostMask = (1U << 28) - 1U; + + /// Flag 1 is at bit 28 + private const int Flag1Offset = 28; + private const uint Flag1Mask = (uint)(1 << Flag1Offset); + + /// Flag 2 is at bit 29 + private const int Flag2Offset = 29; + private const uint Flag2Mask = (uint)(1 << Flag2Offset); + + public uint cost { + get { + return flags & CostMask; + } + set { + flags = (flags & ~CostMask) | value; + } + } + + /// + /// Use as temporary flag during pathfinding. + /// Pathfinders (only) can use this during pathfinding to mark + /// nodes. When done, this flag should be reverted to its default state (false) to + /// avoid messing up other pathfinding requests. + /// + public bool flag1 { + get { + return (flags & Flag1Mask) != 0; + } + set { + flags = (flags & ~Flag1Mask) | (value ? Flag1Mask : 0U); + } + } + + /// + /// Use as temporary flag during pathfinding. + /// Pathfinders (only) can use this during pathfinding to mark + /// nodes. When done, this flag should be reverted to its default state (false) to + /// avoid messing up other pathfinding requests. + /// + public bool flag2 { + get { + return (flags & Flag2Mask) != 0; + } + set { + flags = (flags & ~Flag2Mask) | (value ? Flag2Mask : 0U); + } + } + + /// Backing field for the G score + private uint g; + + /// Backing field for the H score + private uint h; + + /// G score, cost to get to this node + public uint G { get { return g; } set { g = value; } } + + /// H score, estimated cost to get to to the target + public uint H { get { return h; } set { h = value; } } + + /// F score. H score + G score + public uint F { get { return g+h; } } + + public void UpdateG (Path path) { +#if ASTAR_NO_TRAVERSAL_COST + g = parent.g + cost; +#else + g = parent.g + cost + path.GetTraversalCost(node); +#endif + } + } + + /// Handles thread specific path data. + public class PathHandler { + /// + /// Current PathID. + /// See: + /// + private ushort pathID; + + public readonly int threadID; + public readonly int totalThreadCount; + + /// + /// Binary heap to keep track of nodes on the "Open list". + /// See: https://en.wikipedia.org/wiki/A*_search_algorithm + /// + public readonly BinaryHeap heap = new BinaryHeap(128); + + /// ID for the path currently being calculated or last path that was calculated + public ushort PathID { get { return pathID; } } + + /// Array of all PathNodes + public PathNode[] nodes = new PathNode[0]; + + /// + /// StringBuilder that paths can use to build debug strings. + /// Better for performance and memory usage to use a single StringBuilder instead of each path creating its own + /// + public readonly System.Text.StringBuilder DebugStringBuilder = new System.Text.StringBuilder(); + + public PathHandler (int threadID, int totalThreadCount) { + this.threadID = threadID; + this.totalThreadCount = totalThreadCount; + } + + public void InitializeForPath (Path p) { + pathID = p.pathID; + heap.Clear(); + } + + /// Internal method to clean up node data + public void DestroyNode (GraphNode node) { + PathNode pn = GetPathNode(node); + + // Clean up references to help the GC + pn.node = null; + pn.parent = null; + // This is not required for pathfinding, but not clearing it may confuse gizmo drawing for a fraction of a second. + // Especially when 'Show Search Tree' is enabled + pn.pathID = 0; + pn.G = 0; + pn.H = 0; + } + + /// Internal method to initialize node data + public void InitializeNode (GraphNode node) { + //Get the index of the node + int ind = node.NodeIndex; + + if (ind >= nodes.Length) { + // Grow by a factor of 2 + PathNode[] newNodes = new PathNode[System.Math.Max(128, nodes.Length*2)]; + nodes.CopyTo(newNodes, 0); + // Initialize all PathNode instances at once + // It is important that we do this here and don't for example leave the entries as NULL and initialize + // them lazily. By allocating them all at once we are much more likely to allocate the PathNodes close + // to each other in memory (most systems use some kind of bumb-allocator) and this improves cache locality + // and reduces false sharing (which would happen if we allocated PathNodes for the different threads close + // to each other). This has been profiled to give around a 4% difference in overall pathfinding performance. + for (int i = nodes.Length; i < newNodes.Length; i++) newNodes[i] = new PathNode(); + nodes = newNodes; + } + + nodes[ind].node = node; + } + + public PathNode GetPathNode (int nodeIndex) { + return nodes[nodeIndex]; + } + + /// + /// Returns the PathNode corresponding to the specified node. + /// The PathNode is specific to this PathHandler since multiple PathHandlers + /// are used at the same time if multithreading is enabled. + /// + public PathNode GetPathNode (GraphNode node) { + return nodes[node.NodeIndex]; + } + + /// + /// Set all nodes' pathIDs to 0. + /// See: Pathfinding.PathNode.pathID + /// + public void ClearPathIDs () { + for (int i = 0; i < nodes.Length; i++) { + if (nodes[i] != null) nodes[i].pathID = 0; + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/PathHandler.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/PathHandler.cs.meta new file mode 100644 index 0000000..b197ba3 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/PathHandler.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7cc885607b0534cc6a4a34c4caa58d89 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization.meta new file mode 100644 index 0000000..568309f --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fb135586fe44a402c8ded728d001a26c diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonConverters.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonConverters.cs new file mode 100644 index 0000000..3b6ed49 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonConverters.cs @@ -0,0 +1,4 @@ + +// This file has been removed from the project. Since UnityPackages cannot +// delete files, only replace them, this message is left here to prevent old +// files from causing compiler errors diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonConverters.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonConverters.cs.meta new file mode 100644 index 0000000..94fda85 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonConverters.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e736879b230a341c59bfcaed0bec3ac3 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonSerializer.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonSerializer.cs new file mode 100644 index 0000000..695f778 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonSerializer.cs @@ -0,0 +1,909 @@ +using System; +using System.IO; +using System.Collections.Generic; +using UnityEngine; +using Pathfinding.Util; +using Pathfinding.WindowsStore; + +#if ASTAR_NO_ZIP +using Pathfinding.Serialization.Zip; +#elif NETFX_CORE +// For Universal Windows Platform +using ZipEntry = System.IO.Compression.ZipArchiveEntry; +using ZipFile = System.IO.Compression.ZipArchive; +#else +using Pathfinding.Ionic.Zip; +#endif + +namespace Pathfinding.Serialization { + /// Holds information passed to custom graph serializers + public class GraphSerializationContext { + private readonly GraphNode[] id2NodeMapping; + + /// + /// Deserialization stream. + /// Will only be set when deserializing + /// + public readonly BinaryReader reader; + + /// + /// Serialization stream. + /// Will only be set when serializing + /// + public readonly BinaryWriter writer; + + /// + /// Index of the graph which is currently being processed. + /// Version: uint instead of int after 3.7.5 + /// + public readonly uint graphIndex; + + /// Metadata about graphs being deserialized + public readonly GraphMeta meta; + + public GraphSerializationContext (BinaryReader reader, GraphNode[] id2NodeMapping, uint graphIndex, GraphMeta meta) { + this.reader = reader; + this.id2NodeMapping = id2NodeMapping; + this.graphIndex = graphIndex; + this.meta = meta; + } + + public GraphSerializationContext (BinaryWriter writer) { + this.writer = writer; + } + + public void SerializeNodeReference (GraphNode node) { + writer.Write(node == null ? -1 : node.NodeIndex); + } + + public GraphNode DeserializeNodeReference () { + var id = reader.ReadInt32(); + + if (id2NodeMapping == null) throw new Exception("Calling DeserializeNodeReference when not deserializing node references"); + + if (id == -1) return null; + GraphNode node = id2NodeMapping[id]; + if (node == null) throw new Exception("Invalid id ("+id+")"); + return node; + } + + /// Write a Vector3 + public void SerializeVector3 (Vector3 v) { + writer.Write(v.x); + writer.Write(v.y); + writer.Write(v.z); + } + + /// Read a Vector3 + public Vector3 DeserializeVector3 () { + return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); + } + + /// Write an Int3 + public void SerializeInt3 (Int3 v) { + writer.Write(v.x); + writer.Write(v.y); + writer.Write(v.z); + } + + /// Read an Int3 + public Int3 DeserializeInt3 () { + return new Int3(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); + } + + public int DeserializeInt (int defaultValue) { + if (reader.BaseStream.Position <= reader.BaseStream.Length-4) { + return reader.ReadInt32(); + } else { + return defaultValue; + } + } + + public float DeserializeFloat (float defaultValue) { + if (reader.BaseStream.Position <= reader.BaseStream.Length-4) { + return reader.ReadSingle(); + } else { + return defaultValue; + } + } + + /// Read a UnityEngine.Object + public UnityEngine.Object DeserializeUnityObject ( ) { + int inst = reader.ReadInt32(); + + if (inst == int.MaxValue) { + return null; + } + + string name = reader.ReadString(); + string typename = reader.ReadString(); + string guid = reader.ReadString(); + + System.Type type = System.Type.GetType(typename); + + if (type == null) { + Debug.LogError("Could not find type '"+typename+"'. Cannot deserialize Unity reference"); + return null; + } + + if (!string.IsNullOrEmpty(guid)) { + UnityReferenceHelper[] helpers = UnityEngine.Object.FindObjectsOfType(typeof(UnityReferenceHelper)) as UnityReferenceHelper[]; + + for (int i = 0; i < helpers.Length; i++) { + if (helpers[i].GetGUID() == guid) { + if (type == typeof(GameObject)) { + return helpers[i].gameObject; + } else { + return helpers[i].GetComponent(type); + } + } + } + } + + //Try to load from resources + UnityEngine.Object[] objs = Resources.LoadAll(name, type); + + for (int i = 0; i < objs.Length; i++) { + if (objs[i].name == name || objs.Length == 1) { + return objs[i]; + } + } + + return null; + } + } + + /// + /// Handles low level serialization and deserialization of graph settings and data. + /// Mostly for internal use. You can use the methods in the AstarData class for + /// higher level serialization and deserialization. + /// + /// See: AstarData + /// + public class AstarSerializer { + private AstarData data; + + /// Zip which the data is loaded from + private ZipFile zip; + + /// Memory stream with the zip data + private MemoryStream zipStream; + + /// Graph metadata + private GraphMeta meta; + + /// Settings for serialization + private SerializeSettings settings; + + /// Graphs that are being serialized or deserialized + private NavGraph[] graphs; + + /// + /// Index used for the graph in the file. + /// If some graphs were null in the file then graphIndexInZip[graphs[i]] may not equal i. + /// Used for deserialization. + /// + private Dictionary graphIndexInZip; + + private int graphIndexOffset; + + /// Extension to use for binary files + const string binaryExt = ".binary"; + + /// Extension to use for json files + const string jsonExt = ".json"; + + /// + /// Checksum for the serialized data. + /// Used to provide a quick equality check in editor code + /// + private uint checksum = 0xffffffff; + + System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); + + /// Cached StringBuilder to avoid excessive allocations + static System.Text.StringBuilder _stringBuilder = new System.Text.StringBuilder(); + + /// + /// Returns a cached StringBuilder. + /// This function only has one string builder cached and should + /// thus only be called from a single thread and should not be called while using an earlier got string builder. + /// + static System.Text.StringBuilder GetStringBuilder () { _stringBuilder.Length = 0; return _stringBuilder; } + + /// Cached version object for 3.8.3 + public static readonly System.Version V3_8_3 = new System.Version(3, 8, 3); + + /// Cached version object for 3.9.0 + public static readonly System.Version V3_9_0 = new System.Version(3, 9, 0); + + /// Cached version object for 4.1.0 + public static readonly System.Version V4_1_0 = new System.Version(4, 1, 0); + + public AstarSerializer (AstarData data) { + this.data = data; + settings = SerializeSettings.Settings; + } + + public AstarSerializer (AstarData data, SerializeSettings settings) { + this.data = data; + this.settings = settings; + } + + public void SetGraphIndexOffset (int offset) { + graphIndexOffset = offset; + } + + void AddChecksum (byte[] bytes) { + checksum = Checksum.GetChecksum(bytes, checksum); + } + + void AddEntry (string name, byte[] bytes) { +#if NETFX_CORE + var entry = zip.CreateEntry(name); + using (var stream = entry.Open()) { + stream.Write(bytes, 0, bytes.Length); + } +#else + zip.AddEntry(name, bytes); +#endif + } + + public uint GetChecksum () { return checksum; } + + #region Serialize + + public void OpenSerialize () { + // Create a new zip file, here we will store all the data + zipStream = new MemoryStream(); +#if NETFX_CORE + zip = new ZipFile(zipStream, System.IO.Compression.ZipArchiveMode.Create); +#else + zip = new ZipFile(); + zip.AlternateEncoding = System.Text.Encoding.UTF8; + zip.AlternateEncodingUsage = ZipOption.Always; +#endif + meta = new GraphMeta(); + } + + public byte[] CloseSerialize () { + // As the last step, serialize metadata + byte[] bytes = SerializeMeta(); + AddChecksum(bytes); + AddEntry("meta"+jsonExt, bytes); + +#if !ASTAR_NO_ZIP && !NETFX_CORE + // Set dummy dates on every file to prevent the binary data to change + // for identical settings and graphs. + // Prevents the scene from being marked as dirty in the editor + // If ASTAR_NO_ZIP is defined this is not relevant since the replacement zip + // implementation does not even store dates + var dummy = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + foreach (var entry in zip.Entries) { + entry.AccessedTime = dummy; + entry.CreationTime = dummy; + entry.LastModified = dummy; + entry.ModifiedTime = dummy; + } +#endif + + // Save all entries to a single byte array +#if !NETFX_CORE + zip.Save(zipStream); +#endif + zip.Dispose(); + bytes = zipStream.ToArray(); + + zip = null; + zipStream = null; + return bytes; + } + + public void SerializeGraphs (NavGraph[] _graphs) { + if (graphs != null) throw new InvalidOperationException("Cannot serialize graphs multiple times."); + graphs = _graphs; + + if (zip == null) throw new NullReferenceException("You must not call CloseSerialize before a call to this function"); + + if (graphs == null) graphs = new NavGraph[0]; + + for (int i = 0; i < graphs.Length; i++) { + //Ignore graph if null + if (graphs[i] == null) continue; + + // Serialize the graph to a byte array + byte[] bytes = Serialize(graphs[i]); + + AddChecksum(bytes); + AddEntry("graph"+i+jsonExt, bytes); + } + } + + /// Serialize metadata about all graphs + byte[] SerializeMeta () { + if (graphs == null) throw new System.Exception("No call to SerializeGraphs has been done"); + + meta.version = AstarPath.Version; + meta.graphs = graphs.Length; + meta.guids = new List(); + meta.typeNames = new List(); + + // For each graph, save the guid + // of the graph and the type of it + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] != null) { + meta.guids.Add(graphs[i].guid.ToString()); + meta.typeNames.Add(graphs[i].GetType().FullName); + } else { + meta.guids.Add(null); + meta.typeNames.Add(null); + } + } + + // Grab a cached string builder to avoid allocations + var output = GetStringBuilder(); + TinyJsonSerializer.Serialize(meta, output); + return encoding.GetBytes(output.ToString()); + } + + /// Serializes the graph settings to JSON and returns the data + public byte[] Serialize (NavGraph graph) { + // Grab a cached string builder to avoid allocations + var output = GetStringBuilder(); + + TinyJsonSerializer.Serialize(graph, output); + return encoding.GetBytes(output.ToString()); + } + + /// + /// Deprecated method to serialize node data. + /// Deprecated: Not used anymore + /// + [System.Obsolete("Not used anymore. You can safely remove the call to this function.")] + public void SerializeNodes () { + } + + static int GetMaxNodeIndexInAllGraphs (NavGraph[] graphs) { + int maxIndex = 0; + + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] == null) continue; + graphs[i].GetNodes(node => { + maxIndex = Math.Max(node.NodeIndex, maxIndex); + if (node.NodeIndex == -1) { + Debug.LogError("Graph contains destroyed nodes. This is a bug."); + } + }); + } + return maxIndex; + } + + static byte[] SerializeNodeIndices (NavGraph[] graphs) { + var stream = new MemoryStream(); + var writer = new BinaryWriter(stream); + + int maxNodeIndex = GetMaxNodeIndexInAllGraphs(graphs); + + writer.Write(maxNodeIndex); + + // While writing node indices, verify that the max node index is the same + // (user written graphs might have gotten it wrong) + int maxNodeIndex2 = 0; + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] == null) continue; + graphs[i].GetNodes(node => { + maxNodeIndex2 = Math.Max(node.NodeIndex, maxNodeIndex2); + writer.Write(node.NodeIndex); + }); + } + + // Nice to verify if users are writing their own graph types + if (maxNodeIndex2 != maxNodeIndex) throw new Exception("Some graphs are not consistent in their GetNodes calls, sequential calls give different results."); + + byte[] bytes = stream.ToArray(); + writer.Close(); + + return bytes; + } + + /// Serializes info returned by NavGraph.SerializeExtraInfo + static byte[] SerializeGraphExtraInfo (NavGraph graph) { + var stream = new MemoryStream(); + var writer = new BinaryWriter(stream); + var ctx = new GraphSerializationContext(writer); + + ((IGraphInternals)graph).SerializeExtraInfo(ctx); + byte[] bytes = stream.ToArray(); + writer.Close(); + + return bytes; + } + + /// + /// Used to serialize references to other nodes e.g connections. + /// Nodes use the GraphSerializationContext.GetNodeIdentifier and + /// GraphSerializationContext.GetNodeFromIdentifier methods + /// for serialization and deserialization respectively. + /// + static byte[] SerializeGraphNodeReferences (NavGraph graph) { + var stream = new MemoryStream(); + var writer = new BinaryWriter(stream); + var ctx = new GraphSerializationContext(writer); + + graph.GetNodes(node => node.SerializeReferences(ctx)); + writer.Close(); + + var bytes = stream.ToArray(); + return bytes; + } + + public void SerializeExtraInfo () { + if (!settings.nodes) return; + if (graphs == null) throw new InvalidOperationException("Cannot serialize extra info with no serialized graphs (call SerializeGraphs first)"); + + var bytes = SerializeNodeIndices(graphs); + AddChecksum(bytes); + AddEntry("graph_references"+binaryExt, bytes); + + for (int i = 0; i < graphs.Length; i++) { + if (graphs[i] == null) continue; + + bytes = SerializeGraphExtraInfo(graphs[i]); + AddChecksum(bytes); + AddEntry("graph"+i+"_extra"+binaryExt, bytes); + + bytes = SerializeGraphNodeReferences(graphs[i]); + AddChecksum(bytes); + AddEntry("graph"+i+"_references"+binaryExt, bytes); + } + + bytes = SerializeNodeLinks(); + AddChecksum(bytes); + AddEntry("node_link2" + binaryExt, bytes); + } + + byte[] SerializeNodeLinks () { + var stream = new MemoryStream(); + +#if !ASTAR_NO_LINKS + var writer = new BinaryWriter(stream); + var ctx = new GraphSerializationContext(writer); + NodeLink2.SerializeReferences(ctx); +#endif + return stream.ToArray(); + } + + #endregion + + #region Deserialize + + ZipEntry GetEntry (string name) { +#if NETFX_CORE + return zip.GetEntry(name); +#else + return zip[name]; +#endif + } + + bool ContainsEntry (string name) { + return GetEntry(name) != null; + } + + public bool OpenDeserialize (byte[] bytes) { + // Copy the bytes to a stream + zipStream = new MemoryStream(); + zipStream.Write(bytes, 0, bytes.Length); + zipStream.Position = 0; + try { +#if NETFX_CORE + zip = new ZipFile(zipStream); +#else + zip = ZipFile.Read(zipStream); +#endif + } catch (Exception e) { + // Catches exceptions when an invalid zip file is found + Debug.LogError("Caught exception when loading from zip\n"+e); + + zipStream.Dispose(); + return false; + } + + if (ContainsEntry("meta" + jsonExt)) { + meta = DeserializeMeta(GetEntry("meta" + jsonExt)); + } else if (ContainsEntry("meta" + binaryExt)) { + meta = DeserializeBinaryMeta(GetEntry("meta" + binaryExt)); + } else { + throw new Exception("No metadata found in serialized data."); + } + + if (FullyDefinedVersion(meta.version) > FullyDefinedVersion(AstarPath.Version)) { + Debug.LogWarning("Trying to load data from a newer version of the A* Pathfinding Project\nCurrent version: "+AstarPath.Version+" Data version: "+meta.version + + "\nThis is usually fine as the stored data is usually backwards and forwards compatible." + + "\nHowever node data (not settings) can get corrupted between versions (even though I try my best to keep compatibility), so it is recommended " + + "to recalculate any caches (those for faster startup) and resave any files. Even if it seems to load fine, it might cause subtle bugs.\n"); + } else if (FullyDefinedVersion(meta.version) < FullyDefinedVersion(AstarPath.Version)) { + Debug.LogWarning("Upgrading serialized pathfinding data from version " + meta.version + " to " + AstarPath.Version + + "\nThis is usually fine, it just means you have upgraded to a new version." + + "\nHowever node data (not settings) can get corrupted between versions (even though I try my best to keep compatibility), so it is recommended " + + "to recalculate any caches (those for faster startup) and resave any files. Even if it seems to load fine, it might cause subtle bugs.\n"); + } + return true; + } + + /// + /// Returns a version with all fields fully defined. + /// This is used because by default new Version(3,0,0) > new Version(3,0). + /// This is not the desired behaviour so we make sure that all fields are defined here + /// + static System.Version FullyDefinedVersion (System.Version v) { + return new System.Version(Mathf.Max(v.Major, 0), Mathf.Max(v.Minor, 0), Mathf.Max(v.Build, 0), Mathf.Max(v.Revision, 0)); + } + + public void CloseDeserialize () { + zipStream.Dispose(); + zip.Dispose(); + zip = null; + zipStream = null; + } + + NavGraph DeserializeGraph (int zipIndex, int graphIndex, System.Type[] availableGraphTypes) { + // Get the graph type from the metadata we deserialized earlier + var graphType = meta.GetGraphType(zipIndex, availableGraphTypes); + + // Graph was null when saving, ignore + if (System.Type.Equals(graphType, null)) return null; + + // Create a new graph of the right type + NavGraph graph = data.CreateGraph(graphType); + graph.graphIndex = (uint)(graphIndex); + + var jsonName = "graph" + zipIndex + jsonExt; + var binName = "graph" + zipIndex + binaryExt; + + if (ContainsEntry(jsonName)) { + // Read the graph settings + TinyJsonDeserializer.Deserialize(GetString(GetEntry(jsonName)), graphType, graph); + } else if (ContainsEntry(binName)) { + var reader = GetBinaryReader(GetEntry(binName)); + var ctx = new GraphSerializationContext(reader, null, graph.graphIndex, meta); + ((IGraphInternals)graph).DeserializeSettingsCompatibility(ctx); + } else { + throw new FileNotFoundException("Could not find data for graph " + zipIndex + " in zip. Entry 'graph" + zipIndex + jsonExt + "' does not exist"); + } + + if (graph.guid.ToString() != meta.guids[zipIndex]) + throw new Exception("Guid in graph file not equal to guid defined in meta file. Have you edited the data manually?\n"+graph.guid+" != "+meta.guids[zipIndex]); + + return graph; + } + + /// + /// Deserializes graph settings. + /// Note: Stored in files named "graph" where # is the graph number. + /// + public NavGraph[] DeserializeGraphs (System.Type[] availableGraphTypes) { + // Allocate a list of graphs to be deserialized + var graphList = new List(); + + graphIndexInZip = new Dictionary(); + + for (int i = 0; i < meta.graphs; i++) { + var newIndex = graphList.Count + graphIndexOffset; + var graph = DeserializeGraph(i, newIndex, availableGraphTypes); + if (graph != null) { + graphList.Add(graph); + graphIndexInZip[graph] = i; + } + } + + graphs = graphList.ToArray(); + return graphs; + } + + bool DeserializeExtraInfo (NavGraph graph) { + var zipIndex = graphIndexInZip[graph]; + var entry = GetEntry("graph"+zipIndex+"_extra"+binaryExt); + + if (entry == null) + return false; + + var reader = GetBinaryReader(entry); + + var ctx = new GraphSerializationContext(reader, null, graph.graphIndex, meta); + + // Call the graph to process the data + ((IGraphInternals)graph).DeserializeExtraInfo(ctx); + return true; + } + + bool AnyDestroyedNodesInGraphs () { + bool result = false; + + for (int i = 0; i < graphs.Length; i++) { + graphs[i].GetNodes(node => { + if (node.Destroyed) { + result = true; + } + }); + } + return result; + } + + GraphNode[] DeserializeNodeReferenceMap () { + // Get the file containing the list of all node indices + // This is correlated with the new indices of the nodes and a mapping from old to new + // is done so that references can be resolved + var entry = GetEntry("graph_references"+binaryExt); + + if (entry == null) throw new Exception("Node references not found in the data. Was this loaded from an older version of the A* Pathfinding Project?"); + + var reader = GetBinaryReader(entry); + int maxNodeIndex = reader.ReadInt32(); + var int2Node = new GraphNode[maxNodeIndex+1]; + + try { + for (int i = 0; i < graphs.Length; i++) { + graphs[i].GetNodes(node => { + var index = reader.ReadInt32(); + int2Node[index] = node; + }); + } + } catch (Exception e) { + throw new Exception("Some graph(s) has thrown an exception during GetNodes, or some graph(s) have deserialized more or fewer nodes than were serialized", e); + } + +#if !NETFX_CORE + // For Windows Store apps the BaseStream.Position property is not supported + // so we have to disable this error check on that platform + if (reader.BaseStream.Position != reader.BaseStream.Length) { + throw new Exception((reader.BaseStream.Length / 4) + " nodes were serialized, but only data for " + (reader.BaseStream.Position / 4) + " nodes was found. The data looks corrupt."); + } +#endif + + reader.Close(); + return int2Node; + } + + void DeserializeNodeReferences (NavGraph graph, GraphNode[] int2Node) { + var zipIndex = graphIndexInZip[graph]; + var entry = GetEntry("graph"+zipIndex+"_references"+binaryExt); + + if (entry == null) throw new Exception("Node references for graph " + zipIndex + " not found in the data. Was this loaded from an older version of the A* Pathfinding Project?"); + + var reader = GetBinaryReader(entry); + var ctx = new GraphSerializationContext(reader, int2Node, graph.graphIndex, meta); + + graph.GetNodes(node => node.DeserializeReferences(ctx)); + } + + /// + /// Deserializes extra graph info. + /// Extra graph info is specified by the graph types. + /// See: Pathfinding.NavGraph.DeserializeExtraInfo + /// Note: Stored in files named "graph" where # is the graph number. + /// + public void DeserializeExtraInfo () { + bool anyDeserialized = false; + + // Loop through all graphs and deserialize the extra info + // if there is any such info in the zip file + for (int i = 0; i < graphs.Length; i++) { + anyDeserialized |= DeserializeExtraInfo(graphs[i]); + } + + if (!anyDeserialized) { + return; + } + + // Sanity check + // Make sure the graphs don't contain destroyed nodes + if (AnyDestroyedNodesInGraphs()) { + Debug.LogError("Graph contains destroyed nodes. This is a bug."); + } + + // Deserialize map from old node indices to new nodes + var int2Node = DeserializeNodeReferenceMap(); + + // Deserialize node references + for (int i = 0; i < graphs.Length; i++) { + DeserializeNodeReferences(graphs[i], int2Node); + } + + DeserializeNodeLinks(int2Node); + } + + void DeserializeNodeLinks (GraphNode[] int2Node) { +#if !ASTAR_NO_LINKS + var entry = GetEntry("node_link2"+binaryExt); + + if (entry == null) + return; + + var reader = GetBinaryReader(entry); + var ctx = new GraphSerializationContext(reader, int2Node, 0, meta); + NodeLink2.DeserializeReferences(ctx); +#endif + } + + /// Calls PostDeserialization on all loaded graphs + public void PostDeserialization () { + for (int i = 0; i < graphs.Length; i++) { + var ctx = new GraphSerializationContext(null, null, 0, meta); + ((IGraphInternals)graphs[i]).PostDeserialization(ctx); + } + } + + /// + /// Deserializes graph editor settings. + /// For future compatibility this method does not assume that the graphEditors array matches the array in order and/or count. + /// It searches for a matching graph (matching if graphEditor.target == graph) for every graph editor. + /// Multiple graph editors should not refer to the same graph.\n + /// Note: Stored in files named "graph" where # is the graph number. + /// + /// Note: This method is only used for compatibility, newer versions store everything in the graph.serializedEditorSettings field which is already serialized. + /// + public void DeserializeEditorSettingsCompatibility () { + for (int i = 0; i < graphs.Length; i++) { + var zipIndex = graphIndexInZip[graphs[i]]; + ZipEntry entry = GetEntry("graph"+zipIndex+"_editor"+jsonExt); + if (entry == null) continue; + + (graphs[i] as IGraphInternals).SerializedEditorSettings = GetString(entry); + } + } + + /// Returns a binary reader for the data in the zip entry + private static BinaryReader GetBinaryReader (ZipEntry entry) { +#if NETFX_CORE + return new BinaryReader(entry.Open()); +#else + var stream = new System.IO.MemoryStream(); + + entry.Extract(stream); + stream.Position = 0; + return new System.IO.BinaryReader(stream); +#endif + } + + /// Returns the data in the zip entry as a string + private static string GetString (ZipEntry entry) { +#if NETFX_CORE + var reader = new StreamReader(entry.Open()); +#else + var buffer = new MemoryStream(); + + entry.Extract(buffer); + buffer.Position = 0; + var reader = new StreamReader(buffer); +#endif + string s = reader.ReadToEnd(); + reader.Dispose(); + return s; + } + + private GraphMeta DeserializeMeta (ZipEntry entry) { + return TinyJsonDeserializer.Deserialize(GetString(entry), typeof(GraphMeta)) as GraphMeta; + } + + private GraphMeta DeserializeBinaryMeta (ZipEntry entry) { + var meta = new GraphMeta(); + + var reader = GetBinaryReader(entry); + + if (reader.ReadString() != "A*") throw new System.Exception("Invalid magic number in saved data"); + int major = reader.ReadInt32(); + int minor = reader.ReadInt32(); + int build = reader.ReadInt32(); + int revision = reader.ReadInt32(); + + // Required because when saving a version with a field not set, it will save it as -1 + // and then the Version constructor will throw an exception (which we do not want) + if (major < 0) meta.version = new Version(0, 0); + else if (minor < 0) meta.version = new Version(major, 0); + else if (build < 0) meta.version = new Version(major, minor); + else if (revision < 0) meta.version = new Version(major, minor, build); + else meta.version = new Version(major, minor, build, revision); + + meta.graphs = reader.ReadInt32(); + + meta.guids = new List(); + int count = reader.ReadInt32(); + for (int i = 0; i < count; i++) meta.guids.Add(reader.ReadString()); + + meta.typeNames = new List(); + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) meta.typeNames.Add(reader.ReadString()); + + return meta; + } + + + #endregion + + #region Utils + + /// Save the specified data at the specified path + public static void SaveToFile (string path, byte[] data) { +#if NETFX_CORE + throw new System.NotSupportedException("Cannot save to file on this platform"); +#else + using (var stream = new FileStream(path, FileMode.Create)) { + stream.Write(data, 0, data.Length); + } +#endif + } + + /// Load the specified data from the specified path + public static byte[] LoadFromFile (string path) { +#if NETFX_CORE + throw new System.NotSupportedException("Cannot load from file on this platform"); +#else + using (var stream = new FileStream(path, FileMode.Open)) { + var bytes = new byte[(int)stream.Length]; + stream.Read(bytes, 0, (int)stream.Length); + return bytes; + } +#endif + } + + #endregion + } + + /// Metadata for all graphs included in serialization + public class GraphMeta { + /// Project version it was saved with + public Version version; + + /// Number of graphs serialized + public int graphs; + + /// Guids for all graphs + public List guids; + + /// Type names for all graphs + public List typeNames; + + /// Returns the Type of graph number index + public Type GetGraphType (int index, System.Type[] availableGraphTypes) { + // The graph was null when saving. Ignore it + if (String.IsNullOrEmpty(typeNames[index])) return null; + + for (int j = 0; j < availableGraphTypes.Length; j++) { + if (availableGraphTypes[j].FullName == typeNames[index]) return availableGraphTypes[j]; + } + + throw new Exception("No graph of type '" + typeNames[index] + "' could be created, type does not exist"); + } + } + + /// Holds settings for how graphs should be serialized + public class SerializeSettings { + /// + /// Enable to include node data. + /// If false, only settings will be saved + /// + public bool nodes = true; + + /// + /// Use pretty printing for the json data. + /// Good if you want to open up the saved data and edit it manually + /// + [System.Obsolete("There is no support for pretty printing the json anymore")] + public bool prettyPrint; + + /// + /// Save editor settings. + /// Warning: Only applicable when saving from the editor using the AstarPathEditor methods + /// + public bool editorSettings; + + /// Serialization settings for only saving graph settings + public static SerializeSettings Settings { + get { + return new SerializeSettings { + nodes = false + }; + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonSerializer.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonSerializer.cs.meta new file mode 100644 index 0000000..4c4b58f --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/JsonSerializer.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: edd6db33319e94141bb849f4f58261c3 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleJsonReplacement.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleJsonReplacement.cs new file mode 100644 index 0000000..3b6ed49 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleJsonReplacement.cs @@ -0,0 +1,4 @@ + +// This file has been removed from the project. Since UnityPackages cannot +// delete files, only replace them, this message is left here to prevent old +// files from causing compiler errors diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleJsonReplacement.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleJsonReplacement.cs.meta new file mode 100644 index 0000000..997bebe --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleJsonReplacement.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f6973e76355ee420394e5f521d20e2cc +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleZipReplacement.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleZipReplacement.cs new file mode 100644 index 0000000..893a2d6 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleZipReplacement.cs @@ -0,0 +1,79 @@ +#if ASTAR_NO_ZIP +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace Pathfinding.Serialization.Zip { + public enum ZipOption { + Always + } + + public class ZipFile { + public System.Text.Encoding AlternateEncoding; + public ZipOption AlternateEncodingUsage = ZipOption.Always; + + Dictionary dict = new Dictionary(); + + public void AddEntry (string name, byte[] bytes) { + dict[name] = new ZipEntry(name, bytes); + } + + public bool ContainsEntry (string name) { + return dict.ContainsKey(name); + } + + public void Save (System.IO.Stream stream) { + var writer = new System.IO.BinaryWriter(stream); + + writer.Write(dict.Count); + foreach (KeyValuePair pair in dict) { + writer.Write(pair.Key); + writer.Write(pair.Value.bytes.Length); + writer.Write(pair.Value.bytes); + } + } + + public static ZipFile Read (System.IO.Stream stream) { + ZipFile file = new ZipFile(); + + var reader = new System.IO.BinaryReader(stream); + int count = reader.ReadInt32(); + + for (int i = 0; i < count; i++) { + var name = reader.ReadString(); + var length = reader.ReadInt32(); + var bytes = reader.ReadBytes(length); + + file.dict[name] = new ZipEntry(name, bytes); + } + + return file; + } + + public ZipEntry this[string index] { + get { + ZipEntry v; + dict.TryGetValue(index, out v); + return v; + } + } + + public void Dispose () { + } + } + + public class ZipEntry { + internal string name; + internal byte[] bytes; + + public ZipEntry (string name, byte[] bytes) { + this.name = name; + this.bytes = bytes; + } + + public void Extract (System.IO.Stream stream) { + stream.Write(bytes, 0, bytes.Length); + } + } +} +#endif diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleZipReplacement.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleZipReplacement.cs.meta new file mode 100644 index 0000000..55b4b94 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/SimpleZipReplacement.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e4ab3c0aea7564a9c9af3bf72ed95858 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/TinyJson.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/TinyJson.cs new file mode 100644 index 0000000..e0c21a5 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/TinyJson.cs @@ -0,0 +1,447 @@ +using UnityEngine; +using System.Collections.Generic; +using Pathfinding.WindowsStore; +using System; +#if NETFX_CORE +using System.Linq; +using WinRTLegacy; +#endif + +namespace Pathfinding.Serialization { + public class JsonMemberAttribute : System.Attribute { + } + public class JsonOptInAttribute : System.Attribute { + } + + /// + /// A very tiny json serializer. + /// It is not supposed to have lots of features, it is only intended to be able to serialize graph settings + /// well enough. + /// + public class TinyJsonSerializer { + System.Text.StringBuilder output = new System.Text.StringBuilder(); + + Dictionary > serializers = new Dictionary >(); + + static readonly System.Globalization.CultureInfo invariantCulture = System.Globalization.CultureInfo.InvariantCulture; + + public static void Serialize (System.Object obj, System.Text.StringBuilder output) { + new TinyJsonSerializer() { + output = output + }.Serialize(obj); + } + + TinyJsonSerializer () { + serializers[typeof(float)] = v => output.Append(((float)v).ToString("R", invariantCulture)); + serializers[typeof(bool)] = v => output.Append((bool)v ? "true" : "false"); + serializers[typeof(Version)] = serializers[typeof(uint)] = serializers[typeof(int)] = v => output.Append(v.ToString()); + serializers[typeof(string)] = v => output.AppendFormat("\"{0}\"", v.ToString().Replace("\"", "\\\"")); + serializers[typeof(Vector2)] = v => output.AppendFormat("{{ \"x\": {0}, \"y\": {1} }}", ((Vector2)v).x.ToString("R", invariantCulture), ((Vector2)v).y.ToString("R", invariantCulture)); + serializers[typeof(Vector3)] = v => output.AppendFormat("{{ \"x\": {0}, \"y\": {1}, \"z\": {2} }}", ((Vector3)v).x.ToString("R", invariantCulture), ((Vector3)v).y.ToString("R", invariantCulture), ((Vector3)v).z.ToString("R", invariantCulture)); + serializers[typeof(Pathfinding.Util.Guid)] = v => output.AppendFormat("{{ \"value\": \"{0}\" }}", v.ToString()); + serializers[typeof(LayerMask)] = v => output.AppendFormat("{{ \"value\": {0} }}", ((int)(LayerMask)v).ToString()); + } + + void Serialize (System.Object obj) { + if (obj == null) { + output.Append("null"); + return; + } + + var type = obj.GetType(); + var typeInfo = WindowsStoreCompatibility.GetTypeInfo(type); + if (serializers.ContainsKey(type)) { + serializers[type] (obj); + } else if (typeInfo.IsEnum) { + output.Append('"' + obj.ToString() + '"'); + } else if (obj is System.Collections.IList) { + output.Append("["); + var arr = obj as System.Collections.IList; + for (int i = 0; i < arr.Count; i++) { + if (i != 0) + output.Append(", "); + Serialize(arr[i]); + } + output.Append("]"); + } else if (obj is UnityEngine.Object) { + SerializeUnityObject(obj as UnityEngine.Object); + } else { +#if NETFX_CORE + var optIn = typeInfo.CustomAttributes.Any(attr => attr.GetType() == typeof(JsonOptInAttribute)); +#else + var optIn = typeInfo.GetCustomAttributes(typeof(JsonOptInAttribute), true).Length > 0; +#endif + output.Append("{"); + bool earlier = false; + + while (true) { +#if NETFX_CORE + var fields = typeInfo.DeclaredFields.Where(f => !f.IsStatic).ToArray(); +#else + var fields = type.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); +#endif + foreach (var field in fields) { + if (field.DeclaringType != type) continue; + if ((!optIn && field.IsPublic) || +#if NETFX_CORE + field.CustomAttributes.Any(attr => attr.GetType() == typeof(JsonMemberAttribute)) +#else + field.GetCustomAttributes(typeof(JsonMemberAttribute), true).Length > 0 +#endif + ) { + if (earlier) { + output.Append(", "); + } + + earlier = true; + output.AppendFormat("\"{0}\": ", field.Name); + Serialize(field.GetValue(obj)); + } + } + +#if NETFX_CORE + typeInfo = typeInfo.BaseType; + if (typeInfo == null) break; +#else + type = type.BaseType; + if (type == null) break; +#endif + } + output.Append("}"); + } + } + + void QuotedField (string name, string contents) { + output.AppendFormat("\"{0}\": \"{1}\"", name, contents); + } + + void SerializeUnityObject (UnityEngine.Object obj) { + // Note that a unityengine can be destroyed as well + if (obj == null) { + Serialize(null); + return; + } + + output.Append("{"); + var path = obj.name; +#if UNITY_EDITOR + // Figure out the path of the object relative to a Resources folder. + // In a standalone player this cannot be done unfortunately, so we will assume it is at the top level in the Resources folder. + // Fortunately it should be extremely rare to have to serialize references to unity objects in a standalone player. + var realPath = UnityEditor.AssetDatabase.GetAssetPath(obj); + var match = System.Text.RegularExpressions.Regex.Match(realPath, @"Resources/(.*?)(\.\w+)?$"); + if (match != null) path = match.Groups[1].Value; +#endif + QuotedField("Name", path); + output.Append(", "); + QuotedField("Type", obj.GetType().FullName); + + //Write scene path if the object is a Component or GameObject + var component = obj as Component; + var go = obj as GameObject; + + if (component != null || go != null) { + if (component != null && go == null) { + go = component.gameObject; + } + + var helper = go.GetComponent(); + + if (helper == null) { + Debug.Log("Adding UnityReferenceHelper to Unity Reference '"+obj.name+"'"); + helper = go.AddComponent(); + } + + //Make sure it has a unique GUID + helper.Reset(); + output.Append(", "); + QuotedField("GUID", helper.GetGUID().ToString()); + } + output.Append("}"); + } + } + + /// + /// A very tiny json deserializer. + /// It is not supposed to have lots of features, it is only intended to be able to deserialize graph settings + /// well enough. Not much validation of the input is done. + /// + public class TinyJsonDeserializer { + System.IO.TextReader reader; + + static readonly System.Globalization.NumberFormatInfo numberFormat = System.Globalization.NumberFormatInfo.InvariantInfo; + + /// + /// Deserializes an object of the specified type. + /// Will load all fields into the populate object if it is set (only works for classes). + /// + public static System.Object Deserialize (string text, Type type, System.Object populate = null) { + return new TinyJsonDeserializer() { + reader = new System.IO.StringReader(text) + }.Deserialize(type, populate); + } + + /// + /// Deserializes an object of type tp. + /// Will load all fields into the populate object if it is set (only works for classes). + /// + System.Object Deserialize (Type tp, System.Object populate = null) { + var tpInfo = WindowsStoreCompatibility.GetTypeInfo(tp); + + if (tpInfo.IsEnum) { + return Enum.Parse(tp, EatField()); + } else if (TryEat('n')) { + Eat("ull"); + TryEat(','); + return null; + } else if (Type.Equals(tp, typeof(float))) { + return float.Parse(EatField(), numberFormat); + } else if (Type.Equals(tp, typeof(int))) { + return int.Parse(EatField(), numberFormat); + } else if (Type.Equals(tp, typeof(uint))) { + return uint.Parse(EatField(), numberFormat); + } else if (Type.Equals(tp, typeof(bool))) { + return bool.Parse(EatField()); + } else if (Type.Equals(tp, typeof(string))) { + return EatField(); + } else if (Type.Equals(tp, typeof(Version))) { + return new Version(EatField()); + } else if (Type.Equals(tp, typeof(Vector2))) { + Eat("{"); + var result = new Vector2(); + EatField(); + result.x = float.Parse(EatField(), numberFormat); + EatField(); + result.y = float.Parse(EatField(), numberFormat); + Eat("}"); + return result; + } else if (Type.Equals(tp, typeof(Vector3))) { + Eat("{"); + var result = new Vector3(); + EatField(); + result.x = float.Parse(EatField(), numberFormat); + EatField(); + result.y = float.Parse(EatField(), numberFormat); + EatField(); + result.z = float.Parse(EatField(), numberFormat); + Eat("}"); + return result; + } else if (Type.Equals(tp, typeof(Pathfinding.Util.Guid))) { + Eat("{"); + EatField(); + var result = Pathfinding.Util.Guid.Parse(EatField()); + Eat("}"); + return result; + } else if (Type.Equals(tp, typeof(LayerMask))) { + Eat("{"); + EatField(); + var result = (LayerMask)int.Parse(EatField()); + Eat("}"); + return result; + } else if (Type.Equals(tp, typeof(List))) { + System.Collections.IList result = new List(); + + Eat("["); + while (!TryEat(']')) { + result.Add(Deserialize(typeof(string))); + TryEat(','); + } + return result; + } else if (tpInfo.IsArray) { + List ls = new List(); + Eat("["); + while (!TryEat(']')) { + ls.Add(Deserialize(tp.GetElementType())); + TryEat(','); + } + var arr = Array.CreateInstance(tp.GetElementType(), ls.Count); + ls.ToArray().CopyTo(arr, 0); + return arr; + } else if (Type.Equals(tp, typeof(Mesh)) || Type.Equals(tp, typeof(Texture2D)) || Type.Equals(tp, typeof(Transform)) || Type.Equals(tp, typeof(GameObject))) { + return DeserializeUnityObject(); + } else { + var obj = populate ?? Activator.CreateInstance(tp); + Eat("{"); + while (!TryEat('}')) { + var name = EatField(); + var tmpType = tp; + System.Reflection.FieldInfo field = null; + while (field == null && tmpType != null) { + field = tmpType.GetField(name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); + tmpType = tmpType.BaseType; + } + + if (field == null) { + SkipFieldData(); + } else { + field.SetValue(obj, Deserialize(field.FieldType)); + } + TryEat(','); + } + return obj; + } + } + + UnityEngine.Object DeserializeUnityObject () { + Eat("{"); + var result = DeserializeUnityObjectInner(); + Eat("}"); + return result; + } + + UnityEngine.Object DeserializeUnityObjectInner () { + // Ignore InstanceID field (compatibility only) + var fieldName = EatField(); + + if (fieldName == "InstanceID") { + EatField(); + fieldName = EatField(); + } + + if (fieldName != "Name") throw new Exception("Expected 'Name' field"); + string name = EatField(); + + if (name == null) return null; + + if (EatField() != "Type") throw new Exception("Expected 'Type' field"); + string typename = EatField(); + + // Remove assembly information + if (typename.IndexOf(',') != -1) { + typename = typename.Substring(0, typename.IndexOf(',')); + } + + // Note calling through assembly is more stable on e.g WebGL + var type = WindowsStoreCompatibility.GetTypeInfo(typeof(AstarPath)).Assembly.GetType(typename); + type = type ?? WindowsStoreCompatibility.GetTypeInfo(typeof(Transform)).Assembly.GetType(typename); + + if (Type.Equals(type, null)) { + Debug.LogError("Could not find type '"+typename+"'. Cannot deserialize Unity reference"); + return null; + } + + // Check if there is another field there + EatWhitespace(); + if ((char)reader.Peek() == '"') { + if (EatField() != "GUID") throw new Exception("Expected 'GUID' field"); + string guid = EatField(); + + foreach (var helper in UnityEngine.Object.FindObjectsOfType()) { + if (helper.GetGUID() == guid) { + if (Type.Equals(type, typeof(GameObject))) { + return helper.gameObject; + } else { + return helper.GetComponent(type); + } + } + } + } + + // Note: calling LoadAll with an empty string will make it load the whole resources folder, which is probably a bad idea. + if (!string.IsNullOrEmpty(name)) { + // Try to load from resources + UnityEngine.Object[] objs = Resources.LoadAll(name, type); + + for (int i = 0; i < objs.Length; i++) { + if (objs[i].name == name || objs.Length == 1) { + return objs[i]; + } + } + } + + return null; + } + + void EatWhitespace () { + while (char.IsWhiteSpace((char)reader.Peek())) + reader.Read(); + } + + void Eat (string s) { + EatWhitespace(); + for (int i = 0; i < s.Length; i++) { + var c = (char)reader.Read(); + if (c != s[i]) { + throw new Exception("Expected '" + s[i] + "' found '" + c + "'\n\n..." + reader.ReadLine()); + } + } + } + + System.Text.StringBuilder builder = new System.Text.StringBuilder(); + string EatUntil (string c, bool inString) { + builder.Length = 0; + bool escape = false; + while (true) { + var readInt = reader.Peek(); + if (!escape && (char)readInt == '"') { + inString = !inString; + } + + var readChar = (char)readInt; + if (readInt == -1) { + throw new Exception("Unexpected EOF"); + } else if (!escape && readChar == '\\') { + escape = true; + reader.Read(); + } else if (!inString && c.IndexOf(readChar) != -1) { + break; + } else { + builder.Append(readChar); + reader.Read(); + escape = false; + } + } + + return builder.ToString(); + } + + bool TryEat (char c) { + EatWhitespace(); + if ((char)reader.Peek() == c) { + reader.Read(); + return true; + } + return false; + } + + string EatField () { + var result = EatUntil("\",}]", TryEat('"')); + + TryEat('\"'); + TryEat(':'); + TryEat(','); + return result; + } + + void SkipFieldData () { + var indent = 0; + + while (true) { + EatUntil(",{}[]", false); + var last = (char)reader.Peek(); + + switch (last) { + case '{': + case '[': + indent++; + break; + case '}': + case ']': + indent--; + if (indent < 0) return; + break; + case ',': + if (indent == 0) { + reader.Read(); + return; + } + break; + default: + throw new System.Exception("Should not reach this part"); + } + + reader.Read(); + } + } + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/TinyJson.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/TinyJson.cs.meta new file mode 100644 index 0000000..db024b0 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/Serialization/TinyJson.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 761fd44fdff0b40648c9b7ce7763a06f +timeCreated: 1463169419 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/astarclasses.cs b/Final Platformer/Assets/AstarPathfindingProject/Core/astarclasses.cs new file mode 100644 index 0000000..3a7c0bc --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/astarclasses.cs @@ -0,0 +1,1142 @@ +using UnityEngine; +using System.Collections.Generic; + +// Empty namespace declaration to avoid errors in the free version +// Which does not have any classes in the RVO namespace +namespace Pathfinding.RVO {} + +namespace Pathfinding { + using Pathfinding.Util; + +#if UNITY_5_0 + /// Used in Unity 5.0 since the HelpURLAttribute was first added in Unity 5.1 + public class HelpURLAttribute : Attribute { + } +#endif + + [System.Serializable] + /// Stores editor colors + public class AstarColor { + public Color _SolidColor; + public Color _UnwalkableNode; + public Color _BoundsHandles; + + public Color _ConnectionLowLerp; + public Color _ConnectionHighLerp; + + public Color _MeshEdgeColor; + + /// + /// Holds user set area colors. + /// Use GetAreaColor to get an area color + /// + public Color[] _AreaColors; + + public static Color SolidColor = new Color(30/255f, 102/255f, 201/255f, 0.9F); + public static Color UnwalkableNode = new Color(1, 0, 0, 0.5F); + public static Color BoundsHandles = new Color(0.29F, 0.454F, 0.741F, 0.9F); + + public static Color ConnectionLowLerp = new Color(0, 1, 0, 0.5F); + public static Color ConnectionHighLerp = new Color(1, 0, 0, 0.5F); + + public static Color MeshEdgeColor = new Color(0, 0, 0, 0.5F); + + private static Color[] AreaColors = new Color[1]; + + public static int ColorHash () { + var hash = SolidColor.GetHashCode() ^ UnwalkableNode.GetHashCode() ^ BoundsHandles.GetHashCode() ^ ConnectionLowLerp.GetHashCode() ^ ConnectionHighLerp.GetHashCode() ^ MeshEdgeColor.GetHashCode(); + + for (int i = 0; i < AreaColors.Length; i++) hash = 7*hash ^ AreaColors[i].GetHashCode(); + return hash; + } + + /// + /// Returns an color for an area, uses both user set ones and calculated. + /// If the user has set a color for the area, it is used, but otherwise the color is calculated using AstarMath.IntToColor + /// See: + /// + public static Color GetAreaColor (uint area) { + if (area >= AreaColors.Length) return AstarMath.IntToColor((int)area, 1F); + return AreaColors[(int)area]; + } + + /// + /// Returns an color for a tag, uses both user set ones and calculated. + /// If the user has set a color for the tag, it is used, but otherwise the color is calculated using AstarMath.IntToColor + /// See: + /// + public static Color GetTagColor (uint tag) { + if (tag >= AreaColors.Length) return AstarMath.IntToColor((int)tag, 1F); + return AreaColors[(int)tag]; + } + + /// + /// Pushes all local variables out to static ones. + /// This is done because that makes it so much easier to access the colors during Gizmo rendering + /// and it has a positive performance impact as well (gizmo rendering is hot code). + /// It is a bit ugly though, but oh well. + /// + public void PushToStatic (AstarPath astar) { + _AreaColors = _AreaColors ?? new Color[1]; + + SolidColor = _SolidColor; + UnwalkableNode = _UnwalkableNode; + BoundsHandles = _BoundsHandles; + ConnectionLowLerp = _ConnectionLowLerp; + ConnectionHighLerp = _ConnectionHighLerp; + MeshEdgeColor = _MeshEdgeColor; + AreaColors = _AreaColors; + } + + public AstarColor () { + // Set default colors + _SolidColor = new Color(30/255f, 102/255f, 201/255f, 0.9F); + _UnwalkableNode = new Color(1, 0, 0, 0.5F); + _BoundsHandles = new Color(0.29F, 0.454F, 0.741F, 0.9F); + _ConnectionLowLerp = new Color(0, 1, 0, 0.5F); + _ConnectionHighLerp = new Color(1, 0, 0, 0.5F); + _MeshEdgeColor = new Color(0, 0, 0, 0.5F); + } + } + + + /// + /// Returned by graph ray- or linecasts containing info about the hit. + /// This is the return value by the methods. + /// Some members will also be initialized even if nothing was hit, see the individual member descriptions for more info. + /// + /// [Open online documentation to see images] + /// + public struct GraphHitInfo { + /// + /// Start of the line/ray. + /// Note that the point passed to the Linecast method will be clamped to the closest point on the navmesh. + /// + public Vector3 origin; + /// + /// Hit point. + /// In case no obstacle was hit then this will be set to the endpoint of the line. + /// + public Vector3 point; + /// + /// Node which contained the edge which was hit. + /// If the linecast did not hit anything then this will be set to the last node along the line's path (the one which contains the endpoint). + /// + /// For layered grid graphs the linecast will return true (i.e: no free line of sight) if when walking the graph we ended up at X,Z coordinate for the end node + /// even if end node was on a different level (e.g the floor below or above in a building). In this case no node edge was really hit so this field will still be null. + /// + public GraphNode node; + /// + /// Where the tangent starts. and together actually describes the edge which was hit. + /// [Open online documentation to see images] + /// + public Vector3 tangentOrigin; + /// + /// Tangent of the edge which was hit. + /// [Open online documentation to see images] + /// + public Vector3 tangent; + + /// Distance from to + public float distance { + get { + return (point-origin).magnitude; + } + } + + public GraphHitInfo (Vector3 point) { + tangentOrigin = Vector3.zero; + origin = Vector3.zero; + this.point = point; + node = null; + tangent = Vector3.zero; + } + } + + /// Nearest node constraint. Constrains which nodes will be returned by the function + public class NNConstraint { + /// + /// Graphs treated as valid to search on. + /// This is a bitmask meaning that bit 0 specifies whether or not the first graph in the graphs list should be able to be included in the search, + /// bit 1 specifies whether or not the second graph should be included and so on. + /// + /// // Enables the first and third graphs to be included, but not the rest + /// myNNConstraint.graphMask = (1 << 0) | (1 << 2); + /// + /// + /// GraphMask mask1 = GraphMask.FromGraphName("My Grid Graph"); + /// GraphMask mask2 = GraphMask.FromGraphName("My Other Grid Graph"); + /// + /// NNConstraint nn = NNConstraint.Default; + /// + /// nn.graphMask = mask1 | mask2; + /// + /// // Find the node closest to somePoint which is either in 'My Grid Graph' OR in 'My Other Grid Graph' + /// var info = AstarPath.active.GetNearest(somePoint, nn); + /// + /// + /// Note: This does only affect which nodes are returned from a call, if a valid graph is connected to an invalid graph using a node link then it might be searched anyway. + /// + /// See: + /// See: + /// See: bitmasks (view in online documentation for working links) + /// + public GraphMask graphMask = -1; + + /// Only treat nodes in the area as suitable. Does not affect anything if is less than 0 (zero) + public bool constrainArea; + + /// Area ID to constrain to. Will not affect anything if less than 0 (zero) or if is false + public int area = -1; + + /// Constrain the search to only walkable or unwalkable nodes depending on . + public bool constrainWalkability = true; + + /// + /// Only search for walkable or unwalkable nodes if is enabled. + /// If true, only walkable nodes will be searched for, otherwise only unwalkable nodes will be searched for. + /// Does not affect anything if if false. + /// + public bool walkable = true; + + /// + /// if available, do an XZ check instead of checking on all axes. + /// The navmesh/recast graph supports this. + /// + /// This can be important on sloped surfaces. See the image below in which the closest point for each blue point is queried for: + /// [Open online documentation to see images] + /// + /// The navmesh/recast graphs also contain a global option for this: . + /// + public bool distanceXZ; + + /// + /// Sets if tags should be constrained. + /// See: + /// + public bool constrainTags = true; + + /// + /// Nodes which have any of these tags set are suitable. + /// This is a bitmask, i.e bit 0 indicates that tag 0 is good, bit 3 indicates tag 3 is good etc. + /// See: + /// See: + /// See: bitmasks (view in online documentation for working links) + /// + public int tags = -1; + + /// + /// Constrain distance to node. + /// Uses distance from . + /// If this is false, it will completely ignore the distance limit. + /// + /// If there are no suitable nodes within the distance limit then the search will terminate with a null node as a result. + /// Note: This value is not used in this class, it is used by the AstarPath.GetNearest function. + /// + public bool constrainDistance = true; + + /// + /// Returns whether or not the graph conforms to this NNConstraint's rules. + /// Note that only the first 31 graphs are considered using this function. + /// If the has bit 31 set (i.e the last graph possible to fit in the mask), all graphs + /// above index 31 will also be considered suitable. + /// + public virtual bool SuitableGraph (int graphIndex, NavGraph graph) { + return graphMask.Contains(graphIndex); + } + + /// Returns whether or not the node conforms to this NNConstraint's rules + public virtual bool Suitable (GraphNode node) { + if (constrainWalkability && node.Walkable != walkable) return false; + + if (constrainArea && area >= 0 && node.Area != area) return false; + + if (constrainTags && ((tags >> (int)node.Tag) & 0x1) == 0) return false; + + return true; + } + + /// + /// The default NNConstraint. + /// Equivalent to new NNConstraint (). + /// This NNConstraint has settings which works for most, it only finds walkable nodes + /// and it constrains distance set by A* Inspector -> Settings -> Max Nearest Node Distance + /// + public static NNConstraint Default { + get { + return new NNConstraint(); + } + } + + /// Returns a constraint which does not filter the results + public static NNConstraint None { + get { + return new NNConstraint { + constrainWalkability = false, + constrainArea = false, + constrainTags = false, + constrainDistance = false, + graphMask = -1, + }; + } + } + + /// Default constructor. Equals to the property + public NNConstraint () { + } + } + + /// + /// A special NNConstraint which can use different logic for the start node and end node in a path. + /// A PathNNConstraint can be assigned to the Path.nnConstraint field, the path will first search for the start node, then it will call and proceed with searching for the end node (nodes in the case of a MultiTargetPath).\n + /// The default PathNNConstraint will constrain the end point to lie inside the same area as the start point. + /// + public class PathNNConstraint : NNConstraint { + public static new PathNNConstraint Default { + get { + return new PathNNConstraint { + constrainArea = true + }; + } + } + + /// Called after the start node has been found. This is used to get different search logic for the start and end nodes in a path + public virtual void SetStart (GraphNode node) { + if (node != null) { + area = (int)node.Area; + } else { + constrainArea = false; + } + } + } + + /// + /// Internal result of a nearest node query. + /// See: NNInfo + /// + public struct NNInfoInternal { + /// + /// Closest node found. + /// This node is not necessarily accepted by any NNConstraint passed. + /// See: constrainedNode + /// + public GraphNode node; + + /// + /// Optional to be filled in. + /// If the search will be able to find the constrained node without any extra effort it can fill it in. + /// + public GraphNode constrainedNode; + + /// The position clamped to the closest point on the . + public Vector3 clampedPosition; + + /// Clamped position for the optional constrainedNode + public Vector3 constClampedPosition; + + public NNInfoInternal (GraphNode node) { + this.node = node; + constrainedNode = null; + clampedPosition = Vector3.zero; + constClampedPosition = Vector3.zero; + + UpdateInfo(); + } + + /// Updates and from node positions + public void UpdateInfo () { + clampedPosition = node != null ? (Vector3)node.position : Vector3.zero; + constClampedPosition = constrainedNode != null ? (Vector3)constrainedNode.position : Vector3.zero; + } + } + + /// Result of a nearest node query + public struct NNInfo { + /// Closest node + public readonly GraphNode node; + + /// + /// Closest point on the navmesh. + /// This is the query position clamped to the closest point on the . + /// + public readonly Vector3 position; + + /// + /// Closest point on the navmesh. + /// Deprecated: This field has been renamed to + /// + [System.Obsolete("This field has been renamed to 'position'")] + public Vector3 clampedPosition { + get { + return position; + } + } + + public NNInfo (NNInfoInternal internalInfo) { + node = internalInfo.node; + position = internalInfo.clampedPosition; + } + + public static explicit operator Vector3(NNInfo ob) { + return ob.position; + } + + public static explicit operator GraphNode(NNInfo ob) { + return ob.node; + } + } + + /// + /// Progress info for e.g a progressbar. + /// Used by the scan functions in the project + /// See: + /// + public struct Progress { + /// Current progress as a value between 0 and 1 + public readonly float progress; + /// Description of what is currently being done + public readonly string description; + + public Progress (float progress, string description) { + this.progress = progress; + this.description = description; + } + + public Progress MapTo (float min, float max, string prefix = null) { + return new Progress(Mathf.Lerp(min, max, progress), prefix + description); + } + + public override string ToString () { + return progress.ToString("0.0") + " " + description; + } + } + + /// Graphs which can be updated during runtime + public interface IUpdatableGraph { + /// + /// Updates an area using the specified . + /// + /// Notes to implementators. + /// This function should (in order): + /// -# Call o.WillUpdateNode on the GUO for every node it will update, it is important that this is called BEFORE any changes are made to the nodes. + /// -# Update walkabilty using special settings such as the usePhysics flag used with the GridGraph. + /// -# Call Apply on the GUO for every node which should be updated with the GUO. + /// -# Update connectivity info if appropriate (GridGraphs updates connectivity, but most other graphs don't since then the connectivity cannot be recovered later). + /// + void UpdateArea (GraphUpdateObject o); + + /// + /// May be called on the Unity thread before starting the update. + /// See: CanUpdateAsync + /// + void UpdateAreaInit (GraphUpdateObject o); + + /// + /// May be called on the Unity thread after executing the update. + /// See: CanUpdateAsync + /// + void UpdateAreaPost (GraphUpdateObject o); + + GraphUpdateThreading CanUpdateAsync (GraphUpdateObject o); + } + + /// + /// Represents a collection of settings used to update nodes in a specific region of a graph. + /// See: AstarPath.UpdateGraphs + /// See: graph-updates (view in online documentation for working links) + /// + public class GraphUpdateObject { + /// + /// The bounds to update nodes within. + /// Defined in world space. + /// + public Bounds bounds; + + /// + /// Controlls if a flood fill will be carried out after this GUO has been applied. + /// Disabling this can be used to gain a performance boost, but use with care. + /// If you are sure that a GUO will not modify walkability or connections. You can set this to false. + /// For example when only updating penalty values it can save processing power when setting this to false. Especially on large graphs. + /// Note: If you set this to false, even though it does change e.g walkability, it can lead to paths returning that they failed even though there is a path, + /// or the try to search the whole graph for a path even though there is none, and will in the processes use wast amounts of processing power. + /// + /// If using the basic GraphUpdateObject (not a derived class), a quick way to check if it is going to need a flood fill is to check if is true or is true. + /// + /// Deprecated: Not necessary anymore + /// + [System.Obsolete("Not necessary anymore")] + public bool requiresFloodFill { set {} } + + /// + /// Use physics checks to update nodes. + /// When updating a grid graph and this is true, the nodes' position and walkability will be updated using physics checks + /// with settings from "Collision Testing" and "Height Testing". + /// + /// When updating a PointGraph, setting this to true will make it re-evaluate all connections in the graph which passes through the . + /// This has no effect when updating GridGraphs if is turned on. + /// + /// On RecastGraphs, having this enabled will trigger a complete recalculation of all tiles intersecting the bounds. + /// This is quite slow (but powerful). If you only want to update e.g penalty on existing nodes, leave it disabled. + /// + public bool updatePhysics = true; + + /// + /// Reset penalties to their initial values when updating grid graphs and is true. + /// If you want to keep old penalties even when you update the graph you may want to disable this option. + /// + /// The images below shows two overlapping graph update objects, the right one happened to be applied before the left one. They both have updatePhysics = true and are + /// set to increase the penalty of the nodes by some amount. + /// + /// The first image shows the result when resetPenaltyOnPhysics is false. Both penalties are added correctly. + /// [Open online documentation to see images] + /// + /// This second image shows when resetPenaltyOnPhysics is set to true. The first GUO is applied correctly, but then the second one (the left one) is applied + /// and during its updating, it resets the penalties first and then adds penalty to the nodes. The result is that the penalties from both GUOs are not added together. + /// The green patch in at the border is there because physics recalculation (recalculation of the position of the node, checking for obstacles etc.) affects a slightly larger + /// area than the original GUO bounds because of the Grid Graph -> Collision Testing -> Diameter setting (it is enlarged by that value). So some extra nodes have their penalties reset. + /// + /// [Open online documentation to see images] + /// + public bool resetPenaltyOnPhysics = true; + + /// + /// Update Erosion for GridGraphs. + /// When enabled, erosion will be recalculated for grid graphs + /// after the GUO has been applied. + /// + /// In the below image you can see the different effects you can get with the different values.\n + /// The first image shows the graph when no GUO has been applied. The blue box is not identified as an obstacle by the graph, the reason + /// there are unwalkable nodes around it is because there is a height difference (nodes are placed on top of the box) so erosion will be applied (an erosion value of 2 is used in this graph). + /// The orange box is identified as an obstacle, so the area of unwalkable nodes around it is a bit larger since both erosion and collision has made + /// nodes unwalkable.\n + /// The GUO used simply sets walkability to true, i.e making all nodes walkable. + /// + /// [Open online documentation to see images] + /// + /// When updateErosion=True, the reason the blue box still has unwalkable nodes around it is because there is still a height difference + /// so erosion will still be applied. The orange box on the other hand has no height difference and all nodes are set to walkable.\n + /// \n + /// When updateErosion=False, all nodes walkability are simply set to be walkable in this example. + /// + /// See: Pathfinding.GridGraph + /// + public bool updateErosion = true; + + /// + /// NNConstraint to use. + /// The Pathfinding.NNConstraint.SuitableGraph function will be called on the NNConstraint to enable filtering of which graphs to update.\n + /// Note: As the Pathfinding.NNConstraint.SuitableGraph function is A* Pathfinding Project Pro only, this variable doesn't really affect anything in the free version. + /// + public NNConstraint nnConstraint = NNConstraint.None; + + /// + /// Penalty to add to the nodes. + /// A penalty of 1000 is equivalent to the cost of moving 1 world unit. + /// + public int addPenalty; + + /// If true, all nodes' walkable variable will be set to + public bool modifyWalkability; + + /// If is true, the nodes' walkable variable will be set to this value + public bool setWalkability; + + /// If true, all nodes' tag will be set to + public bool modifyTag; + + /// If is true, all nodes' tag will be set to this value + public int setTag; + + /// + /// Track which nodes are changed and save backup data. + /// Used internally to revert changes if needed. + /// + public bool trackChangedNodes; + + /// + /// Nodes which were updated by this GraphUpdateObject. + /// Will only be filled if is true. + /// Note: It might take a few frames for graph update objects to be applied. + /// If you need this info immediately, use . + /// + public List changedNodes; + private List backupData; + private List backupPositionData; + + /// + /// A shape can be specified if a bounds object does not give enough precision. + /// Note that if you set this, you should set the bounds so that it encloses the shape + /// because the bounds will be used as an initial fast check for which nodes that should + /// be updated. + /// + public GraphUpdateShape shape; + + /// + /// Should be called on every node which is updated with this GUO before it is updated. + /// See: + /// + /// The node to save fields for. If null, nothing will be done + public virtual void WillUpdateNode (GraphNode node) { + if (trackChangedNodes && node != null) { + if (changedNodes == null) { changedNodes = ListPool.Claim (); backupData = ListPool.Claim (); backupPositionData = ListPool.Claim (); } + changedNodes.Add(node); + backupPositionData.Add(node.position); + backupData.Add(node.Penalty); + backupData.Add(node.Flags); +#if !ASTAR_NO_GRID_GRAPH + var gridNode = node as GridNode; + if (gridNode != null) backupData.Add(gridNode.InternalGridFlags); +#endif + } + } + + /// + /// Reverts penalties and flags (which includes walkability) on every node which was updated using this GUO. + /// Data for reversion is only saved if is true. + /// + /// Note: Not all data is saved. The saved data includes: penalties, walkability, tags, area, position and for grid graphs (not layered) it also includes connection data. + /// + /// This method modifies the graph. So it must be called inside while it is safe to modify the graph, for example inside a work item as shown in the example below. + /// + /// \miscsnippets MiscSnippets.cs GraphUpdateObject.RevertFromBackup + /// + /// See: blocking (view in online documentation for working links) + /// See: + /// + public virtual void RevertFromBackup () { + if (trackChangedNodes) { + if (changedNodes == null) return; + + int counter = 0; + for (int i = 0; i < changedNodes.Count; i++) { + changedNodes[i].Penalty = backupData[counter]; + counter++; + // Restore the flags, but not the HierarchicalNodeIndex as that could screw up some internal datastructures + var tmp = changedNodes[i].HierarchicalNodeIndex; + changedNodes[i].Flags = backupData[counter]; + changedNodes[i].HierarchicalNodeIndex = tmp; + counter++; +#if !ASTAR_NO_GRID_GRAPH + var gridNode = changedNodes[i] as GridNode; + if (gridNode != null) { + gridNode.InternalGridFlags = (ushort)backupData[counter]; + counter++; + } +#endif + changedNodes[i].position = backupPositionData[i]; + changedNodes[i].SetConnectivityDirty(); + } + + ListPool.Release (ref changedNodes); + ListPool.Release (ref backupData); + ListPool.Release (ref backupPositionData); + } else { + throw new System.InvalidOperationException("Changed nodes have not been tracked, cannot revert from backup. Please set trackChangedNodes to true before applying the update."); + } + } + + /// Updates the specified node using this GUO's settings + public virtual void Apply (GraphNode node) { + if (shape == null || shape.Contains(node)) { + //Update penalty and walkability + node.Penalty = (uint)(node.Penalty+addPenalty); + if (modifyWalkability) { + node.Walkable = setWalkability; + } + + //Update tags + if (modifyTag) node.Tag = (uint)setTag; + } + } + + public GraphUpdateObject () { + } + + /// Creates a new GUO with the specified bounds + public GraphUpdateObject (Bounds b) { + bounds = b; + } + } + + /// Graph which has a well defined transformation from graph space to world space + public interface ITransformedGraph { + GraphTransform transform { get; } + } + + /// Graph which supports the Linecast method + public interface IRaycastableGraph { + bool Linecast (Vector3 start, Vector3 end); + bool Linecast (Vector3 start, Vector3 end, GraphNode hint); + bool Linecast (Vector3 start, Vector3 end, GraphNode hint, out GraphHitInfo hit); + bool Linecast (Vector3 start, Vector3 end, GraphNode hint, out GraphHitInfo hit, List trace); + } + + /// + /// Integer Rectangle. + /// Works almost like UnityEngine.Rect but with integer coordinates + /// + [System.Serializable] + public struct IntRect { + public int xmin, ymin, xmax, ymax; + + public IntRect (int xmin, int ymin, int xmax, int ymax) { + this.xmin = xmin; + this.xmax = xmax; + this.ymin = ymin; + this.ymax = ymax; + } + + public bool Contains (int x, int y) { + return !(x < xmin || y < ymin || x > xmax || y > ymax); + } + + public int Width { + get { + return xmax-xmin+1; + } + } + + public int Height { + get { + return ymax-ymin+1; + } + } + + /// + /// Returns if this rectangle is valid. + /// An invalid rect could have e.g xmin > xmax. + /// Rectamgles with a zero area area invalid. + /// + public bool IsValid () { + return xmin <= xmax && ymin <= ymax; + } + + public static bool operator == (IntRect a, IntRect b) { + return a.xmin == b.xmin && a.xmax == b.xmax && a.ymin == b.ymin && a.ymax == b.ymax; + } + + public static bool operator != (IntRect a, IntRect b) { + return a.xmin != b.xmin || a.xmax != b.xmax || a.ymin != b.ymin || a.ymax != b.ymax; + } + + public override bool Equals (System.Object obj) { + var rect = (IntRect)obj; + + return xmin == rect.xmin && xmax == rect.xmax && ymin == rect.ymin && ymax == rect.ymax; + } + + public override int GetHashCode () { + return xmin*131071 ^ xmax*3571 ^ ymin*3109 ^ ymax*7; + } + + /// + /// Returns the intersection rect between the two rects. + /// The intersection rect is the area which is inside both rects. + /// If the rects do not have an intersection, an invalid rect is returned. + /// See: IsValid + /// + public static IntRect Intersection (IntRect a, IntRect b) { + return new IntRect( + System.Math.Max(a.xmin, b.xmin), + System.Math.Max(a.ymin, b.ymin), + System.Math.Min(a.xmax, b.xmax), + System.Math.Min(a.ymax, b.ymax) + ); + } + + /// Returns if the two rectangles intersect each other + public static bool Intersects (IntRect a, IntRect b) { + return !(a.xmin > b.xmax || a.ymin > b.ymax || a.xmax < b.xmin || a.ymax < b.ymin); + } + + /// + /// Returns a new rect which contains both input rects. + /// This rectangle may contain areas outside both input rects as well in some cases. + /// + public static IntRect Union (IntRect a, IntRect b) { + return new IntRect( + System.Math.Min(a.xmin, b.xmin), + System.Math.Min(a.ymin, b.ymin), + System.Math.Max(a.xmax, b.xmax), + System.Math.Max(a.ymax, b.ymax) + ); + } + + /// Returns a new IntRect which is expanded to contain the point + public IntRect ExpandToContain (int x, int y) { + return new IntRect( + System.Math.Min(xmin, x), + System.Math.Min(ymin, y), + System.Math.Max(xmax, x), + System.Math.Max(ymax, y) + ); + } + + /// Returns a new rect which is expanded by range in all directions. + /// How far to expand. Negative values are permitted. + public IntRect Expand (int range) { + return new IntRect(xmin-range, + ymin-range, + xmax+range, + ymax+range + ); + } + + public override string ToString () { + return "[x: "+xmin+"..."+xmax+", y: " + ymin +"..."+ymax+"]"; + } + + /// Draws some debug lines representing the rect + public void DebugDraw (GraphTransform transform, Color color) { + Vector3 p1 = transform.Transform(new Vector3(xmin, 0, ymin)); + Vector3 p2 = transform.Transform(new Vector3(xmin, 0, ymax)); + Vector3 p3 = transform.Transform(new Vector3(xmax, 0, ymax)); + Vector3 p4 = transform.Transform(new Vector3(xmax, 0, ymin)); + + Debug.DrawLine(p1, p2, color); + Debug.DrawLine(p2, p3, color); + Debug.DrawLine(p3, p4, color); + Debug.DrawLine(p4, p1, color); + } + } + + /// + /// Holds a bitmask of graphs. + /// This bitmask can hold up to 32 graphs. + /// + /// The bitmask can be converted to and from integers implicitly. + /// + /// + /// GraphMask mask1 = GraphMask.FromGraphName("My Grid Graph"); + /// GraphMask mask2 = GraphMask.FromGraphName("My Other Grid Graph"); + /// + /// NNConstraint nn = NNConstraint.Default; + /// + /// nn.graphMask = mask1 | mask2; + /// + /// // Find the node closest to somePoint which is either in 'My Grid Graph' OR in 'My Other Grid Graph' + /// var info = AstarPath.active.GetNearest(somePoint, nn); + /// + /// + /// See: bitmasks (view in online documentation for working links) + /// + [System.Serializable] + public struct GraphMask { + /// Bitmask representing the mask + public int value; + + /// A mask containing every graph + public static GraphMask everything { get { return new GraphMask(-1); } } + + public GraphMask (int value) { + this.value = value; + } + + public static implicit operator int(GraphMask mask) { + return mask.value; + } + + public static implicit operator GraphMask (int mask) { + return new GraphMask(mask); + } + + /// Combines two masks to form the intersection between them + public static GraphMask operator & (GraphMask lhs, GraphMask rhs) { + return new GraphMask(lhs.value & rhs.value); + } + + /// Combines two masks to form the union of them + public static GraphMask operator | (GraphMask lhs, GraphMask rhs) { + return new GraphMask(lhs.value | rhs.value); + } + + /// Inverts the mask + public static GraphMask operator ~ (GraphMask lhs) { + return new GraphMask(~lhs.value); + } + + /// True if this mask contains the graph with the given graph index + public bool Contains (int graphIndex) { + return ((value >> graphIndex) & 1) != 0; + } + + /// A bitmask containing the given graph + public static GraphMask FromGraph (NavGraph graph) { + return 1 << (int)graph.graphIndex; + } + + public override string ToString () { + return value.ToString(); + } + + /// + /// A bitmask containing the first graph with the given name. + /// + /// GraphMask mask1 = GraphMask.FromGraphName("My Grid Graph"); + /// GraphMask mask2 = GraphMask.FromGraphName("My Other Grid Graph"); + /// + /// NNConstraint nn = NNConstraint.Default; + /// + /// nn.graphMask = mask1 | mask2; + /// + /// // Find the node closest to somePoint which is either in 'My Grid Graph' OR in 'My Other Grid Graph' + /// var info = AstarPath.active.GetNearest(somePoint, nn); + /// + /// + public static GraphMask FromGraphName (string graphName) { + var graph = AstarData.active.data.FindGraph(g => g.name == graphName); + + if (graph == null) throw new System.ArgumentException("Could not find any graph with the name '" + graphName + "'"); + return FromGraph(graph); + } + } + + #region Delegates + + /* Delegate with on Path object as parameter. + * This is used for callbacks when a path has finished calculation.\n + * Example function: + * \snippet MiscSnippets.cs OnPathDelegate + */ + public delegate void OnPathDelegate (Path p); + + public delegate void OnGraphDelegate (NavGraph graph); + + public delegate void OnScanDelegate (AstarPath script); + + /// Deprecated: + public delegate void OnScanStatus (Progress progress); + + #endregion + + #region Enums + + public enum GraphUpdateThreading { + /// + /// Call UpdateArea in the unity thread. + /// This is the default value. + /// Not compatible with SeparateThread. + /// + UnityThread = 0, + /// Call UpdateArea in a separate thread. Not compatible with UnityThread. + SeparateThread = 1 << 0, + /// Calls UpdateAreaInit in the Unity thread before everything else + UnityInit = 1 << 1, + /// + /// Calls UpdateAreaPost in the Unity thread after everything else. + /// This is used together with SeparateThread to apply the result of the multithreaded + /// calculations to the graph without modifying it at the same time as some other script + /// might be using it (e.g calling GetNearest). + /// + UnityPost = 1 << 2, + /// Combination of SeparateThread and UnityInit + SeparateAndUnityInit = SeparateThread | UnityInit + } + + /// How path results are logged by the system + public enum PathLog { + /// Does not log anything. This is recommended for release since logging path results has a performance overhead. + None, + /// Logs basic info about the paths + Normal, + /// Includes additional info + Heavy, + /// Same as heavy, but displays the info in-game using GUI + InGame, + /// Same as normal, but logs only paths which returned an error + OnlyErrors + } + + /// + /// How to estimate the cost of moving to the destination during pathfinding. + /// + /// The heuristic is the estimated cost from the current node to the target. + /// The different heuristics have roughly the same performance except not using any heuristic at all ( + /// which is usually significantly slower. + /// + /// In the image below you can see a comparison of the different heuristic options for an 8-connected grid and + /// for a 4-connected grid. + /// Note that all paths within the green area will all have the same length. The only difference between the heuristics + /// is which of those paths of the same length that will be chosen. + /// Note that while the Diagonal Manhattan and Manhattan options seem to behave very differently on an 8-connected grid + /// they only do it in this case because of very small rounding errors. Usually they behave almost identically on 8-connected grids. + /// + /// [Open online documentation to see images] + /// + /// Generally for a 4-connected grid graph the Manhattan option should be used as it is the true distance on a 4-connected grid. + /// For an 8-connected grid graph the Diagonal Manhattan option is the mathematically most correct option, however the Euclidean option + /// is often preferred, especially if you are simplifying the path afterwards using modifiers. + /// + /// For any graph that is not grid based the Euclidean option is the best one to use. + /// + /// See: Wikipedia: A* search_algorithm + /// + public enum Heuristic { + /// Manhattan distance. See: https://en.wikipedia.org/wiki/Taxicab_geometry + Manhattan, + /// + /// Manhattan distance, but allowing diagonal movement as well. + /// Note: This option is currently hard coded for the XZ plane. It will be equivalent to Manhattan distance if you try to use it in the XY plane (i.e for a 2D game). + /// + DiagonalManhattan, + /// Ordinary distance. See: https://en.wikipedia.org/wiki/Euclidean_distance + Euclidean, + /// + /// Use no heuristic at all. + /// This reduces the pathfinding algorithm to Dijkstra's algorithm. + /// This is usually significantly slower compared to using a heuristic, which is why the A* algorithm is usually preferred over Dijkstra's algorithm. + /// You may have to use this if you have a very non-standard graph. For example a world with a wraparound playfield (think Civilization or Asteroids) and you have custom links + /// with a zero cost from one end of the map to the other end. Usually the A* algorithm wouldn't find the wraparound links because it wouldn't think to look in that direction. + /// See: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm + /// + None + } + + /// How to visualize the graphs in the editor + public enum GraphDebugMode { + /// Draw the graphs with a single solid color + SolidColor, + /// + /// Use the G score of the last calculated paths to color the graph. + /// The G score is the cost from the start node to the given node. + /// See: https://en.wikipedia.org/wiki/A*_search_algorithm + /// + G, + /// + /// Use the H score (heuristic) of the last calculated paths to color the graph. + /// The H score is the estimated cost from the current node to the target. + /// See: https://en.wikipedia.org/wiki/A*_search_algorithm + /// + H, + /// + /// Use the F score of the last calculated paths to color the graph. + /// The F score is the G score + the H score, or in other words the estimated cost total cost of the path. + /// See: https://en.wikipedia.org/wiki/A*_search_algorithm + /// + F, + /// + /// Use the penalty of each node to color the graph. + /// This does not show penalties added by tags. + /// See: graph-updates (view in online documentation for working links) + /// See: + /// + Penalty, + /// + /// Visualize the connected components of the graph. + /// A node with a given color can reach any other node with the same color. + /// + /// See: + /// See: https://en.wikipedia.org/wiki/Connected_component_(graph_theory) + /// + Areas, + /// + /// Use the tag of each node to color the graph. + /// See: tags (view in online documentation for working links) + /// See: + /// + Tags, + /// + /// Visualize the hierarchical graph structure of the graph. + /// This is mostly for internal use. + /// See: + /// + HierarchicalNode, + } + + /// Number of threads to use + public enum ThreadCount { + AutomaticLowLoad = -1, + AutomaticHighLoad = -2, + None = 0, + One = 1, + Two, + Three, + Four, + Five, + Six, + Seven, + Eight + } + + /// Internal state of a path in the pipeline + public enum PathState { + Created = 0, + PathQueue = 1, + Processing = 2, + ReturnQueue = 3, + Returned = 4 + } + + /// State of a path request + public enum PathCompleteState { + /// + /// The path has not been calculated yet. + /// See: + /// + NotCalculated = 0, + /// + /// The path calculation is done, but it failed. + /// See: + /// + Error = 1, + /// The path has been successfully calculated + Complete = 2, + /// + /// The path has been calculated, but only a partial path could be found. + /// See: + /// + Partial = 3, + } + + /// What to do when the character is close to the destination + public enum CloseToDestinationMode { + /// The character will stop as quickly as possible when within endReachedDistance (field that exist on most movement scripts) units from the destination + Stop, + /// The character will continue to the exact position of the destination + ContinueToExactDestination, + } + + /// Indicates the side of a line that a point lies on + public enum Side : byte { + /// The point lies exactly on the line + Colinear = 0, + /// The point lies on the left side of the line + Left = 1, + /// The point lies on the right side of the line + Right = 2 + } + + public enum InspectorGridHexagonNodeSize { + /// Value is the distance between two opposing sides in the hexagon + Width, + /// Value is the distance between two opposing vertices in the hexagon + Diameter, + /// Value is the raw node size of the grid + NodeSize + } + + public enum InspectorGridMode { + Grid, + IsometricGrid, + Hexagonal, + Advanced + } + + /// + /// Determines which direction the agent moves in. + /// For 3D games you most likely want the ZAxisIsForward option as that is the convention for 3D games. + /// For 2D games you most likely want the YAxisIsForward option as that is the convention for 2D games. + /// + public enum OrientationMode { + ZAxisForward, + YAxisForward, + } + + #endregion +} + +namespace Pathfinding.Util { + /// Prevents code stripping. See: https://docs.unity3d.com/Manual/ManagedCodeStripping.html + public class PreserveAttribute : System.Attribute { + } +} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Core/astarclasses.cs.meta b/Final Platformer/Assets/AstarPathfindingProject/Core/astarclasses.cs.meta new file mode 100644 index 0000000..63b1d90 --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Core/astarclasses.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f499b0552bded40b6a9d474dfb94343a +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/Final Platformer/Assets/AstarPathfindingProject/Documentation.meta b/Final Platformer/Assets/AstarPathfindingProject/Documentation.meta new file mode 100644 index 0000000..8f7111e --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Documentation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2bd84edb620a9b51094b9d533b8d8020 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Final Platformer/Assets/AstarPathfindingProject/Documentation/documentation.html b/Final Platformer/Assets/AstarPathfindingProject/Documentation/documentation.html new file mode 100644 index 0000000..559408f --- /dev/null +++ b/Final Platformer/Assets/AstarPathfindingProject/Documentation/documentation.html @@ -0,0 +1,17044 @@ + + + + + + + + Documentation + + + + + + + + +
+
+
+
+
+ +

Get Started With The A* Pathfinding Project

+ + +

Get Started with the A* Pathfinding Project

Pathfinding is all about finding the best path between point A and B. This is what the A* Pathfinding Project does, in this tutorial you will learn how to set up the project in a new scene and get a simple AI moving while avoiding obstacles.

This AI you will write will not be very advanced, it is just the minimal amount of code needed to get moving and following a path. If you want a more advanced AI you can either extend the script you will write in this tutorial or use (or extend) the AIPath or RichAI scripts included in the package (see part 2 for basic usage of the RichAI component).

Downloading

The first thing you need to do, if you haven't done so already, is to download the A* Pathfinding Project
+The project can be downloaded from here, you can either download the free version with some limited features (but still very powerful) or buy the pro version with more cool stuff included.
+If you want, you can explore the different example scenes in the project before you start with the next section.

Javascript (Unityscript)

If you are using UnityScript, you should first follow the instructions on the page Working with Javascript.

All example code is in C#. But I hope it will not be too hard following them since C# and Unityscript are quite similar. You should know that UnityScript does not support optional parameters so you must always pass all parameters to a function. If you get an error message that the function you are trying to call has no such overload, this might be the problem. Then check the docs for all parameters and their default values.

Deploying for Mobile/UWP

Depending on which platform you are building for, you might want to read this page first: Deploying for mobile/uwp

Troubleshooting

At this stage, if you are getting any compiler errors from the code you can first check the Readme_upgrading.txt file included, if you are upgrading from an older version. A common problem is that classes in the A* Pathfinding Project have the same name as classes in your project. This can be solved either by renaming one of the classes, or placing your class in a namespace so it will not conflict with the other class anymore.

If you are still getting errors, take a look at the forums (see http://forum.arongranberg.com) to see if anyone else has the same problem or post a new question there.

Overview

  • The central script of the A* Pathfinding Project is the script 'astarpath.cs', it acts as a central hub for everything else.
    + In the AstarPath inspector you create all graphs and adjust all settings.
    +There should always be one (always one, no more) astarpath.cs component in a scene which uses Pathfinding.
    +The astarpath.cs script can be found at Menu bar–>Components–>Pathfinding–>Pathfinder

  • The second most important component is the 'Seeker.cs' component, a Seeker component should be attached to every GameObject which uses Pathfinding (e.g all AIs).
    +The Seeker component handles path calls for one unit and post processes the paths. The Seeker isn't needed, but it makes Pathfinding easier.

  • To make the AIs move there are a number of included movement scripts in the package (e.g AIPath, RichAI, AILerp). You may use one of the included ones or you can write your own (see Writing a movement script). You can find a comparison of the built-in movement scripts here: Movement scripts.

  • Lastly there are the modifier scripts (e.g SimpleSmoothModifier.cs). Modifiers post-processes paths to smooth or simplify them, if a modifier is attached to the same GameObject as a Seeker it will post-process all paths that Seeker handles. See Using Modifiers.

+

Video Tutorial

If you prefer a video tutorial instead of a text tutorial. Here is a video for you. The video tutorial takes a more high-level approach and you will learn how to use the built-in movement scripts instead of writing a custom one. Since the video and text tutorials cover slightly different ground, it is not a bad idea to take a look at both.

You can also take a look at the excellent tutorial by Gabriel Williams (Unity Cookie) in part 8 of the series on making a Tower Defence game: https://www.youtube.com/watch?feature=player_embedded&v=PUJSvd53v4k The video covers most things which will be discussed in the text tutorial.

New Scene

Create a new scene, name it "PathfindingTest". Now let's create something which an AI could walk on and something for it to avoid: add a plane to the scene, place it in the scene origin (0,0,0) and scale it to 10,10,10.
+Create a new layer (Edit->Project Settings->Tags) named "Ground" and place the plane in that layer. Now create some cubes of differerent scales and place them on the plane, these will be obstacles which the AI should avoid. Place them in a new layer named "Obstacles".
+Your scene should now look something like this:

Adding A*

Now we have ground for an AI to stand on and obstacles for it to avoid. So now we are going to add the A* Pathfinding System to the scene to enable Pathfinding.
+Create a new GameObject, name it "A*", add the "AstarPath" component to it (Menu bar–>Components–>Pathfinding–>Pathfinder).
+The AstarPath inspector is divided into several parts. The two most important is the Graphs area and the Scan button at the bottom.
+The Graphs area holds all the graphs in your scene, you may have up to 256 but usually 1 or 2 will be sufficient. A single graph is usually preferred for simplicity.
+If you open the Graphs area by clicking on it you will see a list of graphs which you can add. I can't explain them all here but the two main ones is the Grid Graph which generates nodes in a grid pattern and the Recast Graph which automatically calculates a navmesh from the world (only available in the pro version).
+The Scan button is for updating the graphs, this is also done on startup (unless the startup is cached, more about that in another part) and some graphs will do it automatically when changing the graph settings and the scanning won't cause any lag.
+There is also a shortcut to use Cmd+Alt+S (mac) or Ctrl+Alt+S (windows).

For this tutorial we will create a Grid Graph, after adding it, click on the new Grid Graph label to bring up the graph inspector. As the name implies, the GridGraph will generate a grid of nodes, width*depth. A grid can be positioned anywhere in the scene and you can rotate it any way you want.
+The Node Size variable determines how large a square/node in the grid is, for this tutorial you can leave it at 1, so the nodes will be spaced 1 unit apart.
+The position needs to be changed though. Switch to bottom-left in the small selector to the right of the position field (currently named "Center"), then enter (-50,-0.1,-50). The -0.1 is to avoid floating point errors, in our scene the ground is at Y=0, if the graph was to have position Y=0 too, we might get annoying floating point errors when casting rays against it for example (like the height check does).
+To make the grid fit our scene we need to change the width and depth variables, set both to 100 in this case. You can see that the grid is correctly positioned by the white bounding rectangle in the scene view which should now be enclosing the plane exactly.

Height Testing

In order to place the nodes at their correct height, the A* system fires off a bunch of rays against the scene to see where they hit. That's the Height Testing settings.
+A ray, optionally thick (as opposed to a line), is fired from [Ray Length] units above the grid downwards, a node is placed where it hits. If it doesn't hit anything, it is either made unwalkable if the Unwalkable When No Ground variable is toggled or the node is placed at Y=0 relative to the grid if it is set to false.
+We need to change the mask used, currently it includes everything, but that would include our obstacles as well, and we don't want that. So set the Mask to only include the "Ground" layer which we created earlier.
+

Collision Testing

When a node has been placed, it is checked for walkability, this can be done with a Sphere, Capsule or a Ray. Usually a capsule is used with the same diameter and height as the AI character which is going to be walking around in the world, preferably with some margin though.
+Our AI will have the standard diameter and height of 1 and 2 world units respectively, but we will set the diameter and height for the collision testing to 2 and 2 to get some margin.
+Next, to make the system aware of the obstacles we placed, we need to change the mask for the Collision Testing, this time set it to contain only the "Obstacles" layer as we wouldn't want our ground to be treated as an obstacle.

Now everything should be set up correctly to scan the graph.
+Press Scan. Wait a fraction of a second and you've got a generated grid! (if you have done everything correctly, that is, compare your settings to the image below, also check that Show Graphs is true)
+

+

Adding the AI

What is a pathfinding test without some moving stuff? Not fun at all, so let's add an AI to play around with.
+Create a capsule and add the Character Controller component to it, also place it somewhere visible on the plane.
+Add the Seeker component to the AI, this script is a helper script for calling requesting paths from other scripts, it can also handle path modifiers which can e.g smooth the path or simplify it using raycasts.

There are 2 alternatives now. You can either write your own movement script or you can use one of the built-in movement scripts. I recommend following the tutorial for writing a custom movement script even if you end up using one of the built-in ones in your game because it makes it easier to understand how the system works under the hood.

Check out this subpage for the tutorial: Writing a movement script

There are also included movement scripts in the project which you can use if you don't want to write your own script. The included scripts are much more advanced than what you write in the tutorial linked above. The included scripts are called AIPath, RichAI and AILerp. The AIPath script can be used on any graph while RichAI is primarily for navmesh based graphs. While the AIPath and RichAI scripts follow the path loosely, the AILerp script uses interpolation to move along the path very precisely, but perhaps not in the most realistic way. Which one you use depends on your game.

See

For more information about the included movement scripts, take a look at Movement scripts. You can also see how they are used in the included example scenes.

+For this tutorial you can attach the AIPath component to the AI. Create a new GameObject named "Target" and position it where you want the AI to move. Then attach the AIDestinationSetter component to the AI. This component is just a very simple helper script which will tell the AIPath script to move to a particular location. You will likely replace this script with your own game specific script in the future. The AIDestinationSetter component has a single field called "target", assign the "Target" GameObject that you created earlier to this field.

If you press play now the AI should move to the target. How the movement scripts work and how to configure them is explained in more detail in the video tutorial linked above. Take a look at that if something doesn't seem to work.

Smoothing

Now you have learned how to set up a simple grid graph and how to calculate paths Pathfinding, but surely there must be a way to get those paths to look a bit smoother?
+Sure it is. Path smoothing and simplification scripts are called Path Modifiers and are scripts which can be added to the same GameObject as a Seeker.
+The most straight forward one is the Simple Smooth modifier which can be found at Menu bar–>Components–>Pathfinding–>Modifiers–>Simple Smooth. Add that to our AI.
+ What this modifier is going to do, is to subdivide the path a number of times until each segment becomes smaller than the Max Segment Length variable. Then it will smooth the path by moving the points closer to each other. The modifier has a number of settings, I won't go through all of them here. See the SimpleSmoothModifier documentation for more info about each variable. For this tutorial you can set Max Segment Length to, say 1. Iterations to 5 and Strength to 0.25. Experiment with it to get good values.

Now press play again, the path should look much smoother, just as we wanted.
+

Note

Smoothers don't usually take world geometry or the graph into account, so be careful with applying too much smoothing since that could cause paths to pass through unwalkable areas.

+

Another good modifier to use is the FunnelModifier which will simplify the path a great deal. This modifier is almost always used when using navmesh/recast graphs.

Read more about modifiers on the page Using Modifiers.

Logging settings

Every time a path is calculated by the system it can optionally be logged to the console. This can be a big help in understanding what the system is doing and also to spot performance issues. Logging is not free however, so for release builds it is recommended that you disable it.

You can change the logging settings under the A* Inspector -> Settings -> Debug tab.

Use less debugging to improve performance (a bit) or just to get rid of the Console spam. Use more debugging (heavy) if you want more information about what the pathfinding scripts are doing. The InGame option will display the latest path log using in-game GUI.

The End

That was the end of the Get Started tutorial part 1. I hope you learned something from it.
+From here on you can explore the rest of the documentation or dig straight in to the project.
+If you want a little better AI, you can use the AIPath script which is included in the project.

You can continue with the next part of the get started tutorial, where we will use navmesh graphs: Using navmeshes

You can also take a look in the sidebar, there you will find a number of tutorials for how to use the package.

Good Luck!

+ +
+
+
+
+
+
+
+
+ + + + + + + + +
+
+ +
+ + diff --git a/Final Platformer/Logs/Packages-Update.log b/Final Platformer/Logs/Packages-Update.log new file mode 100644 index 0000000..1f432dc --- /dev/null +++ b/Final Platformer/Logs/Packages-Update.log @@ -0,0 +1,43 @@ + +=== Tue Apr 7 12:10:08 2020 + +Packages were changed. +Update Mode: mergeDefaultDependencies + +The following packages were added: + com.unity.analytics@3.2.2 + com.unity.purchasing@2.0.3 + com.unity.ads@2.0.8 + com.unity.textmeshpro@1.4.1 + com.unity.package-manager-ui@2.0.8 + com.unity.collab-proxy@1.2.15 + com.unity.modules.ai@1.0.0 + com.unity.modules.animation@1.0.0 + com.unity.modules.assetbundle@1.0.0 + com.unity.modules.audio@1.0.0 + com.unity.modules.cloth@1.0.0 + com.unity.modules.director@1.0.0 + com.unity.modules.imageconversion@1.0.0 + com.unity.modules.imgui@1.0.0 + com.unity.modules.jsonserialize@1.0.0 + com.unity.modules.particlesystem@1.0.0 + com.unity.modules.physics@1.0.0 + com.unity.modules.physics2d@1.0.0 + com.unity.modules.screencapture@1.0.0 + com.unity.modules.terrain@1.0.0 + com.unity.modules.terrainphysics@1.0.0 + com.unity.modules.tilemap@1.0.0 + com.unity.modules.ui@1.0.0 + com.unity.modules.uielements@1.0.0 + com.unity.modules.umbra@1.0.0 + com.unity.modules.unityanalytics@1.0.0 + com.unity.modules.unitywebrequest@1.0.0 + com.unity.modules.unitywebrequestassetbundle@1.0.0 + com.unity.modules.unitywebrequestaudio@1.0.0 + com.unity.modules.unitywebrequesttexture@1.0.0 + com.unity.modules.unitywebrequestwww@1.0.0 + com.unity.modules.vehicles@1.0.0 + com.unity.modules.video@1.0.0 + com.unity.modules.vr@1.0.0 + com.unity.modules.wind@1.0.0 + com.unity.modules.xr@1.0.0 diff --git a/Final Platformer/Packages/manifest.json b/Final Platformer/Packages/manifest.json new file mode 100644 index 0000000..94cbf93 --- /dev/null +++ b/Final Platformer/Packages/manifest.json @@ -0,0 +1,41 @@ +{ + "dependencies": { + "com.unity.ads": "2.0.8", + "com.unity.analytics": "3.2.2", + "com.unity.cinemachine": "2.5.0", + "com.unity.collab-proxy": "1.2.15", + "com.unity.package-manager-ui": "2.0.8", + "com.unity.purchasing": "2.0.3", + "com.unity.textmeshpro": "1.4.1", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/Final Platformer/ProjectSettings/AudioManager.asset b/Final Platformer/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..4f31e74 --- /dev/null +++ b/Final Platformer/ProjectSettings/AudioManager.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 diff --git a/Final Platformer/ProjectSettings/ClusterInputManager.asset b/Final Platformer/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/Final Platformer/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/Final Platformer/ProjectSettings/DynamicsManager.asset b/Final Platformer/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..b3c263d --- /dev/null +++ b/Final Platformer/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,30 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 8 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0 + m_ClothInterCollisionStiffness: 0 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 diff --git a/Final Platformer/ProjectSettings/EditorBuildSettings.asset b/Final Platformer/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..60072fc --- /dev/null +++ b/Final Platformer/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Scenes/Menu.unity + guid: c48ac158bd1b2da41a5468343ebd5190 + - enabled: 1 + path: Assets/Scenes/SampleScene.unity + guid: 92c6f936976649c43a98c5f39cbd760b + m_configObjects: {} diff --git a/Final Platformer/ProjectSettings/EditorSettings.asset b/Final Platformer/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..8d9e83b --- /dev/null +++ b/Final Platformer/ProjectSettings/EditorSettings.asset @@ -0,0 +1,21 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 7 + m_ExternalVersionControlSupport: Visible Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 2 + m_DefaultBehaviorMode: 1 + m_SpritePackerMode: 4 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd + m_ProjectGenerationRootNamespace: + m_UserGeneratedProjectSuffix: + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/Final Platformer/ProjectSettings/GraphicsSettings.asset b/Final Platformer/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..ce1e58e --- /dev/null +++ b/Final Platformer/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,61 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_AlwaysIncludedShaders: + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_LogWhenShaderIsCompiled: 0 diff --git a/Final Platformer/ProjectSettings/InputManager.asset b/Final Platformer/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..2f98bf1 --- /dev/null +++ b/Final Platformer/ProjectSettings/InputManager.asset @@ -0,0 +1,295 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/Final Platformer/ProjectSettings/NavMeshAreas.asset b/Final Platformer/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..3b0b7c3 --- /dev/null +++ b/Final Platformer/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/Final Platformer/ProjectSettings/NetworkManager.asset b/Final Platformer/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..5dc6a83 --- /dev/null +++ b/Final Platformer/ProjectSettings/NetworkManager.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!149 &1 +NetworkManager: + m_ObjectHideFlags: 0 + m_DebugLevel: 0 + m_Sendrate: 15 + m_AssetToPrefab: {} diff --git a/Final Platformer/ProjectSettings/Physics2DSettings.asset b/Final Platformer/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..47880b1 --- /dev/null +++ b/Final Platformer/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 4 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_AutoSimulation: 1 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 1 + m_AutoSyncTransforms: 0 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/Final Platformer/ProjectSettings/PresetManager.asset b/Final Platformer/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000..7992168 --- /dev/null +++ b/Final Platformer/ProjectSettings/PresetManager.asset @@ -0,0 +1,13 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + m_DefaultList: + - type: + m_NativeTypeID: 20 + m_ManagedTypePPtr: {fileID: 0} + m_ManagedTypeFallback: + defaultPresets: + - m_Preset: {fileID: 2655988077585873504, guid: bfcfc320427f8224bbb7a96f3d3aebad, + type: 2} diff --git a/Final Platformer/ProjectSettings/ProjectSettings.asset b/Final Platformer/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..c416a55 --- /dev/null +++ b/Final Platformer/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,601 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 18 + productGUID: ed2cf287f38d00e478fe594d051affc0 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: Final Platformer + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + displayResolutionDialog: 1 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 0 + androidBlitType: 0 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 1 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + graphicsJobs: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + graphicsJobMode: 0 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 0 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + vulkanEnableSetSRGBWrite: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 0.1 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + isWsaHolographicRemotingEnabled: 0 + vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + enableVideoLayer: 0 + useProtectedVideoMemory: 0 + minimumSupportedHeadTracking: 0 + maximumSupportedHeadTracking: 1 + hololens: + depthFormat: 1 + depthBufferSharingEnabled: 1 + oculus: + sharedDepthBuffer: 1 + dashSupport: 1 + lowOverheadMode: 0 + protectedContext: 0 + v2Signing: 0 + enable360StereoCapture: 0 + protectGraphicsMemory: 0 + enableFrameTimingStats: 0 + useHDRDisplay: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: + Standalone: com.Company.ProductName + buildNumber: {} + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 16 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 1 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 9.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 9.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + iPhoneSplashScreen: {fileID: 0} + iPhoneHighResSplashScreen: {fileID: 0} + iPhoneTallHighResSplashScreen: {fileID: 0} + iPhone47inSplashScreen: {fileID: 0} + iPhone55inPortraitSplashScreen: {fileID: 0} + iPhone55inLandscapeSplashScreen: {fileID: 0} + iPhone58inPortraitSplashScreen: {fileID: 0} + iPhone58inLandscapeSplashScreen: {fileID: 0} + iPadPortraitSplashScreen: {fileID: 0} + iPadHighResPortraitSplashScreen: {fileID: 0} + iPadLandscapeSplashScreen: {fileID: 0} + iPadHighResLandscapeSplashScreen: {fileID: 0} + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSUseLaunchScreenStoryboard: 0 + iOSLaunchScreenCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + clonedFromGUID: 5f34be1353de5cf4398729fda238591b + templatePackageId: com.unity.template.2d@1.3.0 + templateDefaultScene: Assets/Scenes/SampleScene.unity + AndroidTargetArchitectures: 5 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 1 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + resolutionDialogBanner: {fileID: 0} + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsAPIs: [] + m_BuildTargetVRSettings: [] + m_BuildTargetEnableVuforiaSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 3 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 1 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 0 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + spritePackerPolicy: + webGLMemorySize: 256 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 1 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + scriptingDefineSymbols: {} + platformArchitecture: {} + scriptingBackend: {} + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + allowUnsafeCode: 0 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: Template_2D + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: Template_2D + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + metroCompilationOverrides: 1 + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + xboxOneScriptCompiler: 1 + XboxOneOverrideIdentityName: + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} + cloudServicesEnabled: + UNet: 1 + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_PrivateKeyPath: + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + facebookSdkVersion: 7.9.4 + facebookAppId: + facebookCookies: 1 + facebookLogging: 1 + facebookStatus: 1 + facebookXfbml: 0 + facebookFrictionlessRequests: 1 + apiCompatibilityLevel: 6 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + projectName: + organizationId: + cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 + legacyClampBlendShapeWeights: 0 diff --git a/Final Platformer/ProjectSettings/ProjectVersion.txt b/Final Platformer/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..bb1701c --- /dev/null +++ b/Final Platformer/ProjectSettings/ProjectVersion.txt @@ -0,0 +1 @@ +m_EditorVersion: 2018.4.15f1 diff --git a/Final Platformer/ProjectSettings/QualitySettings.asset b/Final Platformer/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..4ae8c38 --- /dev/null +++ b/Final Platformer/ProjectSettings/QualitySettings.asset @@ -0,0 +1,191 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 3 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 0 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 0 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Nintendo 3DS: 5 + Nintendo Switch: 5 + PS4: 5 + PSM: 5 + PSP2: 2 + Standalone: 5 + Tizen: 2 + WebGL: 3 + WiiU: 5 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/Final Platformer/ProjectSettings/TagManager.asset b/Final Platformer/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..2892d87 --- /dev/null +++ b/Final Platformer/ProjectSettings/TagManager.asset @@ -0,0 +1,48 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: + - EBullet + - PBullet + - Platform + - Rocket + - Heart + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - Effects + - Bullets + - Player + - Enemies + - Obstacles + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/Final Platformer/ProjectSettings/TimeManager.asset b/Final Platformer/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..06bcc6d --- /dev/null +++ b/Final Platformer/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.1 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/Final Platformer/ProjectSettings/UnityConnectSettings.asset b/Final Platformer/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..c3ae9a0 --- /dev/null +++ b/Final Platformer/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 1 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/Final Platformer/ProjectSettings/VFXManager.asset b/Final Platformer/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000..6e0eaca --- /dev/null +++ b/Final Platformer/ProjectSettings/VFXManager.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05