From 8722a9920c1f6119bf6e769cba270e63097f8e25 Mon Sep 17 00:00:00 2001
From: chai <215380520@qq.com>
Date: Thu, 23 May 2024 10:08:29 +0800
Subject: + astar project
---
.../Core/ECS/Components.meta | 8 +
.../Core/ECS/Components/AgentCylinderShape.cs | 18 +
.../Core/ECS/Components/AgentCylinderShape.cs.meta | 11 +
.../Core/ECS/Components/AgentMovementPlane.cs | 30 ++
.../Core/ECS/Components/AgentMovementPlane.cs.meta | 11 +
.../ECS/Components/AgentMovementPlaneSource.cs | 15 +
.../Components/AgentMovementPlaneSource.cs.meta | 11 +
.../ECS/Components/AgentOffMeshLinkTraversal.cs | 388 +++++++++++++++++++++
.../Components/AgentOffMeshLinkTraversal.cs.meta | 11 +
.../Core/ECS/Components/AutoRepathPolicy.cs | 97 ++++++
.../Core/ECS/Components/AutoRepathPolicy.cs.meta | 11 +
.../Core/ECS/Components/DestinationPoint.cs | 26 ++
.../Core/ECS/Components/DestinationPoint.cs.meta | 11 +
.../Core/ECS/Components/GravityState.cs | 16 +
.../Core/ECS/Components/GravityState.cs.meta | 11 +
.../Core/ECS/Components/ManagedMovementOverride.cs | 91 +++++
.../ECS/Components/ManagedMovementOverride.cs.meta | 11 +
.../Core/ECS/Components/ManagedState.cs | 222 ++++++++++++
.../Core/ECS/Components/ManagedState.cs.meta | 11 +
.../Core/ECS/Components/MovementControl.cs | 85 +++++
.../Core/ECS/Components/MovementControl.cs.meta | 11 +
.../Core/ECS/Components/MovementSettings.cs | 113 ++++++
.../Core/ECS/Components/MovementSettings.cs.meta | 11 +
.../Core/ECS/Components/MovementState.cs | 196 +++++++++++
.../Core/ECS/Components/MovementState.cs.meta | 11 +
.../Core/ECS/Components/MovementStatistics.cs | 17 +
.../Core/ECS/Components/MovementStatistics.cs.meta | 11 +
.../Core/ECS/Components/RVO.meta | 8 +
.../Core/ECS/Components/RVO/AgentIndex.cs | 58 +++
.../Core/ECS/Components/RVO/AgentIndex.cs.meta | 11 +
.../Core/ECS/Components/RVO/RVOAgent.cs | 94 +++++
.../Core/ECS/Components/RVO/RVOAgent.cs.meta | 11 +
.../ECS/Components/ReadyToTraverseOffMeshLink.cs | 10 +
.../Components/ReadyToTraverseOffMeshLink.cs.meta | 11 +
.../Core/ECS/Components/ResolvedMovement.cs | 35 ++
.../Core/ECS/Components/ResolvedMovement.cs.meta | 11 +
.../Core/ECS/Components/SearchState.cs | 11 +
.../Core/ECS/Components/SearchState.cs.meta | 11 +
.../Core/ECS/Components/SimulateMovement.cs | 46 +++
.../Core/ECS/Components/SimulateMovement.cs.meta | 11 +
.../Core/ECS/Components/SyncWithTransform.cs | 35 ++
.../Core/ECS/Components/SyncWithTransform.cs.meta | 11 +
.../Core/ECS/EntityAccess.cs | 223 ++++++++++++
.../Core/ECS/EntityAccess.cs.meta | 11 +
.../Core/ECS/IRuntimeBaker.cs | 9 +
.../Core/ECS/IRuntimeBaker.cs.meta | 11 +
.../com.arongranberg.astar/Core/ECS/Jobs.meta | 8 +
.../ECS/Jobs/JobAlignAgentWithMovementDirection.cs | 43 +++
.../JobAlignAgentWithMovementDirection.cs.meta | 11 +
.../Core/ECS/Jobs/JobApplyGravity.cs | 75 ++++
.../Core/ECS/Jobs/JobApplyGravity.cs.meta | 11 +
.../Core/ECS/Jobs/JobControl.cs | 146 ++++++++
.../Core/ECS/Jobs/JobControl.cs.meta | 11 +
.../Core/ECS/Jobs/JobDrawFollowerGizmos.cs | 104 ++++++
.../Core/ECS/Jobs/JobDrawFollowerGizmos.cs.meta | 11 +
.../Core/ECS/Jobs/JobManagedMovementOverride.cs | 45 +++
.../ECS/Jobs/JobManagedMovementOverride.cs.meta | 11 +
.../ECS/Jobs/JobManagedOffMeshLinkTransition.cs | 86 +++++
.../Jobs/JobManagedOffMeshLinkTransition.cs.meta | 11 +
.../Core/ECS/Jobs/JobMoveAgent.cs | 95 +++++
.../Core/ECS/Jobs/JobMoveAgent.cs.meta | 11 +
.../Core/ECS/Jobs/JobPrepareAgentRaycasts.cs | 47 +++
.../Core/ECS/Jobs/JobPrepareAgentRaycasts.cs.meta | 11 +
.../Core/ECS/Jobs/JobRepairPath.cs | 277 +++++++++++++++
.../Core/ECS/Jobs/JobRepairPath.cs.meta | 11 +
.../Core/ECS/Jobs/JobStartOffMeshLinkTransition.cs | 33 ++
.../ECS/Jobs/JobStartOffMeshLinkTransition.cs.meta | 11 +
.../Core/ECS/Jobs/JobSyncEntitiesToTransforms.cs | 47 +++
.../ECS/Jobs/JobSyncEntitiesToTransforms.cs.meta | 11 +
.../com.arongranberg.astar/Core/ECS/Systems.meta | 8 +
.../Core/ECS/Systems/AIMoveSystem.cs | 268 ++++++++++++++
.../Core/ECS/Systems/AIMoveSystem.cs.meta | 11 +
.../Core/ECS/Systems/AIMovementSystemGroup.cs | 194 +++++++++++
.../Core/ECS/Systems/AIMovementSystemGroup.cs.meta | 11 +
.../ECS/Systems/FallbackResolveMovementSystem.cs | 51 +++
.../Systems/FallbackResolveMovementSystem.cs.meta | 11 +
.../Core/ECS/Systems/FollowerControlSystem.cs | 322 +++++++++++++++++
.../Core/ECS/Systems/FollowerControlSystem.cs.meta | 11 +
.../ECS/Systems/MovementPlaneFromGraphSystem.cs | 188 ++++++++++
.../Systems/MovementPlaneFromGraphSystem.cs.meta | 11 +
.../Core/ECS/Systems/PollPendingPathsSystem.cs | 84 +++++
.../ECS/Systems/PollPendingPathsSystem.cs.meta | 11 +
.../Core/ECS/Systems/RVOSystem.cs | 254 ++++++++++++++
.../Core/ECS/Systems/RVOSystem.cs.meta | 11 +
.../ECS/Systems/SyncDestinationTransformSystem.cs | 28 ++
.../Systems/SyncDestinationTransformSystem.cs.meta | 11 +
.../ECS/Systems/SyncTransformsToEntitiesSystem.cs | 91 +++++
.../Systems/SyncTransformsToEntitiesSystem.cs.meta | 11 +
88 files changed, 4807 insertions(+)
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentCylinderShape.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentCylinderShape.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlane.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlane.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlaneSource.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlaneSource.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentOffMeshLinkTraversal.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentOffMeshLinkTraversal.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AutoRepathPolicy.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AutoRepathPolicy.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/DestinationPoint.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/DestinationPoint.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/GravityState.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/GravityState.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedMovementOverride.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedMovementOverride.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedState.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedState.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementControl.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementControl.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementSettings.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementSettings.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementState.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementState.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementStatistics.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementStatistics.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/AgentIndex.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/AgentIndex.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/RVOAgent.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/RVOAgent.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ReadyToTraverseOffMeshLink.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ReadyToTraverseOffMeshLink.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ResolvedMovement.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ResolvedMovement.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SearchState.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SearchState.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SimulateMovement.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SimulateMovement.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SyncWithTransform.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SyncWithTransform.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/IRuntimeBaker.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/IRuntimeBaker.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobAlignAgentWithMovementDirection.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobAlignAgentWithMovementDirection.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobApplyGravity.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobApplyGravity.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobControl.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobControl.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobDrawFollowerGizmos.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobDrawFollowerGizmos.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedMovementOverride.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedMovementOverride.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobMoveAgent.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobMoveAgent.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobPrepareAgentRaycasts.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobPrepareAgentRaycasts.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobRepairPath.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobRepairPath.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobStartOffMeshLinkTransition.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobStartOffMeshLinkTransition.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobSyncEntitiesToTransforms.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobSyncEntitiesToTransforms.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/AIMoveSystem.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/AIMoveSystem.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/AIMovementSystemGroup.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/AIMovementSystemGroup.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/FallbackResolveMovementSystem.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/FallbackResolveMovementSystem.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/FollowerControlSystem.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/FollowerControlSystem.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/MovementPlaneFromGraphSystem.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/MovementPlaneFromGraphSystem.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/PollPendingPathsSystem.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/PollPendingPathsSystem.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/RVOSystem.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/RVOSystem.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/SyncDestinationTransformSystem.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/SyncDestinationTransformSystem.cs.meta
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/SyncTransformsToEntitiesSystem.cs
create mode 100644 Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Systems/SyncTransformsToEntitiesSystem.cs.meta
(limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS')
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components.meta
new file mode 100644
index 0000000..662ec62
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c496a3fafab9fab4a8c139fc7295f219
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentCylinderShape.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentCylinderShape.cs
new file mode 100644
index 0000000..9909d0f
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentCylinderShape.cs
@@ -0,0 +1,18 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+ using Pathfinding.ECS.RVO;
+
+ /// An agent's shape represented as a cylinder
+ [System.Serializable]
+ public struct AgentCylinderShape : IComponentData {
+ /// Radius of the agent in world units
+ public float radius;
+
+ /// Height of the agent in world units
+ public float height;
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentCylinderShape.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentCylinderShape.cs.meta
new file mode 100644
index 0000000..aa81963
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentCylinderShape.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e9dd6a4018eb50a48b69d83cb69a09b9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlane.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlane.cs
new file mode 100644
index 0000000..3321294
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlane.cs
@@ -0,0 +1,30 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+ using Pathfinding.Util;
+ using Unity.Mathematics;
+
+ /// Holds an agent's movement plane
+ [System.Serializable]
+ public struct AgentMovementPlane : IComponentData {
+ ///
+ /// The movement plane for the agent.
+ ///
+ /// The movement plane determines what the "up" direction of the agent is.
+ /// For most typical 3D games, this will be aligned with the Y axis, but there are
+ /// games in which the agent needs to navigate on walls, or on spherical worlds.
+ /// For those games this movement plane will track the plane in which the agent is currently moving.
+ ///
+ /// See: spherical (view in online documentation for working links)
+ ///
+ public NativeMovementPlane value;
+
+ /// Create a movement plane aligned with the XZ plane of the specified rotation
+ public AgentMovementPlane (quaternion rotation) {
+ value = new NativeMovementPlane(rotation);
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlane.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlane.cs.meta
new file mode 100644
index 0000000..9e6ebba
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlane.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b7137ab6e696b37428b1bec8e09e78ad
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlaneSource.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlaneSource.cs
new file mode 100644
index 0000000..1bcfbb4
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlaneSource.cs
@@ -0,0 +1,15 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+
+namespace Pathfinding.ECS {
+ ///
+ /// The movement plane source for an agent.
+ ///
+ /// See:
+ ///
+ [System.Serializable]
+ public struct AgentMovementPlaneSource : ISharedComponentData {
+ public MovementPlaneSource value;
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlaneSource.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlaneSource.cs.meta
new file mode 100644
index 0000000..e3a1b88
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentMovementPlaneSource.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a2198aa10fd82b94db223e3dbec9352b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentOffMeshLinkTraversal.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentOffMeshLinkTraversal.cs
new file mode 100644
index 0000000..0b79f59
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentOffMeshLinkTraversal.cs
@@ -0,0 +1,388 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+using Unity.Transforms;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+ using Pathfinding.Util;
+ using Unity.Collections.LowLevel.Unsafe;
+ using UnityEngine;
+
+ ///
+ /// Holds unmanaged information about an off-mesh link that the agent is currently traversing.
+ /// This component is added to the agent when it starts traversing an off-mesh link.
+ /// It is removed when the agent has finished traversing the link.
+ ///
+ /// See:
+ ///
+ public struct AgentOffMeshLinkTraversal : IComponentData {
+ /// \copydocref{OffMeshLinks.OffMeshLinkTracer.relativeStart}
+ public float3 relativeStart;
+
+ /// \copydocref{OffMeshLinks.OffMeshLinkTracer.relativeEnd}
+ public float3 relativeEnd;
+
+ /// \copydocref{OffMeshLinks.OffMeshLinkTracer.relativeStart}. Deprecated: Use relativeStart instead
+ [System.Obsolete("Use relativeStart instead")]
+ public float3 firstPosition => relativeStart;
+
+ /// \copydocref{OffMeshLinks.OffMeshLinkTracer.relativeEnd}. Deprecated: Use relativeEnd instead
+ [System.Obsolete("Use relativeEnd instead")]
+ public float3 secondPosition => relativeEnd;
+
+ /// \copydocref{OffMeshLinks.OffMeshLinkTracer.isReverse}
+ public bool isReverse;
+
+ public AgentOffMeshLinkTraversal (OffMeshLinks.OffMeshLinkTracer linkInfo) {
+ relativeStart = linkInfo.relativeStart;
+ relativeEnd = linkInfo.relativeEnd;
+ isReverse = linkInfo.isReverse;
+ }
+ }
+
+ ///
+ /// Holds managed information about an off-mesh link that the agent is currently traversing.
+ /// This component is added to the agent when it starts traversing an off-mesh link.
+ /// It is removed when the agent has finished traversing the link.
+ ///
+ /// See:
+ ///
+ public class ManagedAgentOffMeshLinkTraversal : IComponentData, System.ICloneable, ICleanupComponentData {
+ /// Internal context used to pass component data to the coroutine
+ public AgentOffMeshLinkTraversalContext context;
+
+ /// Coroutine which is used to traverse the link
+ public System.Collections.IEnumerator coroutine;
+ public IOffMeshLinkHandler handler;
+ public IOffMeshLinkStateMachine stateMachine;
+
+ public ManagedAgentOffMeshLinkTraversal() {}
+
+ public ManagedAgentOffMeshLinkTraversal (AgentOffMeshLinkTraversalContext context, IOffMeshLinkHandler handler) {
+ this.context = context;
+ this.handler = handler;
+ this.coroutine = null;
+ this.stateMachine = null;
+ }
+
+ public object Clone () {
+ // This will set coroutine and stateMachine to null.
+ // This is correct, as the coroutine cannot be cloned, and the state machine may be unique for a specific agent
+ return new ManagedAgentOffMeshLinkTraversal((AgentOffMeshLinkTraversalContext)context.Clone(), handler);
+ }
+ }
+
+ public struct MovementTarget {
+ internal bool isReached;
+ public bool reached => isReached;
+
+ public MovementTarget (bool isReached) {
+ this.isReached = isReached;
+ }
+ }
+
+ ///
+ /// Context with helpers for traversing an off-mesh link.
+ ///
+ /// This will be passed to the code that is responsible for traversing the off-mesh link.
+ ///
+ /// Warning: This context should never be accessed outside of an implementation of the interface.
+ ///
+ public class AgentOffMeshLinkTraversalContext : System.ICloneable {
+ internal unsafe AgentOffMeshLinkTraversal* linkInfoPtr;
+ internal unsafe MovementControl* movementControlPtr;
+ internal unsafe MovementSettings* movementSettingsPtr;
+ internal unsafe LocalTransform* transformPtr;
+ internal unsafe AgentMovementPlane* movementPlanePtr;
+
+ /// The entity that is traversing the off-mesh link
+ public Entity entity;
+
+ /// Some internal state of the agent
+ [Unity.Properties.DontCreateProperty]
+ public ManagedState managedState;
+
+ ///
+ /// The off-mesh link that is being traversed.
+ ///
+ /// See:
+ ///
+ [Unity.Properties.DontCreateProperty]
+ internal OffMeshLinks.OffMeshLinkConcrete concreteLink;
+
+ protected bool disabledRVO;
+ protected float backupRotationSmoothing = float.NaN;
+
+ ///
+ /// Delta time since the last link simulation.
+ ///
+ /// During high time scales, the simulation may run multiple substeps per frame.
+ ///
+ /// This is not the same as Time.deltaTime. Inside the link coroutine, you should always use this field instead of Time.deltaTime.
+ ///
+ public float deltaTime;
+
+ protected GameObject gameObjectCache;
+
+ ///
+ /// GameObject associated with the agent.
+ ///
+ /// In most cases, an agent is associated with an agent, but this is not always the case.
+ /// For example, if you have created an entity without using the component, this property may return null.
+ ///
+ /// Note: When directly modifying the agent's transform during a link traversal, you should use the property instead of modifying the GameObject's transform.
+ ///
+ public virtual GameObject gameObject {
+ get {
+ if (gameObjectCache == null) {
+ var follower = BatchedEvents.Find(entity, (follower, entity) => follower.entity == entity);
+ if (follower != null) gameObjectCache = follower.gameObject;
+ }
+ return gameObjectCache;
+ }
+ }
+
+ /// ECS LocalTransform component attached to the agent
+ public ref LocalTransform transform {
+ get {
+ unsafe {
+ return ref *transformPtr;
+ }
+ }
+ }
+
+ /// The movement settings for the agent
+ public ref MovementSettings movementSettings {
+ get {
+ unsafe {
+ return ref *movementSettingsPtr;
+ }
+ }
+ }
+
+ ///
+ /// How the agent should move.
+ ///
+ /// The agent will move according to this data, every frame.
+ ///
+ public ref MovementControl movementControl {
+ get {
+ unsafe {
+ return ref *movementControlPtr;
+ }
+ }
+ }
+
+ /// Information about the off-mesh link that the agent is traversing
+ public OffMeshLinks.OffMeshLinkTracer link {
+ get {
+ unsafe {
+ return new OffMeshLinks.OffMeshLinkTracer(concreteLink, linkInfoPtr->relativeStart, linkInfoPtr->relativeEnd, linkInfoPtr->isReverse);
+ }
+ }
+ }
+
+ ///
+ /// Information about the off-mesh link that the agent is traversing.
+ ///
+ /// Deprecated: Use the property instead
+ ///
+ [System.Obsolete("Use the link property instead")]
+ public AgentOffMeshLinkTraversal linkInfo {
+ get {
+ unsafe {
+ return *linkInfoPtr;
+ }
+ }
+ }
+
+ ///
+ /// The plane in which the agent is moving.
+ ///
+ /// In a 3D game, this will typically be the XZ plane, but in a 2D game
+ /// it will typically be the XY plane. Games on spherical planets could have planes that are aligned with the surface of the planet.
+ ///
+ public ref NativeMovementPlane movementPlane {
+ get {
+ unsafe {
+ return ref movementPlanePtr->value;
+ }
+ }
+ }
+
+ public AgentOffMeshLinkTraversalContext (OffMeshLinks.OffMeshLinkConcrete link) {
+ this.concreteLink = link;
+ }
+
+ ///
+ /// Internal method to set the data of the context.
+ ///
+ /// This is used by the job system to set the data of the context.
+ /// You should almost never need to use this.
+ ///
+ public virtual unsafe void SetInternalData (Entity entity, ref LocalTransform transform, ref AgentMovementPlane movementPlane, ref MovementControl movementControl, ref MovementSettings movementSettings, ref AgentOffMeshLinkTraversal linkInfo, ManagedState state, float deltaTime) {
+ this.linkInfoPtr = (AgentOffMeshLinkTraversal*)UnsafeUtility.AddressOf(ref linkInfo);
+ this.movementControlPtr = (MovementControl*)UnsafeUtility.AddressOf(ref movementControl);
+ this.movementSettingsPtr = (MovementSettings*)UnsafeUtility.AddressOf(ref movementSettings);
+ this.transformPtr = (LocalTransform*)UnsafeUtility.AddressOf(ref transform);
+ this.movementPlanePtr = (AgentMovementPlane*)UnsafeUtility.AddressOf(ref movementPlane);
+ this.managedState = state;
+ this.deltaTime = deltaTime;
+ this.entity = entity;
+ }
+
+ ///
+ /// Disables local avoidance for the agent.
+ ///
+ /// Agents that traverse links are already marked as 'unstoppable' by the local avoidance system,
+ /// but calling this method will make other agents ignore them completely while traversing the link.
+ ///
+ public void DisableLocalAvoidance () {
+ if (managedState.enableLocalAvoidance) {
+ disabledRVO = true;
+ managedState.enableLocalAvoidance = false;
+ }
+ }
+
+ ///
+ /// Disables rotation smoothing for the agent.
+ ///
+ /// This disables the effect of while the agent is traversing the link.
+ /// Having rotation smoothing enabled can make the agent rotate towards its target rotation more slowly,
+ /// which is sometimes not desirable.
+ ///
+ /// Rotation smoothing will automatically be restored when the agent finishes traversing the link (if it was enabled before).
+ ///
+ /// The method automatically disables rotation smoothing when called.
+ ///
+ public void DisableRotationSmoothing () {
+ if (float.IsNaN(backupRotationSmoothing) && movementSettings.rotationSmoothing > 0) {
+ backupRotationSmoothing = movementSettings.rotationSmoothing;
+ movementSettings.rotationSmoothing = 0;
+ }
+ }
+
+ ///
+ /// Restores the agent's settings to what it was before the link traversal started.
+ ///
+ /// This undos the changes made by and .
+ ///
+ /// This method is automatically called when the agent finishes traversing the link.
+ ///
+ public virtual void Restore () {
+ if (disabledRVO) {
+ managedState.enableLocalAvoidance = true;
+ disabledRVO = false;
+ }
+ if (!float.IsNaN(backupRotationSmoothing)) {
+ movementSettings.rotationSmoothing = backupRotationSmoothing;
+ backupRotationSmoothing = float.NaN;
+ }
+ }
+
+ /// Teleports the agent to the given position
+ public virtual void Teleport (float3 position) {
+ transform.Position = position;
+ }
+
+ ///
+ /// Thrown when the off-mesh link traversal should be aborted.
+ ///
+ /// See:
+ ///
+ public class AbortOffMeshLinkTraversal : System.Exception {}
+
+ ///
+ /// Aborts traversing the off-mesh link.
+ ///
+ /// This will immediately stop your off-mesh link traversal coroutine.
+ ///
+ /// This is useful if your agent was traversing an off-mesh link, but you have detected that it cannot continue.
+ /// Maybe the ladder it was climbing was destroyed, or the bridge it was walking on collapsed.
+ ///
+ /// Note: If you instead want to immediately make the agent move to the end of the link, you can call , and then use 'yield break;' from your coroutine.
+ ///
+ /// If true, the agent will be teleported back to the start of the link (from the perspective of the agent). Its rotation will remain unchanged.
+ public virtual void Abort (bool teleportToStart = true) {
+ if (teleportToStart) Teleport(link.relativeStart);
+ // Cancel the current path, as otherwise the agent will instantly try to traverse the off-mesh link again.
+ managedState.pathTracer.SetFromSingleNode(managedState.pathTracer.startNode, transform.Position, movementPlane);
+ throw new AbortOffMeshLinkTraversal();
+ }
+
+ ///
+ /// Move towards a point while ignoring the navmesh.
+ /// This method should be called repeatedly until the returned property is true.
+ ///
+ /// Returns: A struct which can be used to check if the target has been reached.
+ ///
+ /// Note: This method completely ignores the navmesh. It also overrides local avoidance, if enabled (other agents will still avoid it, but this agent will not avoid other agents).
+ ///
+ /// TODO: The gravity property is not yet implemented. Gravity is always applied.
+ ///
+ /// The position to move towards.
+ /// The rotation to rotate towards.
+ /// If true, gravity will be applied to the agent.
+ /// If true, the agent will slow down as it approaches the target.
+ public virtual MovementTarget MoveTowards (float3 position, quaternion rotation, bool gravity, bool slowdown) {
+ // If rotation smoothing was enabled, it could cause a very slow convergence to the target rotation.
+ // Therefore, we disable it here.
+ // The agent will try to remove its remaining rotation smoothing offset as quickly as possible.
+ // After the off-mesh link is traversed, the rotation smoothing will be automatically restored.
+ DisableRotationSmoothing();
+
+ var dirInPlane = movementPlane.ToPlane(position - transform.Position);
+ var remainingDistance = math.length(dirInPlane);
+ var maxSpeed = movementSettings.follower.Speed(slowdown ? remainingDistance : float.PositiveInfinity);
+ var speed = movementSettings.follower.Accelerate(movementControl.speed, movementSettings.follower.slowdownTime, deltaTime);
+ speed = math.min(speed, maxSpeed);
+
+ var targetRot = movementPlane.ToPlane(rotation);
+ var currentRot = movementPlane.ToPlane(transform.Rotation);
+ var remainingRot = Mathf.Abs(AstarMath.DeltaAngle(currentRot, targetRot));
+ movementControl = new MovementControl {
+ targetPoint = position,
+ endOfPath = position,
+ speed = speed,
+ maxSpeed = speed * 1.1f,
+ hierarchicalNodeIndex = -1,
+ overrideLocalAvoidance = true,
+ targetRotation = targetRot,
+ targetRotationHint = targetRot,
+ targetRotationOffset = 0,
+ rotationSpeed = math.radians(movementSettings.follower.rotationSpeed),
+ };
+
+ return new MovementTarget {
+ isReached = remainingDistance <= (slowdown ? 0.01f : speed * (1/30f)) && remainingRot < math.radians(1),
+ };
+ }
+
+ public virtual object Clone () {
+ var clone = (AgentOffMeshLinkTraversalContext)MemberwiseClone();
+ clone.entity = Entity.Null;
+ clone.gameObjectCache = null;
+ clone.managedState = null;
+ unsafe {
+ linkInfoPtr = null;
+ movementControlPtr = null;
+ movementSettingsPtr = null;
+ transformPtr = null;
+ movementPlanePtr = null;
+ }
+ return clone;
+ }
+ }
+}
+
+// ctx.MoveTowards (position, rotation, rvo = Auto | Disabled | AutoUnstoppable, gravity = auto|disabled) -> { reached() }
+
+// MovementTarget { ... }
+// while (!movementTarget.reached) {
+// ctx.SetMovementTarget(movementTarget);
+// yield return null;
+// }
+// yield return ctx.MoveTo(position, rotation)
+// ctx.TeleportTo(position, rotation)
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentOffMeshLinkTraversal.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentOffMeshLinkTraversal.cs.meta
new file mode 100644
index 0000000..28f8e71
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AgentOffMeshLinkTraversal.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e7b7b15e5b39fc142a7dd409c4c3a18d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AutoRepathPolicy.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AutoRepathPolicy.cs
new file mode 100644
index 0000000..a9324c1
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AutoRepathPolicy.cs
@@ -0,0 +1,97 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ ///
+ /// Policy for how often to recalculate an agent's path.
+ ///
+ /// See:
+ ///
+ /// This is the unmanaged equivalent of .
+ ///
+ [System.Serializable]
+ public struct AutoRepathPolicy : IComponentData {
+ ///
+ /// How sensitive the agent should be to changes in its destination for Mode.Dynamic.
+ /// A higher value means the destination has to move less for the path to be recalculated.
+ ///
+ /// See:
+ ///
+ public const float Sensitivity = 10.0f;
+
+ ///
+ /// Policy to use when recalculating paths.
+ ///
+ /// See: for more details.
+ ///
+ public Pathfinding.AutoRepathPolicy.Mode mode;
+
+ /// Number of seconds between each automatic path recalculation for Mode.EveryNSeconds, and the maximum interval for Mode.Dynamic
+ public float period;
+
+ float3 lastDestination;
+ float lastRepathTime;
+
+ public static AutoRepathPolicy Default => new AutoRepathPolicy {
+ mode = Pathfinding.AutoRepathPolicy.Mode.Dynamic,
+ period = 2,
+ lastDestination = float.PositiveInfinity,
+ lastRepathTime = float.NegativeInfinity
+ };
+
+ public AutoRepathPolicy (Pathfinding.AutoRepathPolicy policy) {
+ mode = policy.mode;
+ period = policy.mode == Pathfinding.AutoRepathPolicy.Mode.Dynamic ? policy.maximumPeriod : policy.period;
+ lastDestination = float.PositiveInfinity;
+ lastRepathTime = float.NegativeInfinity;
+ }
+
+ ///
+ /// True if the path should be recalculated according to the policy
+ ///
+ /// The above parameters are relevant only if is .
+ ///
+ /// The current position of the agent.
+ /// The radius of the agent. You may pass 0.0 if the agent doesn't have a radius.
+ /// The goal of the agent right now
+ /// The current time in seconds
+ public bool ShouldRecalculatePath (float3 position, float radius, float3 destination, float time) {
+ if (mode == Pathfinding.AutoRepathPolicy.Mode.Never || float.IsPositiveInfinity(destination.x)) return false;
+
+ float timeSinceLast = time - lastRepathTime;
+ if (mode == Pathfinding.AutoRepathPolicy.Mode.EveryNSeconds) {
+ return timeSinceLast >= period;
+ } else {
+ // cost = change in destination / max(distance to destination, radius)
+ float squaredCost = math.lengthsq(destination - lastDestination) / math.max(math.lengthsq(position - lastDestination), radius*radius);
+ float fraction = squaredCost * (Sensitivity*Sensitivity);
+ if (float.IsNaN(fraction)) {
+ // The agent's radius is zero, and the destination is precisely at the agent's position, which is also the destination of the last calculated path
+ // This is a special case. It happens sometimes for the AILerp component when it reaches its
+ // destination, as the AILerp component has no radius.
+ // In this case we just use the maximum period.
+ fraction = 0;
+ }
+
+ return timeSinceLast >= period*(1 - math.sqrt(fraction));
+ }
+ }
+
+ public void Reset () {
+ lastDestination = float.PositiveInfinity;
+ lastRepathTime = float.NegativeInfinity;
+ }
+
+ /// Must be called when a path request has been scheduled
+ public void DidRecalculatePath (float3 destination, float time) {
+ lastRepathTime = time;
+ lastDestination = destination;
+ // Randomize the repath time slightly so that all agents don't request a path at the same time
+ // in the future. This is useful when there are a lot of agents instantiated at exactly the same time.
+ const float JITTER_AMOUNT = 0.3f;
+ lastRepathTime -= (UnityEngine.Random.value - 0.5f) * JITTER_AMOUNT * period;
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AutoRepathPolicy.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AutoRepathPolicy.cs.meta
new file mode 100644
index 0000000..0f99f79
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/AutoRepathPolicy.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 49e745654af51f043a68a105c85e2bae
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/DestinationPoint.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/DestinationPoint.cs
new file mode 100644
index 0000000..d3ff623
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/DestinationPoint.cs
@@ -0,0 +1,26 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ /// Holds an agent's destination point
+ public struct DestinationPoint : IComponentData {
+ ///
+ /// The destination point that the agent is moving towards.
+ ///
+ /// This is the point that the agent is trying to reach, but it may not always be possible to reach it.
+ ///
+ /// See:
+ /// See:
+ ///
+ public float3 destination;
+
+ ///
+ /// The direction the agent should face when it reaches the destination.
+ ///
+ /// If zero, the agent will not try to face any particular direction when reaching the destination.
+ ///
+ public float3 facingDirection;
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/DestinationPoint.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/DestinationPoint.cs.meta
new file mode 100644
index 0000000..ca2370d
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/DestinationPoint.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 87fc1fca9dfafa64b98ec33b24a358fa
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/GravityState.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/GravityState.cs
new file mode 100644
index 0000000..4ab8700
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/GravityState.cs
@@ -0,0 +1,16 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ /// Agent state related to gravity
+ public struct GravityState : IComponentData, IEnableableComponent {
+ ///
+ /// Current vertical velocity of the agent.
+ /// This is the velocity that the agent is moving with due to gravity.
+ /// It is not necessarily the same as the Y component of the estimated velocity.
+ ///
+ public float verticalVelocity;
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/GravityState.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/GravityState.cs.meta
new file mode 100644
index 0000000..2275bbb
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/GravityState.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ed943911778141b4988cbdcd7f5b3a07
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedMovementOverride.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedMovementOverride.cs
new file mode 100644
index 0000000..4f45dab
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedMovementOverride.cs
@@ -0,0 +1,91 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+
+namespace Pathfinding.ECS {
+ using Unity.Transforms;
+
+ public delegate void BeforeControlDelegate(Entity entity, float dt, ref LocalTransform localTransform, ref AgentCylinderShape shape, ref AgentMovementPlane movementPlane, ref DestinationPoint destination, ref MovementState movementState, ref MovementSettings movementSettings);
+ public delegate void AfterControlDelegate(Entity entity, float dt, ref LocalTransform localTransform, ref AgentCylinderShape shape, ref AgentMovementPlane movementPlane, ref DestinationPoint destination, ref MovementState movementState, ref MovementSettings movementSettings, ref MovementControl movementControl);
+ public delegate void BeforeMovementDelegate(Entity entity, float dt, ref LocalTransform localTransform, ref AgentCylinderShape shape, ref AgentMovementPlane movementPlane, ref DestinationPoint destination, ref MovementState movementState, ref MovementSettings movementSettings, ref MovementControl movementControl, ref ResolvedMovement resolvedMovement);
+
+ ///
+ /// Helper for adding and removing hooks to the FollowerEntity component.
+ /// This is used to allow other systems to override the movement of the agent.
+ ///
+ /// See:
+ ///
+ public ref struct ManagedMovementOverrides {
+ Entity entity;
+ World world;
+
+ public ManagedMovementOverrides (Entity entity, World world) {
+ this.entity = entity;
+ this.world = world;
+ }
+
+ public void AddBeforeControlCallback (BeforeControlDelegate value) {
+ AddCallback(value);
+ }
+ public void RemoveBeforeControlCallback (BeforeControlDelegate value) {
+ RemoveCallback(value);
+ }
+
+ public void AddAfterControlCallback (AfterControlDelegate value) {
+ AddCallback(value);
+ }
+ public void RemoveAfterControlCallback (AfterControlDelegate value) {
+ RemoveCallback(value);
+ }
+
+ public void AddBeforeMovementCallback (BeforeMovementDelegate value) {
+ AddCallback(value);
+ }
+ public void RemoveBeforeMovementCallback (BeforeMovementDelegate value) {
+ RemoveCallback(value);
+ }
+
+ void AddCallback(T callback) where T : System.Delegate where C : ManagedMovementOverride, IComponentData, new() {
+ if (callback == null) throw new System.ArgumentNullException(nameof(callback));
+ if (world == null || !world.EntityManager.Exists(entity)) throw new System.InvalidOperationException("The entity does not exist. You can only set a callback when the FollowerEntity is active and has been enabled. If you are trying to set this during Awake or OnEnable, try setting it during Start instead.");
+ if (!world.EntityManager.HasComponent(entity)) world.EntityManager.AddComponentData(entity, new C());
+ world.EntityManager.GetComponentData(entity).AddCallback(callback);
+ }
+
+ void RemoveCallback(T callback) where T : System.Delegate where C : ManagedMovementOverride, IComponentData, new() {
+ if (callback == null) throw new System.ArgumentNullException(nameof(callback));
+ if (world == null || !world.EntityManager.Exists(entity)) return;
+ if (!world.EntityManager.HasComponent(entity)) return;
+
+ var comp = world.EntityManager.GetComponentData(entity);
+ if (!comp.RemoveCallback(callback)) {
+ world.EntityManager.RemoveComponent(entity);
+ }
+ }
+ }
+
+ ///
+ /// Stores a delegate that can be used to override movement control and movement settings for a specific entity.
+ /// This is used by the FollowerEntity to allow other systems to override the movement of the entity.
+ ///
+ /// See:
+ ///
+ public class ManagedMovementOverride : IComponentData where T : class, System.Delegate {
+ public T callback;
+
+ public void AddCallback(T callback) => this.callback = (T)System.Delegate.Combine(this.callback, callback);
+ public bool RemoveCallback(T callback) => (this.callback = (T)System.Delegate.Remove(this.callback, callback)) != null;
+ }
+
+ // IJobEntity does not support generic jobs yet, so we have to make concrete component types for each delegate type
+ public class ManagedMovementOverrideBeforeControl : ManagedMovementOverride, System.ICloneable {
+ // No fields in this class can be cloned safely
+ public object Clone() => new ManagedMovementOverrideBeforeControl();
+ }
+ public class ManagedMovementOverrideAfterControl : ManagedMovementOverride {
+ public object Clone() => new ManagedMovementOverrideAfterControl();
+ }
+ public class ManagedMovementOverrideBeforeMovement : ManagedMovementOverride {
+ public object Clone() => new ManagedMovementOverrideBeforeMovement();
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedMovementOverride.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedMovementOverride.cs.meta
new file mode 100644
index 0000000..409fc88
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedMovementOverride.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ce6a314668bdcdd498d5d9d3ebf753c2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedState.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedState.cs
new file mode 100644
index 0000000..d219541
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedState.cs
@@ -0,0 +1,222 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+ using Pathfinding.ECS.RVO;
+ using UnityEngine.Serialization;
+
+ ///
+ /// Settings for agent movement that cannot be put anywhere else.
+ ///
+ /// The Unity ECS in general wants everything in components to be unmanaged types.
+ /// However, some things cannot be unmanaged types, for example delegates and interfaces.
+ /// There are also other things like path references and node references which are not unmanaged types at the moment.
+ ///
+ /// This component is used to store those things.
+ ///
+ /// It can also be used for things that are not used often, and so are best kept out-of-band to avoid bloating the ECS chunks too much.
+ ///
+ [System.Serializable]
+ public class ManagedState : IComponentData, System.IDisposable, System.ICloneable {
+ ///
+ /// Settings for when to recalculate the path.
+ ///
+ /// Deprecated: Use , or the component instead.
+ ///
+ [System.Obsolete("Use FollowerEntity.autoRepath, or the Pathfinding.ECS.AutoRepathPolicy component instead")]
+ public Pathfinding.AutoRepathPolicy autoRepath = new Pathfinding.AutoRepathPolicy();
+
+ /// Calculates in which direction to move to follow the path
+ public PathTracer pathTracer;
+
+ ///
+ /// Local avoidance settings.
+ ///
+ /// When the agent has local avoidance enabled, these settings will be copied into a component which is attached to the agent.
+ ///
+ /// See:
+ ///
+ [FormerlySerializedAs("rvoAgent")]
+ public RVOAgent rvoSettings = RVOAgent.Default;
+
+ /// Callback for when the agent starts to traverse an off-mesh link
+ [System.NonSerialized]
+ public IOffMeshLinkHandler onTraverseOffMeshLink;
+
+ public PathRequestSettings pathfindingSettings = PathRequestSettings.Default;
+
+ ///
+ /// True if local avoidance is enabled for this agent.
+ ///
+ /// Enabling this will automatically add a component to the entity.
+ ///
+ /// See: local-avoidance (view in online documentation for working links)
+ ///
+ [FormerlySerializedAs("rvoEnabled")]
+ public bool enableLocalAvoidance;
+
+ ///
+ /// True if gravity is enabled for this agent.
+ ///
+ /// The agent will always fall down according to its own movement plane.
+ /// The gravity applied is Physics.gravity.y.
+ ///
+ /// Enabling this will add the component to the entity.
+ ///
+ public bool enableGravity = true;
+
+ /// Path that is being calculated, if any
+ // Do not create a property visitor for this field, as otherwise the ECS infrastructure will try to patch entities inside it, and get very confused.
+ // I haven't been able to replicate this issue recently, but it has caused problems in the past.
+ // [Unity.Properties.DontCreateProperty]
+ public Path pendingPath { get; private set; }
+
+ ///
+ /// Path that is being followed, if any.
+ ///
+ /// The agent may have moved away from this path since it was calculated. So it may not be up to date.
+ ///
+ // Do not create a property visitor for this field, as otherwise the ECS infrastructure will try to patch entities inside it, and get very confused.
+ // [Unity.Properties.DontCreateProperty]
+ public Path activePath { get; private set; }
+
+ ///
+ /// \copydocref{IAstarAI.SetPath}.
+ ///
+ /// Warning: In almost all cases you should use instead of this method.
+ ///
+ public static void SetPath (Path path, ManagedState state, in AgentMovementPlane movementPlane, ref DestinationPoint destination) {
+ if (path == null) {
+ state.CancelCurrentPathRequest();
+ state.ClearPath();
+ } else if (path.PipelineState == PathState.Created) {
+ // Path has not started calculation yet
+ state.CancelCurrentPathRequest();
+ state.pendingPath = path;
+ path.Claim(state);
+ AstarPath.StartPath(path);
+ } else if (path.PipelineState >= PathState.ReturnQueue) {
+ // Path has already been calculated
+
+ if (state.pendingPath == path) {
+ // The pending path is now obviously no longer pending
+ state.pendingPath = null;
+ } else {
+ // We might be calculating another path at the same time, and we don't want that path to override this one. So cancel it.
+ state.CancelCurrentPathRequest();
+
+ // Increase the refcount on the path.
+ // If the path was already our pending path, then the refcount will have already been incremented
+ path.Claim(state);
+ }
+
+ var abPath = path as ABPath;
+ if (abPath == null) throw new System.ArgumentException("This function only works with ABPaths, or paths inheriting from ABPath");
+
+ if (!abPath.error) {
+ try {
+ state.pathTracer.SetPath(abPath, movementPlane.value);
+
+ // Release the previous path back to the pool, to reduce GC pressure
+ if (state.activePath != null) state.activePath.Release(state);
+
+ state.activePath = abPath;
+ } catch (System.Exception e) {
+ // If the path was so invalid that the path tracer throws an exception, then we should not use it.
+ abPath.Release(state);
+ state.ClearPath();
+ UnityEngine.Debug.LogException(e);
+ }
+
+ // If a RandomPath or MultiTargetPath have just been calculated, then we need
+ // to patch our destination point, to ensure the agent continues to move towards the end of the path.
+ // For these path types, the end point of the path is not known before the calculation starts.
+ if (!abPath.endPointKnownBeforeCalculation) {
+ destination = new DestinationPoint { destination = abPath.originalEndPoint, facingDirection = default };
+ }
+
+ // Right now, the pathTracer is almost fully up to date.
+ // To make it fully up to date, we'd also have to call pathTracer.UpdateStart and pathTracer.UpdateEnd after this function.
+ // During normal path recalculations, the JobRepairPath will be scheduled right after this function, and it will
+ // call those functions. The incomplete state will not be observable outside the system.
+ // When called from FollowerEntity, the SetPath method on that component will ensure that these methods are called.
+ } else {
+ abPath.Release(state);
+ }
+ } 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.");
+ }
+ }
+
+ public void ClearPath () {
+ pathTracer.Clear();
+ if (activePath != null) {
+ activePath.Release(this);
+ activePath = null;
+ }
+ }
+
+ public void CancelCurrentPathRequest () {
+ if (pendingPath != null) {
+ pendingPath.FailWithError("Canceled by script");
+ pendingPath.Release(this);
+ pendingPath = null;
+ }
+ }
+
+ public void Dispose () {
+ pathTracer.Dispose();
+ if (pendingPath != null) {
+ pendingPath.FailWithError("Canceled because entity was destroyed");
+ pendingPath.Release(this);
+ pendingPath = null;
+ }
+ if (activePath != null) {
+ activePath.Release(this);
+ activePath = null;
+ }
+ }
+
+ ///
+ /// Pops the current part, and the next part from the start of the path.
+ ///
+ /// It is assumed that the agent is currently on a normal NodeSequence part, and that the next part in the path is an off-mesh link.
+ ///
+ public void PopNextLinkFromPath () {
+ if (pathTracer.partCount < 2 && pathTracer.GetPartType(1) != Funnel.PartType.OffMeshLink) {
+ throw new System.InvalidOperationException("The next part in the path is not an off-mesh link.");
+ }
+ pathTracer.PopParts(2, pathfindingSettings.traversalProvider, activePath);
+ }
+
+ ///
+ /// Clones the managed state for when an entity is duplicated.
+ ///
+ /// Some fields are cleared instead of being cloned, such as the pending path,
+ /// which cannot reasonably be cloned.
+ ///
+ object System.ICloneable.Clone () {
+ return new ManagedState {
+ #pragma warning disable 618
+ autoRepath = autoRepath.Clone(),
+ #pragma warning restore 618
+ pathTracer = pathTracer.Clone(),
+ rvoSettings = rvoSettings,
+ pathfindingSettings = new PathRequestSettings {
+ graphMask = pathfindingSettings.graphMask,
+ tagPenalties = pathfindingSettings.tagPenalties != null ? (int[])pathfindingSettings.tagPenalties.Clone() : null,
+ traversableTags = pathfindingSettings.traversableTags,
+ traversalProvider = null, // Cannot be safely cloned or copied
+ },
+ enableLocalAvoidance = enableLocalAvoidance,
+ enableGravity = enableGravity,
+ onTraverseOffMeshLink = null, // Cannot be safely cloned or copied
+ pendingPath = null, // Cannot be safely cloned or copied
+ activePath = null, // Cannot be safely cloned or copied
+ };
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedState.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedState.cs.meta
new file mode 100644
index 0000000..6320c9e
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ManagedState.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 33d1c95731798be41b90302b91409645
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementControl.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementControl.cs
new file mode 100644
index 0000000..dfd03eb
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementControl.cs
@@ -0,0 +1,85 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+ using Pathfinding.Util;
+
+ ///
+ /// Desired movement for an agent.
+ /// This data will be fed to the local avoidance system to calculate the final movement of the agent.
+ /// If no local avoidance is used, it will be directly copied to .
+ ///
+ /// See:
+ ///
+ public struct MovementControl : IComponentData {
+ /// The point the agent should move towards
+ public float3 targetPoint;
+
+ ///
+ /// The end of the current path.
+ ///
+ /// This informs the local avoidance system about the final desired destination for the agent.
+ /// This is used to make agents stop if the destination is crowded and it cannot reach its destination.
+ ///
+ /// If this is not set, agents will often move forever around a crowded destination, always trying to find
+ /// some way to get closer, but never finding it.
+ ///
+ public float3 endOfPath;
+
+ /// The speed at which the agent should move towards , in meters per second
+ public float speed;
+
+ ///
+ /// The maximum speed at which the agent may move, in meters per second.
+ ///
+ /// It is recommended to keep this slightly above , to allow the local avoidance system to move agents around more efficiently when necessary.
+ ///
+ public float maxSpeed;
+
+ ///
+ /// The index of the hierarchical node that the agent is currently in.
+ /// Will be -1 if the hierarchical node index is not known.
+ /// See:
+ ///
+ public int hierarchicalNodeIndex;
+
+ ///
+ /// The desired rotation of the agent, in radians, relative to the current movement plane.
+ /// See:
+ ///
+ public float targetRotation;
+
+ ///
+ /// The desired rotation of the agent, in radians, over a longer time horizon, relative to the current movement plane.
+ ///
+ /// The is usually only over a very short time-horizon, usually a single simulation time step.
+ /// This variable is used to provide a hint of where the agent wants to rotate to over a slightly longer time scale (on the order of a second or so).
+ /// It is not used to control movement directly, but it may be used to guide animations, or rotation smoothing.
+ ///
+ /// If no better hint is available, this should be set to the same value as .
+ ///
+ /// See:
+ ///
+ public float targetRotationHint;
+
+ ///
+ /// Additive modifier to , in radians.
+ /// This is used by the local avoidance system to rotate the agent, without this causing a feedback loop.
+ /// This extra rotation will be ignored by the control system which decides how the agent *wants* to move.
+ /// It will instead be directly applied to the agent.
+ ///
+ public float targetRotationOffset;
+
+ /// The speed at which the agent should rotate towards + , in radians per second
+ public float rotationSpeed;
+
+ ///
+ /// If true, this agent will ignore other agents during local avoidance, but other agents will still avoid this one.
+ /// This is useful for example for a player character which should not avoid other agents, but other agents should avoid the player.
+ ///
+ public bool overrideLocalAvoidance;
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementControl.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementControl.cs.meta
new file mode 100644
index 0000000..4c0b87a
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementControl.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 067b0510e83c84e43b21eb81fb804132
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementSettings.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementSettings.cs
new file mode 100644
index 0000000..b6e2bef
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementSettings.cs
@@ -0,0 +1,113 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using UnityEngine;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ using Pathfinding.PID;
+
+ /// How to calculate which direction is "up" for the agent
+ public enum MovementPlaneSource : byte {
+ ///
+ /// The graph's natural up direction will be used to align the agent.
+ /// This is the most common option.
+ ///
+ Graph,
+ ///
+ /// The agent will be aligned with the normal of the navmesh.
+ ///
+ /// This is useful when you have a spherical world, or some other strange shape.
+ ///
+ /// The agent will look at the normal of the navmesh around the point it is currently standing on to determine which way is up.
+ /// The radius of the agent will be used to determine the size of the area to sample the normal from.
+ /// A bit of smoothing is done to make sure sharp changes in the normal do not cause the agent to rotate too fast.
+ ///
+ /// Note: If you have a somewhat flat world, and you want to align the agent to the ground, this is not the option you want.
+ /// Instead, you might want to disable and then align the transform using a custom script.
+ ///
+ /// Warning: Using this option has a performance penalty.
+ ///
+ /// [Open online documentation to see videos]
+ ///
+ /// See: spherical (view in online documentation for working links)
+ ///
+ NavmeshNormal,
+ ///
+ /// The agent will be aligned with the ground normal.
+ ///
+ /// This is useful when you have a spherical world, or some other strange shape.
+ ///
+ /// You may want to use this instead of the NavmeshNormal option if your collider is smoother than your navmesh.
+ /// For example, if you have a spherical world with a sphere collider, you may want to use this option instead of the NavmeshNormal option.
+ ///
+ /// Note: If you have a somewhat flat world, and you want to align the agent to the ground, this is not the option you want.
+ /// Instead, you might want to disable and then align the transform using a custom script.
+ ///
+ /// Warning: Using this option has a performance penalty.
+ ///
+ Raycast,
+ }
+
+ [System.Serializable]
+ public struct MovementSettings : IComponentData {
+ /// Additional movement settings
+ public PIDMovement follower;
+
+ /// Flags for enabling debug rendering in the scene view
+ public PIDMovement.DebugFlags debugFlags;
+
+ ///
+ /// How far away from the destination should the agent aim to stop, in world units.
+ ///
+ /// If the agent is within this distance from the destination point it will be considered to have reached the destination.
+ ///
+ /// Even if you want the agent to stop precisely at a given point, it is recommended to keep this slightly above zero.
+ /// If it is exactly zero, the agent may have a hard time deciding that it
+ /// has actually reached the end of the path, due to floating point errors and such.
+ ///
+ /// Note: This will not be multiplied the agent's scale.
+ ///
+ public float stopDistance;
+
+ ///
+ /// How much to smooth the visual rotation of the agent.
+ ///
+ /// This does not affect movement, but smoothes out how the agent rotates visually.
+ ///
+ /// Recommended values are between 0.0 and 0.5.
+ /// A value of zero will disable smoothing completely.
+ ///
+ /// The smoothing is done primarily using an exponential moving average, but with
+ /// a small linear term to make the rotation converge faster when the agent is almost facing the desired direction.
+ ///
+ /// Adding smoothing will make the visual rotation of the agent lag a bit behind the actual rotation.
+ /// Too much smoothing may make the agent seem sluggish, and appear to move sideways.
+ ///
+ /// The unit for this field is seconds.
+ ///
+ public float rotationSmoothing;
+ public float positionSmoothing;
+
+ ///
+ /// 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;
+
+ ///
+ /// How to calculate which direction is "up" for the agent.
+ /// See:
+ ///
+ /// Deprecated: Use the AgentMovementPlaneSource component instead, or the movementPlaneSource property on the FollowerEntity component
+ ///
+ [System.Obsolete("Use the AgentMovementPlaneSource component instead, or the movementPlaneSource property on the FollowerEntity component")]
+ public MovementPlaneSource movementPlaneSource;
+
+ /// \copydocref{IAstarAI.isStopped}
+ public bool isStopped;
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementSettings.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementSettings.cs.meta
new file mode 100644
index 0000000..734fcc4
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementSettings.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a4fafdd860735074e8ca2abad75c3992
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementState.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementState.cs
new file mode 100644
index 0000000..0ea83ad
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementState.cs
@@ -0,0 +1,196 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+ using Pathfinding.PID;
+
+ public struct MovementState : IComponentData {
+ /// State of the PID controller for the movement
+ public PIDMovement.PersistentState followerState;
+
+ /// The next corner in the path
+ public float3 nextCorner;
+
+ ///
+ /// The end of the current path.
+ /// Note that the agent may be heading towards an off-mesh link which is not the same as this point.
+ ///
+ public float3 endOfPath;
+
+ ///
+ /// The closest point on the navmesh to the agent.
+ /// The agent will be snapped to this point.
+ ///
+ public float3 closestOnNavmesh;
+
+ ///
+ /// Offset from the agent's internal position to its visual position.
+ ///
+ /// This is used when position smoothing is enabled. Otherwise it is zero.
+ ///
+ public float3 positionOffset;
+
+ ///
+ /// The index of the hierarchical node that the agent is currently in.
+ /// Will be -1 if the hierarchical node index is not known.
+ ///
+ /// This field is valid during all system updates in the . It is not guaranteed to be valid after that group has finished running, as graph updates may have changed the graph.
+ ///
+ /// See:
+ ///
+ public int hierarchicalNodeIndex;
+
+ /// The remaining distance until the end of the path, or the next off-mesh link
+ public float remainingDistanceToEndOfPart;
+
+ ///
+ /// The current additional rotation that is applied to the agent.
+ /// This is used by the local avoidance system to rotate the agent, without this causing a feedback loop.
+ ///
+ /// See:
+ ///
+ public float rotationOffset;
+
+ ///
+ /// An additional, purely visual, rotation offset.
+ /// This is used for rotation smoothing, but does not affect the movement of the agent.
+ ///
+ public float rotationOffset2;
+
+ ///
+ /// Version number of when the movement state was last updated.
+ /// In particular, , , , , and will only
+ /// be considered up to date if this is equal to the current version number of the path tracer.
+ ///
+ public ushort pathTracerVersion;
+
+ /// Bitmask for various flags
+ ushort flags;
+
+ const int ReachedDestinationFlag = 1 << 0;
+ const int reachedDestinationAndOrientationFlag = 1 << 1;
+ const int ReachedEndOfPathFlag = 1 << 2;
+ const int reachedEndOfPathAndOrientationFlag = 1 << 3;
+ const int ReachedEndOfPartFlag = 1 << 4;
+ const int TraversingLastPartFlag = 1 << 5;
+
+ ///
+ /// True if the agent has reached its destination.
+ /// The destination will be considered reached if all of these conditions are met:
+ /// - The agent has a path
+ /// - The path is not stale
+ /// - The destination is not significantly below the agent's feet.
+ /// - The destination is not significantly above the agent's head.
+ /// - The agent is on the last part of the path (there are no more remaining off-mesh links).
+ /// - The remaining distance to the end of the path + the distance from the end of the path to the destination is less than .
+ ///
+ public bool reachedDestination {
+ get => (flags & ReachedDestinationFlag) != 0;
+ set => flags = (ushort)((flags & ~ReachedDestinationFlag) | (value ? ReachedDestinationFlag : 0));
+ }
+
+ ///
+ /// True if the agent has reached its destination and is facing the desired orientation.
+ /// This will become true if all of these conditions are met:
+ /// - is true
+ /// - The agent is facing the desired facing direction as specified in .
+ ///
+ public bool reachedDestinationAndOrientation {
+ get => (flags & reachedDestinationAndOrientationFlag) != 0;
+ set => flags = (ushort)((flags & ~reachedDestinationAndOrientationFlag) | (value ? reachedDestinationAndOrientationFlag : 0));
+ }
+
+ ///
+ /// True if the agent has reached the end of the path.
+ /// The end of the path will be considered reached if all of these conditions are met:
+ /// - The agent has a path
+ /// - The path is not stale
+ /// - The end of the path is not significantly below the agent's feet.
+ /// - The end of the path is not significantly above the agent's head.
+ /// - The agent is on the last part of the path (there are no more remaining off-mesh links).
+ /// - The remaining distance to the end of the path is less than .
+ ///
+ public bool reachedEndOfPath {
+ get => (flags & ReachedEndOfPathFlag) != 0;
+ set => flags = (ushort)((flags & ~ReachedEndOfPathFlag) | (value ? ReachedEndOfPathFlag : 0));
+ }
+
+ ///
+ /// True if the agent has reached its destination and is facing the desired orientation.
+ /// This will become true if all of these conditions are met:
+ /// - is true
+ /// - The agent is facing the desired facing direction as specified in .
+ ///
+ public bool reachedEndOfPathAndOrientation {
+ get => (flags & reachedEndOfPathAndOrientationFlag) != 0;
+ set => flags = (ushort)((flags & ~reachedEndOfPathAndOrientationFlag) | (value ? reachedEndOfPathAndOrientationFlag : 0));
+ }
+
+ ///
+ /// True if the agent has reached the end of the current part in the path.
+ /// The end of the current part will be considered reached if all of these conditions are met:
+ /// - The agent has a path
+ /// - The path is not stale
+ /// - The end of the current part is not significantly below the agent's feet.
+ /// - The end of the current part is not significantly above the agent's head.
+ /// - The remaining distance to the end of the part is not significantly larger than the agent's radius.
+ ///
+ public bool reachedEndOfPart {
+ get => (flags & ReachedEndOfPartFlag) != 0;
+ set => flags = (ushort)((flags & ~ReachedEndOfPartFlag) | (value ? ReachedEndOfPartFlag : 0));
+ }
+
+ ///
+ /// True if the agent is traversing the last part of the path.
+ ///
+ /// If false, the agent will have to traverse at least one off-mesh link before it gets to its destination.
+ ///
+ public bool traversingLastPart {
+ get => (flags & TraversingLastPartFlag) != 0;
+ set => flags = (ushort)((flags & ~TraversingLastPartFlag) | (value ? TraversingLastPartFlag : 0));
+ }
+
+ ///
+ /// The index of the graph that the agent is currently traversing.
+ ///
+ /// Will be if the agent has no path, or the node that the agent is traversing has been destroyed.
+ ///
+ public uint graphIndex {
+ get => (uint)(flags >> 8);
+ internal set => flags = (ushort)((flags & 0xFF) | (ushort)(value << 8));
+ }
+
+ ///
+ /// True if the agent is currently on a valid node.
+ ///
+ /// This is true if the agent has a path, and the node that the agent is traversing is walkable and not destroyed.
+ ///
+ /// If false, the and fields are invalid.
+ ///
+ public bool isOnValidNode => hierarchicalNodeIndex != -1;
+
+ public MovementState(UnityEngine.Vector3 agentPosition) {
+ this = default;
+ SetPathIsEmpty(agentPosition);
+ }
+
+ /// Sets the appropriate fields to indicate that the agent has no path
+ public void SetPathIsEmpty (UnityEngine.Vector3 agentPosition) {
+ nextCorner = agentPosition;
+ endOfPath = agentPosition;
+ closestOnNavmesh = agentPosition;
+ hierarchicalNodeIndex = -1;
+ remainingDistanceToEndOfPart = float.PositiveInfinity;
+ reachedEndOfPath = false;
+ reachedDestination = false;
+ reachedEndOfPart = false;
+ reachedDestinationAndOrientation = false;
+ reachedEndOfPathAndOrientation = false;
+ traversingLastPart = true;
+ graphIndex = GraphNode.InvalidGraphIndex;
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementState.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementState.cs.meta
new file mode 100644
index 0000000..71f1c38
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementState.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cd27960b09d0034419af8c9451a551fb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementStatistics.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementStatistics.cs
new file mode 100644
index 0000000..d29781c
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementStatistics.cs
@@ -0,0 +1,17 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ public struct MovementStatistics : IComponentData {
+ ///
+ /// The estimated velocity that the agent is moving with.
+ /// This includes all form of movement, including local avoidance and gravity.
+ ///
+ public float3 estimatedVelocity;
+
+ /// The position of the agent at the end of the last movement simulation step
+ public float3 lastPosition;
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementStatistics.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementStatistics.cs.meta
new file mode 100644
index 0000000..3b36a89
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/MovementStatistics.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 40111712788bfc3409f6b39341f91e2a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO.meta
new file mode 100644
index 0000000..30598c4
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 233cdeb50c94c714ab4c82711f977368
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/AgentIndex.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/AgentIndex.cs
new file mode 100644
index 0000000..72d4c4c
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/AgentIndex.cs
@@ -0,0 +1,58 @@
+using Pathfinding.RVO;
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Transforms;
+#endif
+using UnityEngine;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS.RVO {
+ using Pathfinding.RVO;
+
+ ///
+ /// Index of an RVO agent in the local avoidance simulation.
+ ///
+ /// If this component is present, that indicates that the agent is part of a local avoidance simulation.
+ /// The is responsible for adding and removing this component as necessary.
+ /// Any other systems should only concern themselves with the component.
+ ///
+ /// Warning: This component does not support cloning. You must not clone entities that use this component.
+ /// There doesn't seem to be any way to make this work with the Unity.Entities API at the moment.
+ ///
+#if MODULE_ENTITIES
+ [WriteGroup(typeof(ResolvedMovement))]
+#endif
+ public readonly struct AgentIndex
+#if MODULE_ENTITIES
+ : Unity.Entities.ICleanupComponentData
+#endif
+ {
+ internal const int DeletedBit = 1 << 31;
+ internal const int IndexMask = (1 << 24) - 1;
+ internal const int VersionOffset = 24;
+ internal const int VersionMask = 0b1111_111 << VersionOffset;
+
+ public readonly int packedAgentIndex;
+ public int Index => packedAgentIndex & IndexMask;
+ public int Version => packedAgentIndex & VersionMask;
+ public bool Valid => (packedAgentIndex & DeletedBit) == 0;
+
+ public AgentIndex(int packedAgentIndex) {
+ this.packedAgentIndex = packedAgentIndex;
+ }
+
+ public AgentIndex(int version, int index) {
+ version <<= VersionOffset;
+ UnityEngine.Assertions.Assert.IsTrue((index & IndexMask) == index);
+ packedAgentIndex = (version & VersionMask) | (index & IndexMask);
+ }
+
+ public AgentIndex WithIncrementedVersion () {
+ return new AgentIndex((((packedAgentIndex & VersionMask) + (1 << VersionOffset)) & VersionMask) | Index);
+ }
+
+ public AgentIndex WithDeleted () {
+ return new AgentIndex(packedAgentIndex | DeletedBit);
+ }
+ }
+}
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/AgentIndex.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/AgentIndex.cs.meta
new file mode 100644
index 0000000..b67b67e
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/AgentIndex.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cd00f859416fc5c4f984c5680d19fc7d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/RVOAgent.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/RVOAgent.cs
new file mode 100644
index 0000000..5049190
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/RVOAgent.cs
@@ -0,0 +1,94 @@
+#if MODULE_ENTITIES
+using Pathfinding.RVO;
+using Unity.Entities;
+using UnityEngine;
+using Unity.Transforms;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS.RVO {
+ using Pathfinding.RVO;
+
+ ///
+ /// Agent data for the local avoidance system.
+ ///
+ /// See: local-avoidance (view in online documentation for working links)
+ ///
+ [System.Serializable]
+ public struct RVOAgent : IComponentData {
+ /// How far into the future to look for collisions with other agents (in seconds)
+ [Tooltip("How far into the future to look for collisions with other agents (in seconds)")]
+ public float agentTimeHorizon;
+
+ /// How far into the future to look for collisions with obstacles (in seconds)
+ [Tooltip("How far into the future to look for collisions with obstacles (in seconds)")]
+ public float obstacleTimeHorizon;
+
+ ///
+ /// Max number of other agents to take into account.
+ /// A smaller value can reduce CPU load, a higher value can lead to better local avoidance quality.
+ ///
+ [Tooltip("Max number of other agents to take into account.\n" +
+ "A smaller value can reduce CPU load, a higher value can lead to better local avoidance quality.")]
+ public int maxNeighbours;
+
+ ///
+ /// Specifies the avoidance layer for this agent.
+ /// The mask on other agents will determine if they will avoid this agent.
+ ///
+ public RVOLayer layer;
+
+ ///
+ /// Layer mask specifying which layers this agent will avoid.
+ /// You can set it as CollidesWith = RVOLayer.DefaultAgent | RVOLayer.Layer3 | RVOLayer.Layer6 ...
+ ///
+ /// This can be very useful in games which have multiple teams of some sort. For example you usually
+ /// want the agents in one team to avoid each other, but you do not want them to avoid the enemies.
+ ///
+ /// This field only affects which other agents that this agent will avoid, it does not affect how other agents
+ /// react to this agent.
+ ///
+ /// See: bitmasks (view in online documentation for working links)
+ /// See: http://en.wikipedia.org/wiki/Mask_(computing)
+ ///
+ [Pathfinding.EnumFlag]
+ public RVOLayer collidesWith;
+
+ /// \copydocref{Pathfinding.RVO.IAgent.Priority}
+ [Tooltip("How strongly other agents will avoid this agent")]
+ [UnityEngine.Range(0, 1)]
+ public float priority;
+
+ ///
+ /// Priority multiplier.
+ /// This functions identically to the , however it is not exposed in the Unity inspector.
+ /// It is primarily used by the .
+ ///
+ [System.NonSerialized]
+ public float priorityMultiplier;
+
+ [System.NonSerialized]
+ public float flowFollowingStrength;
+
+ /// Enables drawing debug information in the scene view
+ public AgentDebugFlags debug;
+
+ /// A locked unit cannot move. Other units will still avoid it but avoidance quality is not the best.
+ [Tooltip("A locked unit cannot move. Other units will still avoid it. But avoidance quality is not the best")]
+ public bool locked;
+
+ /// Good default settings for an RVO agent
+ public static readonly RVOAgent Default = new RVOAgent {
+ locked = false,
+ agentTimeHorizon = 1.0f,
+ obstacleTimeHorizon = 0.5f,
+ maxNeighbours = 10,
+ layer = RVOLayer.DefaultAgent,
+ collidesWith = (RVOLayer)(-1),
+ priority = 0.5f,
+ priorityMultiplier = 1.0f,
+ flowFollowingStrength = 0.0f,
+ debug = AgentDebugFlags.Nothing,
+ };
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/RVOAgent.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/RVOAgent.cs.meta
new file mode 100644
index 0000000..046cc35
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/RVO/RVOAgent.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 910690cbba23a2745a85046d13e5c03b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ReadyToTraverseOffMeshLink.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ReadyToTraverseOffMeshLink.cs
new file mode 100644
index 0000000..43e179e
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ReadyToTraverseOffMeshLink.cs
@@ -0,0 +1,10 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+
+namespace Pathfinding.ECS {
+ /// Enabled if the agnet is ready to start traversing an off-mesh link
+ [System.Serializable]
+ public struct ReadyToTraverseOffMeshLink : IComponentData, IEnableableComponent {
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ReadyToTraverseOffMeshLink.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ReadyToTraverseOffMeshLink.cs.meta
new file mode 100644
index 0000000..bcfbe64
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ReadyToTraverseOffMeshLink.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8473eb53e4d194545b1395c9301ffc55
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ResolvedMovement.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ResolvedMovement.cs
new file mode 100644
index 0000000..d91a621
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ResolvedMovement.cs
@@ -0,0 +1,35 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+ using Pathfinding.Util;
+
+ ///
+ /// Holds the final movement data for an entity.
+ /// This is the data that is used by the movement system to move the entity.
+ ///
+ public struct ResolvedMovement : IComponentData {
+ /// \copydocref{MovementControl.targetPoint}
+ public float3 targetPoint;
+
+ /// \copydocref{MovementControl.speed}
+ public float speed;
+
+ public float turningRadiusMultiplier;
+
+ /// \copydocref{MovementControl.targetRotation}
+ public float targetRotation;
+
+ /// \copydocref{MovementControl.targetRotationHint}
+ public float targetRotationHint;
+
+ /// \copydocref{MovementControl.targetRotationOffset}
+ public float targetRotationOffset;
+
+ /// \copydocref{MovementControl.rotationSpeed}
+ public float rotationSpeed;
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ResolvedMovement.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ResolvedMovement.cs.meta
new file mode 100644
index 0000000..74542a5
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/ResolvedMovement.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 659b0f455df2f744189544436f88bf05
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SearchState.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SearchState.cs
new file mode 100644
index 0000000..44c5eac
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SearchState.cs
@@ -0,0 +1,11 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+
+ public struct SearchState : IComponentData {
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SearchState.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SearchState.cs.meta
new file mode 100644
index 0000000..5198363
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SearchState.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fe063c5087e811f47aec8d8889a66d68
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SimulateMovement.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SimulateMovement.cs
new file mode 100644
index 0000000..49d316d
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SimulateMovement.cs
@@ -0,0 +1,46 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+
+ ///
+ /// Tag component to enable movement for an entity.
+ /// Without this component, most systems will completely ignore the entity.
+ ///
+ /// There are some more specific components that can be used to selectively enable/disable some jobs:
+ /// -
+ /// -
+ /// -
+ ///
+ /// Removing one of the above components can be useful if you want to override the movement of an agent in some way.
+ ///
+ public struct SimulateMovement : IComponentData {
+ }
+
+ ///
+ /// Tag component to allow the agent to repair its path and recalculate various statistics.
+ ///
+ /// Allows the to run.
+ ///
+ public struct SimulateMovementRepair : IComponentData {
+ }
+
+ ///
+ /// Tag component to allow the agent to calculate how it wants to move.
+ ///
+ /// Allows the to run.
+ ///
+ public struct SimulateMovementControl : IComponentData {
+ }
+
+ ///
+ /// Tag component to allow the agent to move according to its desired movement parameters.
+ ///
+ /// Allows to run the , and jobs.
+ ///
+ public struct SimulateMovementFinalize : IComponentData {
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SimulateMovement.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SimulateMovement.cs.meta
new file mode 100644
index 0000000..485bb36
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SimulateMovement.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 38abdbf4c8ebfd64aa17d17cfd43cc8e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SyncWithTransform.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SyncWithTransform.cs
new file mode 100644
index 0000000..497b8b5
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SyncWithTransform.cs
@@ -0,0 +1,35 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+
+ ///
+ /// Tag component to enable syncing between an agent's Transform and the agent entity's position.
+ ///
+ /// See:
+ ///
+ public struct SyncPositionWithTransform : IComponentData {
+ }
+
+ ///
+ /// Tag component to enable syncing between an agent's Transform and the agent entity's rotation.
+ ///
+ /// See:
+ ///
+ public struct SyncRotationWithTransform : IComponentData {
+ }
+
+ ///
+ /// Tag component to indicate that the agent's forward direction is along the Y axis.
+ ///
+ /// This is used to convert between the forward direction of the GameObject and the internal forward direction, which always uses +Z as forward.
+ ///
+ /// See:
+ /// See:
+ ///
+ public struct OrientationYAxisForward : IComponentData {
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SyncWithTransform.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SyncWithTransform.cs.meta
new file mode 100644
index 0000000..c3f179f
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Components/SyncWithTransform.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 04e21506cb33eb847838b0a75fda6bf0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs
new file mode 100644
index 0000000..8264ac6
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs
@@ -0,0 +1,223 @@
+#if MODULE_ENTITIES
+using Unity.Collections.LowLevel.Unsafe;
+using Unity.Entities;
+
+namespace Pathfinding.ECS {
+ /// Helper for EntityAccess
+ static class EntityAccessHelper {
+ public static readonly int GlobalSystemVersionOffset = UnsafeUtility.GetFieldOffset(typeof(ComponentTypeHandle).GetField("m_GlobalSystemVersion", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
+ }
+
+ ///
+ /// Wrapper for a pointer.
+ ///
+ /// Very similar to the entities package RefRW struct. But unfortunately that one cannot be used because the required constructor is not exposed.
+ ///
+ public ref struct ComponentRef where T : unmanaged {
+ unsafe byte* ptr;
+
+ public unsafe ComponentRef(byte* ptr) {
+ this.ptr = ptr;
+ }
+
+ public ref T value {
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ get {
+ unsafe {
+ return ref *(T*)ptr;
+ }
+ }
+ }
+ }
+
+ /// Utility for efficient random access to entity storage data from the main thread
+ public struct EntityStorageCache {
+ EntityStorageInfo storage;
+ Entity entity;
+ int lastWorldHash;
+
+ ///
+ /// Retrieves the storage for a given entity.
+ ///
+ /// This method is very fast if the entity is the same as the last call to this method.
+ /// If the entity is different, it will be slower.
+ ///
+ /// Returns: True if the entity exists, and false if it does not.
+ ///
+ // Inlining makes this method about 20% faster. It's hot when accessing properties on the FollowerEntity component.
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public bool Update (World world, Entity entity, out EntityManager entityManager, out EntityStorageInfo storage) {
+ entityManager = default;
+ storage = this.storage;
+ if (world == null) return false;
+ entityManager = world.EntityManager;
+ // We must use entityManager.EntityOrderVersion here, not GlobalSystemVersion, because
+ // the GlobalSystemVersion does not necessarily update when structural changes happen.
+ var worldHash = entityManager.EntityOrderVersion ^ ((int)world.SequenceNumber << 8);
+ if (worldHash != lastWorldHash || entity != this.entity) {
+ if (!entityManager.Exists(entity)) return false;
+ this.storage = storage = entityManager.GetStorageInfo(entity);
+ this.entity = entity;
+ lastWorldHash = worldHash;
+ }
+ return true;
+ }
+
+ ///
+ /// Retrieves a component for a given entity.
+ ///
+ /// This is a convenience method to call on this object and update on the access object, and then retrieve the component data.
+ ///
+ /// This method is very fast if the entity is the same as the last call to this method.
+ /// If the entity is different, it will be slower.
+ ///
+ /// Warning: You must not store the returned reference past a structural change in the ECS world.
+ ///
+ /// Returns: True if the entity exists, and false if it does not.
+ /// Throws: An exception if the entity does not have the given component.
+ ///
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public bool GetComponentData(Entity entity, ref EntityAccess access, out ComponentRef value) where A : unmanaged, IComponentData {
+ if (Update(World.DefaultGameObjectInjectionWorld, entity, out var entityManager, out var storage)) {
+ access.Update(entityManager);
+ unsafe {
+ value = new ComponentRef((byte*)UnsafeUtility.AddressOf(ref access[storage]));
+ }
+ return true;
+ } else {
+ value = default;
+ return false;
+ }
+ }
+ }
+
+ ///
+ /// Utility for efficient random access to entity component data from the main thread.
+ ///
+ /// Since this struct returns a reference to the component data, it is faster than using EntityManager.GetComponentData,
+ /// in particular for larger component types.
+ ///
+ /// Warning: Some checks are not enforced by this API. It is the user's responsibility to ensure that
+ /// this struct does not survive past an ECS system update. If you only use this struct from the main thread
+ /// and only store it locally on the stack, this should not be a problem.
+ /// This struct also does not enforce that you only read to the component data if the readOnly flag is set.
+ ///
+ public struct EntityAccess where T : unmanaged, IComponentData {
+ public ComponentTypeHandle handle;
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+ SystemHandle systemHandle;
+#endif
+ uint lastSystemVersion;
+ ulong worldSequenceNumber;
+ bool readOnly;
+
+ public EntityAccess(bool readOnly) {
+ handle = default;
+ this.readOnly = readOnly;
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+ systemHandle = default;
+#endif
+
+ // Version 0 is never used by EntityManager.GlobalSystemVersion
+ lastSystemVersion = 0;
+ worldSequenceNumber = 0;
+ }
+
+ ///
+ /// Update the component type handle if necessary.
+ ///
+ /// This must be called if any jobs or system might have been scheduled since the struct was created or since the last call to Update.
+ ///
+ public void Update (EntityManager entityManager) {
+ // If the global system version has changed, jobs may have been scheduled which writes
+ // to the component data. Therefore we need to complete all dependencies before we can
+ // safely read or write to the component data.
+
+ var systemVersion = entityManager.GlobalSystemVersion;
+ var sequenceNumber = entityManager.WorldUnmanaged.SequenceNumber;
+ if (systemVersion != lastSystemVersion || worldSequenceNumber != sequenceNumber) {
+ if (lastSystemVersion == 0 || worldSequenceNumber != sequenceNumber) {
+ handle = entityManager.GetComponentTypeHandle(readOnly);
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+ entityManager.WorldUnmanaged.IsSystemValid(systemHandle);
+ systemHandle = entityManager.WorldUnmanaged.GetExistingUnmanagedSystem();
+#endif
+ }
+
+ lastSystemVersion = systemVersion;
+ worldSequenceNumber = sequenceNumber;
+ if (readOnly) entityManager.CompleteDependencyBeforeRO();
+ else entityManager.CompleteDependencyBeforeRW();
+ }
+
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+ handle.Update(ref entityManager.WorldUnmanaged.ResolveSystemStateRef(systemHandle));
+#else
+ // handle.Update just does the same thing as this unsafe code, but in a much more roundabout way
+ unsafe {
+ var ptr = (byte*)UnsafeUtility.AddressOf(ref handle);
+ *(uint*)(ptr + EntityAccessHelper.GlobalSystemVersionOffset) = systemVersion;
+ }
+#endif
+ }
+
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ public bool HasComponent (EntityStorageInfo storage) {
+ return storage.Chunk.Has(ref handle);
+ }
+
+ public ref T this[EntityStorageInfo storage] {
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ get {
+ unsafe {
+ var ptr = readOnly ? ((T*)storage.Chunk.GetRequiredComponentDataPtrRO(ref handle) + storage.IndexInChunk) : ((T*)storage.Chunk.GetRequiredComponentDataPtrRW(ref handle) + storage.IndexInChunk);
+ return ref *ptr;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Utility for efficient random access to managed entity component data from the main thread.
+ ///
+ /// Warning: Some checks are not enforced by this API. It is the user's responsibility to ensure that
+ /// this struct does not survive past an ECS system update. If you only use this struct from the main thread
+ /// and only store it locally on the stack, this should not be a problem.
+ /// This struct also does not enforce that you only read to the component data if the readOnly flag is set.
+ ///
+ public struct ManagedEntityAccess where T : class, IComponentData {
+ EntityManager entityManager;
+ ComponentTypeHandle handle;
+ bool readOnly;
+
+ public ManagedEntityAccess(bool readOnly) {
+ entityManager = default;
+ handle = default;
+ this.readOnly = readOnly;
+ }
+
+ public ManagedEntityAccess(EntityManager entityManager, bool readOnly) : this(readOnly) {
+ Update(entityManager);
+ }
+
+ public void Update (EntityManager entityManager) {
+ if (readOnly) entityManager.CompleteDependencyBeforeRO();
+ else entityManager.CompleteDependencyBeforeRW();
+ handle = entityManager.GetComponentTypeHandle(readOnly);
+ this.entityManager = entityManager;
+ }
+
+ public T this[EntityStorageInfo storage] {
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ get {
+ return storage.Chunk.GetManagedComponentAccessor(ref handle, entityManager)[storage.IndexInChunk];
+ }
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
+ set {
+ var accessor = storage.Chunk.GetManagedComponentAccessor(ref handle, entityManager);
+ accessor[storage.IndexInChunk] = value;
+ }
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs.meta
new file mode 100644
index 0000000..5a10e58
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/EntityAccess.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: eeb1b5067974c4947bb6751ab2a33627
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/IRuntimeBaker.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/IRuntimeBaker.cs
new file mode 100644
index 0000000..077ba9e
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/IRuntimeBaker.cs
@@ -0,0 +1,9 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+
+namespace Pathfinding.Util {
+ interface IRuntimeBaker {
+ void OnCreatedEntity(World world, Entity entity);
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/IRuntimeBaker.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/IRuntimeBaker.cs.meta
new file mode 100644
index 0000000..afec79b
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/IRuntimeBaker.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c44cc7455a88b82419ef53a61dd5626c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs.meta
new file mode 100644
index 0000000..d357f8a
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: fabe887d69022ae459e7619f5b0fbdf6
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobAlignAgentWithMovementDirection.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobAlignAgentWithMovementDirection.cs
new file mode 100644
index 0000000..4553462
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobAlignAgentWithMovementDirection.cs
@@ -0,0 +1,43 @@
+#if MODULE_ENTITIES
+using Unity.Burst;
+using Unity.Entities;
+using Unity.Mathematics;
+using Unity.Transforms;
+
+namespace Pathfinding.ECS {
+ [BurstCompile]
+ public partial struct JobAlignAgentWithMovementDirection : IJobEntity {
+ public float dt;
+
+ public void Execute (ref LocalTransform transform, in MovementSettings movementSettings, in MovementState movementState, in AgentCylinderShape shape, in AgentMovementPlane movementPlane, in MovementControl movementControl, ref ResolvedMovement resolvedMovement) {
+ if (math.lengthsq(movementControl.targetPoint - resolvedMovement.targetPoint) > 0.001f && resolvedMovement.speed > movementSettings.follower.speed * 0.1f) {
+ // If the agent is moving, align it with the movement direction
+ var desiredDirection = movementPlane.value.ToPlane(movementControl.targetPoint - transform.Position);
+ var actualDirection = movementPlane.value.ToPlane(resolvedMovement.targetPoint - transform.Position);
+
+ float desiredAngle;
+ if (math.lengthsq(desiredDirection) > math.pow(movementSettings.follower.speed * 0.1f, 2)) {
+ desiredAngle = math.atan2(desiredDirection.y, desiredDirection.x);
+ } else {
+ // If the agent did not desire to move at all, use the agent's current rotation
+ desiredAngle = movementPlane.value.ToPlane(transform.Rotation) + math.PI*0.5f;
+ }
+
+ // The agent only moves if the actual movement direction is non-zero
+ if (math.lengthsq(actualDirection) > math.pow(movementSettings.follower.speed * 0.1f, 2)) {
+ var actualAngle = math.atan2(actualDirection.y, actualDirection.x);
+ resolvedMovement.targetRotationOffset = AstarMath.DeltaAngle(desiredAngle, actualAngle);
+ return;
+ }
+ }
+
+ {
+ // Decay the rotation offset
+ // var da = AstarMath.DeltaAngle(movementState.rotationOffset, 0);
+ // resolvedMovement.targetRotationOffset += da * dt * 2.0f;
+ resolvedMovement.targetRotationOffset = AstarMath.DeltaAngle(0, resolvedMovement.targetRotationOffset) * (1 - dt * 2.0f);
+ }
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobAlignAgentWithMovementDirection.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobAlignAgentWithMovementDirection.cs.meta
new file mode 100644
index 0000000..e8059c1
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobAlignAgentWithMovementDirection.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b348dcdafdb36a946afe26daf64739a8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobApplyGravity.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobApplyGravity.cs
new file mode 100644
index 0000000..347fbc9
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobApplyGravity.cs
@@ -0,0 +1,75 @@
+#if MODULE_ENTITIES
+using Pathfinding.Drawing;
+using Pathfinding.Util;
+using Unity.Burst;
+using Unity.Collections;
+using Unity.Entities;
+using Unity.Mathematics;
+using Unity.Transforms;
+using UnityEngine;
+
+namespace Pathfinding.ECS {
+ [BurstCompile]
+ public partial struct JobApplyGravity : IJobEntity {
+ [ReadOnly]
+ public NativeArray raycastHits;
+ [ReadOnly]
+ public NativeArray raycastCommands;
+ public CommandBuilder draw;
+ public float dt;
+
+ public static void UpdateMovementPlaneFromNormal (float3 normal, ref AgentMovementPlane movementPlane) {
+ // Calculate a new movement plane that is perpendicular to the surface normal
+ // and is as similar to the previous movement plane as possible.
+ var forward = math.normalizesafe(math.mul(movementPlane.value.rotation, new float3(0, 0, 1)));
+ normal = math.normalizesafe(normal);
+ // TODO: This doesn't guarantee an orthogonal basis? forward and normal may not be perpendicular
+ movementPlane.value = new NativeMovementPlane(new quaternion(new float3x3(
+ math.cross(normal, forward),
+ normal,
+ forward
+ )));
+ }
+
+ void ResolveGravity (RaycastHit hit, bool grounded, ref LocalTransform transform, in AgentMovementPlane movementPlane, ref GravityState gravityState) {
+ var localPosition = movementPlane.value.ToPlane(transform.Position, out var currentElevation);
+ if (grounded) {
+ // 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, because 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
+ // CONVERGENCE_SPEED in the expression below determines how quickly it converges but high values can lead to too much noise.
+ const float CONVERGENCE_SPEED = 5f;
+ gravityState.verticalVelocity *= math.max(0, 1 - CONVERGENCE_SPEED * dt);
+
+ movementPlane.value.ToPlane(hit.point, out var hitElevation);
+ var elevationDelta = gravityState.verticalVelocity * dt;
+ const float VERTICAL_COLLISION_ADJUSTMENT_SPEED = 6f;
+ if (hitElevation > currentElevation) {
+ // Already below ground, only allow upwards movement
+ currentElevation = Mathf.MoveTowards(currentElevation, hitElevation, VERTICAL_COLLISION_ADJUSTMENT_SPEED * math.sqrt(math.abs(hitElevation - currentElevation)) * dt);
+ } else {
+ // Was above the ground, allow downwards movement until we are at the ground
+ currentElevation = math.max(hitElevation, currentElevation + elevationDelta);
+ }
+ } else {
+ var elevationDelta = gravityState.verticalVelocity * dt;
+ currentElevation += elevationDelta;
+ }
+ transform.Position = movementPlane.value.ToWorld(localPosition, currentElevation);
+ }
+
+ public void Execute (ref LocalTransform transform, in MovementSettings movementSettings, ref AgentMovementPlane movementPlane, ref GravityState gravityState, in AgentMovementPlaneSource movementPlaneSource, [Unity.Entities.EntityIndexInQuery] int entityIndexInQuery) {
+ var hit = raycastHits[entityIndexInQuery];
+ var hitAnything = math.any((float3)hit.normal != 0f);
+ if (hitAnything && movementPlaneSource.value == MovementPlaneSource.Raycast) {
+ UpdateMovementPlaneFromNormal(hit.normal, ref movementPlane);
+ }
+ ResolveGravity(hit, hitAnything, ref transform, in movementPlane, ref gravityState);
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobApplyGravity.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobApplyGravity.cs.meta
new file mode 100644
index 0000000..a53f5e1
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobApplyGravity.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 618ca53d45ce69442aaae615eb4f3291
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobControl.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobControl.cs
new file mode 100644
index 0000000..49c804f
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobControl.cs
@@ -0,0 +1,146 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+using Unity.Profiling;
+using Unity.Transforms;
+using Unity.Burst;
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+using Pathfinding.Drawing;
+using Pathfinding.PID;
+using Unity.Burst.Intrinsics;
+
+namespace Pathfinding.ECS {
+ [BurstCompile]
+ public partial struct JobControl : IJobEntity, IJobEntityChunkBeginEnd {
+ public float dt;
+ public CommandBuilder draw;
+ [ReadOnly]
+ [NativeDisableContainerSafetyRestriction]
+ public NavmeshEdges.NavmeshBorderData navmeshEdgeData;
+
+ [NativeDisableContainerSafetyRestriction]
+ public NativeList edgesScratch;
+
+ private static readonly ProfilerMarker MarkerConvertObstacles = new ProfilerMarker("ConvertObstacles");
+
+ public static float3 ClampToNavmesh (float3 position, float3 closestOnNavmesh, in AgentCylinderShape shape, in AgentMovementPlane movementPlane) {
+ // Don't clamp the elevation except to make sure it's not too far below the navmesh.
+ var clamped2D = movementPlane.value.ToPlane(closestOnNavmesh, out float clampedElevation);
+ movementPlane.value.ToPlane(position, out float currentElevation);
+ currentElevation = math.max(currentElevation, clampedElevation - shape.height * 0.4f);
+ position = movementPlane.value.ToWorld(clamped2D, currentElevation);
+ return position;
+ }
+
+ public bool OnChunkBegin (in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) {
+ if (!edgesScratch.IsCreated) edgesScratch = new NativeList(64, Allocator.Temp);
+ return true;
+ }
+
+ public void OnChunkEnd (in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask, bool chunkWasExecuted) {}
+
+ public void Execute (ref LocalTransform transform, ref MovementState state, in DestinationPoint destination, in AgentCylinderShape shape, in AgentMovementPlane movementPlane, in MovementSettings settings, in ResolvedMovement resolvedMovement, ref MovementControl controlOutput) {
+ // Clamp the agent to the navmesh.
+ var position = ClampToNavmesh(transform.Position, state.closestOnNavmesh, in shape, in movementPlane);
+
+ edgesScratch.Clear();
+ var scale = math.abs(transform.Scale);
+ var settingsTemp = settings.follower;
+ // Scale the settings by the agent's scale
+ settingsTemp.ScaleByAgentScale(scale);
+ settingsTemp.desiredWallDistance *= resolvedMovement.turningRadiusMultiplier;
+
+ if (state.isOnValidNode) {
+ MarkerConvertObstacles.Begin();
+ var localBounds = PIDMovement.InterestingEdgeBounds(ref settingsTemp, position, state.nextCorner, shape.height, movementPlane.value);
+ navmeshEdgeData.GetEdgesInRange(state.hierarchicalNodeIndex, localBounds, edgesScratch, movementPlane.value);
+ MarkerConvertObstacles.End();
+ }
+
+ // To ensure we detect that the end of the path is reached robustly we make the agent move slightly closer.
+ // to the destination than the stopDistance.
+ const float FUZZ = 0.005f;
+ // If we are moving towards an off-mesh link, then we want the agent to stop precisely at the off-mesh link.
+ // TODO: Depending on the link, we may want the agent to move towards the link at full speed, instead of slowing down.
+ var stopDistance = state.traversingLastPart ? math.max(0, settings.stopDistance - FUZZ) : 0f;
+ var distanceToSteeringTarget = math.max(0, state.remainingDistanceToEndOfPart - stopDistance);
+ var rotation = movementPlane.value.ToPlane(transform.Rotation) - state.rotationOffset - state.rotationOffset2;
+
+ transform.Position = position;
+
+ if (dt > 0.000001f) {
+ if (!math.isfinite(distanceToSteeringTarget)) {
+ // The agent has no path, just stay still
+ controlOutput = new MovementControl {
+ targetPoint = position,
+ speed = 0,
+ endOfPath = position,
+ maxSpeed = settings.follower.speed,
+ overrideLocalAvoidance = false,
+ hierarchicalNodeIndex = state.hierarchicalNodeIndex,
+ targetRotation = resolvedMovement.targetRotation,
+ rotationSpeed = settings.follower.maxRotationSpeed,
+ targetRotationOffset = state.rotationOffset, // May be modified by other systems
+ };
+ } else if (settings.isStopped) {
+ // The user has requested that the agent slow down as quickly as possible.
+ // TODO: If the agent is not clamped to the navmesh, it should still move towards the navmesh if it is outside it.
+ controlOutput = new MovementControl {
+ // Keep moving in the same direction as during the last frame, but slow down
+ targetPoint = position + math.normalizesafe(resolvedMovement.targetPoint - position) * 10.0f,
+ speed = settings.follower.Accelerate(resolvedMovement.speed, settings.follower.slowdownTime, -dt),
+ endOfPath = state.endOfPath,
+ maxSpeed = settings.follower.speed,
+ overrideLocalAvoidance = false,
+ hierarchicalNodeIndex = state.hierarchicalNodeIndex,
+ targetRotation = resolvedMovement.targetRotation,
+ rotationSpeed = settings.follower.maxRotationSpeed,
+ targetRotationOffset = state.rotationOffset, // May be modified by other systems
+ };
+ } else {
+ var controlParams = new PIDMovement.ControlParams {
+ edges = edgesScratch.AsArray(),
+ nextCorner = state.nextCorner,
+ agentRadius = shape.radius,
+ facingDirectionAtEndOfPath = destination.facingDirection,
+ endOfPath = state.endOfPath,
+ remainingDistance = distanceToSteeringTarget,
+ closestOnNavmesh = state.closestOnNavmesh,
+ debugFlags = settings.debugFlags,
+ p = position,
+ rotation = rotation,
+ maxDesiredWallDistance = state.followerState.maxDesiredWallDistance,
+ speed = controlOutput.speed,
+ movementPlane = movementPlane.value,
+ };
+
+ var control = PIDMovement.Control(ref settingsTemp, dt, ref controlParams, ref draw, out state.followerState.maxDesiredWallDistance);
+ var positionDelta = movementPlane.value.ToWorld(control.positionDelta, 0);
+ var speed = math.length(positionDelta) / dt;
+
+ controlOutput = new MovementControl {
+ targetPoint = position + math.normalizesafe(positionDelta) * distanceToSteeringTarget,
+ speed = speed,
+ endOfPath = state.endOfPath,
+ maxSpeed = settingsTemp.speed * 1.1f,
+ overrideLocalAvoidance = false,
+ hierarchicalNodeIndex = state.hierarchicalNodeIndex,
+ // It may seem sketchy to use a target rotation so close to the current rotation. One might think
+ // there's risk of overshooting this target rotation if the frame rate is uneven.
+ // But the TimeScaledRateManager ensures that this is not the case.
+ // The cheap simulation's time (which is the one actually rotating the agent) is always guaranteed to be
+ // behind (or precisely caught up with) the full simulation's time (that's the simulation which runs this system).
+ targetRotation = rotation + control.rotationDelta,
+ targetRotationHint = rotation + AstarMath.DeltaAngle(rotation, control.targetRotation),
+ rotationSpeed = math.abs(control.rotationDelta / dt),
+ targetRotationOffset = state.rotationOffset, // May be modified by other systems
+ };
+ }
+ } else {
+ controlOutput.hierarchicalNodeIndex = -1;
+ }
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobControl.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobControl.cs.meta
new file mode 100644
index 0000000..c4b5992
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobControl.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4b24028677256624696bab00b6f43ac0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobDrawFollowerGizmos.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobDrawFollowerGizmos.cs
new file mode 100644
index 0000000..df4652d
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobDrawFollowerGizmos.cs
@@ -0,0 +1,104 @@
+#if MODULE_ENTITIES
+using System.Runtime.InteropServices;
+using Pathfinding.Drawing;
+using Pathfinding.PID;
+using Pathfinding.Util;
+using Unity.Burst;
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+using Unity.Entities;
+using Unity.Mathematics;
+using Unity.Transforms;
+
+namespace Pathfinding.ECS {
+ [BurstCompile]
+ struct DrawGizmosJobUtils {
+ [BurstCompile]
+ internal static void DrawPath (ref CommandBuilder draw, ref UnsafeSpan vertices, ref AgentCylinderShape shape) {
+ draw.PushColor(Palette.Colorbrewer.Set1.Orange);
+ // Some people will set the agent's radius to zero. In that case we just draw the path as a polyline as we have no good reference for how to space the symbols.
+ if (shape.radius > 0.01f) {
+ var generator = new CommandBuilder.PolylineWithSymbol(CommandBuilder.SymbolDecoration.ArrowHead, shape.radius * 0.5f, shape.radius * 0.0f, shape.radius * 4f, true);
+ for (int i = vertices.Length - 1; i >= 0; i--) generator.MoveTo(ref draw, vertices[i]);
+ } else {
+ for (int i = 0; i < vertices.Length - 1; i++) draw.Line(vertices[i], vertices[i+1]);
+ }
+ draw.PopColor();
+ }
+ }
+
+ public partial struct JobDrawFollowerGizmos : IJobChunk {
+ public CommandBuilder draw;
+ public GCHandle entityManagerHandle;
+ [ReadOnly]
+ public ComponentTypeHandle LocalTransformTypeHandleRO;
+ [ReadOnly]
+ public ComponentTypeHandle AgentCylinderShapeHandleRO;
+ [ReadOnly]
+ public ComponentTypeHandle MovementSettingsHandleRO;
+ [ReadOnly]
+ public ComponentTypeHandle AgentMovementPlaneHandleRO;
+ // This is actually not read only, because the GetNextCorners function can modify internal state
+ // See JobRepairPath.Scheduler.ManagedStateTypeHandleRW for details about why NativeDisableContainerSafetyRestriction is required
+ [NativeDisableContainerSafetyRestriction]
+ public ComponentTypeHandle ManagedStateHandleRW;
+ [ReadOnly]
+ public ComponentTypeHandle MovementStateHandleRO;
+ [ReadOnly]
+ public ComponentTypeHandle ResolvedMovementHandleRO;
+
+ [NativeDisableContainerSafetyRestriction]
+ public NativeList scratchBuffer1;
+ [NativeDisableContainerSafetyRestriction]
+ public NativeArray scratchBuffer2;
+
+ public void Execute (in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in Unity.Burst.Intrinsics.v128 chunkEnabledMask) {
+ if (!scratchBuffer1.IsCreated) scratchBuffer1 = new NativeList(32, Allocator.Temp);
+ if (!scratchBuffer2.IsCreated) scratchBuffer2 = new NativeArray(32, Allocator.Temp);
+
+ unsafe {
+ var localTransforms = (LocalTransform*)chunk.GetNativeArray(ref LocalTransformTypeHandleRO).GetUnsafeReadOnlyPtr();
+ var agentCylinderShapes = (AgentCylinderShape*)chunk.GetNativeArray(ref AgentCylinderShapeHandleRO).GetUnsafeReadOnlyPtr();
+ var movementSettings = (MovementSettings*)chunk.GetNativeArray(ref MovementSettingsHandleRO).GetUnsafeReadOnlyPtr();
+ var movementPlanes = (AgentMovementPlane*)chunk.GetNativeArray(ref AgentMovementPlaneHandleRO).GetUnsafeReadOnlyPtr();
+ var managedStates = chunk.GetManagedComponentAccessor(ref ManagedStateHandleRW, (EntityManager)entityManagerHandle.Target);
+ var movementStates = (MovementState*)chunk.GetNativeArray(ref MovementStateHandleRO).GetUnsafeReadOnlyPtr();
+ var resolvedMovement = (ResolvedMovement*)chunk.GetNativeArray(ref ResolvedMovementHandleRO).GetUnsafeReadOnlyPtr();
+
+ for (int i = 0; i < chunk.Count; i++) {
+ Execute(ref localTransforms[i], ref movementPlanes[i], ref agentCylinderShapes[i], managedStates[i], ref movementSettings[i], ref movementStates[i], ref resolvedMovement[i]);
+ }
+ }
+ }
+
+ public void Execute (ref LocalTransform transform, ref AgentMovementPlane movementPlane, ref AgentCylinderShape shape, ManagedState managedState, ref MovementSettings settings, ref MovementState movementState, ref ResolvedMovement resolvedMovement) {
+ if ((settings.debugFlags & PIDMovement.DebugFlags.Funnel) != 0) {
+ managedState.pathTracer.DrawFunnel(draw, movementPlane.value);
+ }
+ if ((settings.debugFlags & PIDMovement.DebugFlags.Rotation) != 0) {
+ var p2D = movementPlane.value.ToPlane(transform.Position, out float positionElevation);
+ draw.PushMatrix(math.mul(new float4x4(movementPlane.value.rotation, float3.zero), float4x4.Translate(new float3(0, positionElevation, 0))));
+ var visualRotation = movementPlane.value.ToPlane(transform.Rotation);
+ var unsmoothedRotation = visualRotation - movementState.rotationOffset2;
+ var internalRotation = unsmoothedRotation - movementState.rotationOffset;
+ var targetInternalRotation = resolvedMovement.targetRotation;
+ var targetInternalRotationHint = resolvedMovement.targetRotationHint;
+ math.sincos(math.PI*0.5f + new float3(visualRotation, unsmoothedRotation, internalRotation), out var s, out var c);
+ draw.xz.ArrowheadArc(p2D, new float2(c.x, s.x), shape.radius * 1.1f, Palette.Colorbrewer.Set1.Blue);
+ draw.xz.ArrowheadArc(p2D, new float2(c.y, s.y), shape.radius * 1.1f, Palette.Colorbrewer.Set1.Purple);
+ draw.xz.ArrowheadArc(p2D, new float2(c.z, s.z), shape.radius * 1.1f, Palette.Colorbrewer.Set1.Orange);
+ math.sincos(math.PI*0.5f + new float2(targetInternalRotation, targetInternalRotationHint), out var s2, out var c2);
+ draw.xz.ArrowheadArc(p2D, new float2(c2.x, s2.x), shape.radius * 1.2f, Palette.Colorbrewer.Set1.Yellow);
+ draw.xz.ArrowheadArc(p2D, new float2(c2.y, s2.y), shape.radius * 1.2f, Palette.Colorbrewer.Set1.Pink);
+ draw.PopMatrix();
+ }
+ if ((settings.debugFlags & PIDMovement.DebugFlags.Path) != 0 && managedState.pathTracer.hasPath) {
+ scratchBuffer1.Clear();
+ managedState.pathTracer.GetNextCorners(scratchBuffer1, int.MaxValue, ref scratchBuffer2, Allocator.Temp, managedState.pathfindingSettings.traversalProvider, managedState.activePath);
+ var span = scratchBuffer1.AsUnsafeSpan();
+ DrawGizmosJobUtils.DrawPath(ref draw, ref span, ref shape);
+ }
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobDrawFollowerGizmos.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobDrawFollowerGizmos.cs.meta
new file mode 100644
index 0000000..377d6ac
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobDrawFollowerGizmos.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f202855630112e347bc6d3e68ee6f314
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedMovementOverride.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedMovementOverride.cs
new file mode 100644
index 0000000..abdd98e
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedMovementOverride.cs
@@ -0,0 +1,45 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Transforms;
+
+namespace Pathfinding.ECS {
+ public partial struct JobManagedMovementOverrideBeforeControl : IJobEntity {
+ public float dt;
+
+ public void Execute (ManagedMovementOverrideBeforeControl managedOverride, Entity entity, ref LocalTransform localTransform, ref AgentCylinderShape shape, ref AgentMovementPlane movementPlane, ref DestinationPoint destination, ref MovementState movementState, ref MovementSettings movementSettings) {
+ if (managedOverride.callback != null) {
+ managedOverride.callback(entity, dt, ref localTransform, ref shape, ref movementPlane, ref destination, ref movementState, ref movementSettings);
+ // The callback may have modified the movement state, so we need to reset the path tracer version to indicate that the movement state is not up to date.
+ // This will cause the repair job to avoid optimizing some updates away.
+ movementState.pathTracerVersion--;
+ }
+ }
+ }
+
+ public partial struct JobManagedMovementOverrideAfterControl : IJobEntity {
+ public float dt;
+
+ public void Execute (ManagedMovementOverrideAfterControl managedOverride, Entity entity, ref LocalTransform localTransform, ref AgentCylinderShape shape, ref AgentMovementPlane movementPlane, ref DestinationPoint destination, ref MovementState movementState, ref MovementSettings movementSettings, ref MovementControl movementControl) {
+ if (managedOverride.callback != null) {
+ managedOverride.callback(entity, dt, ref localTransform, ref shape, ref movementPlane, ref destination, ref movementState, ref movementSettings, ref movementControl);
+ // The callback may have modified the movement state, so we need to reset the path tracer version to indicate that the movement state is not up to date.
+ // This will cause the repair job to avoid optimizing some updates away.
+ movementState.pathTracerVersion--;
+ }
+ }
+ }
+
+ public partial struct JobManagedMovementOverrideBeforeMovement : IJobEntity {
+ public float dt;
+
+ public void Execute (ManagedMovementOverrideBeforeMovement managedOverride, Entity entity, ref LocalTransform localTransform, ref AgentCylinderShape shape, ref AgentMovementPlane movementPlane, ref DestinationPoint destination, ref MovementState movementState, ref MovementSettings movementSettings, ref MovementControl movementControl, ref ResolvedMovement resolvedMovement) {
+ if (managedOverride.callback != null) {
+ managedOverride.callback(entity, dt, ref localTransform, ref shape, ref movementPlane, ref destination, ref movementState, ref movementSettings, ref movementControl, ref resolvedMovement);
+ // The callback may have modified the movement state, so we need to reset the path tracer version to indicate that the movement state is not up to date.
+ // This will cause the repair job to avoid optimizing some updates away.
+ movementState.pathTracerVersion--;
+ }
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedMovementOverride.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedMovementOverride.cs.meta
new file mode 100644
index 0000000..24d261a
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedMovementOverride.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6e9dcafc22a15f547984558ee0fc626f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs
new file mode 100644
index 0000000..8055cc8
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs
@@ -0,0 +1,86 @@
+#pragma warning disable 0282 // Allows the 'partial' keyword without warnings
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Collections;
+using UnityEngine;
+using Unity.Transforms;
+using Unity.Collections.LowLevel.Unsafe;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+
+ public partial struct JobManagedOffMeshLinkTransition : IJobEntity {
+ public EntityCommandBuffer commandBuffer;
+ public float deltaTime;
+
+ public void Execute (Entity entity, ManagedState state, ref LocalTransform transform, ref AgentMovementPlane movementPlane, ref MovementControl movementControl, ref MovementSettings movementSettings, ref AgentOffMeshLinkTraversal linkInfo, ManagedAgentOffMeshLinkTraversal managedLinkInfo) {
+ if (!MoveNext(entity, state, ref transform, ref movementPlane, ref movementControl, ref movementSettings, ref linkInfo, managedLinkInfo, deltaTime)) {
+ commandBuffer.RemoveComponent(entity);
+ commandBuffer.RemoveComponent(entity);
+ }
+ }
+
+ public static bool MoveNext (Entity entity, ManagedState state, ref LocalTransform transform, ref AgentMovementPlane movementPlane, ref MovementControl movementControl, ref MovementSettings movementSettings, ref AgentOffMeshLinkTraversal linkInfo, ManagedAgentOffMeshLinkTraversal managedLinkInfo, float deltaTime) {
+ unsafe {
+ managedLinkInfo.context.SetInternalData(entity, ref transform, ref movementPlane, ref movementControl, ref movementSettings, ref linkInfo, state, deltaTime);
+ }
+
+ // Initialize the coroutine during the first step.
+ // This can also happen if the entity is duplicated, since the coroutine cannot be cloned.
+ if (managedLinkInfo.coroutine == null) {
+ // If we are calculating a path right now, cancel that path calculation.
+ // We don't want to calculate a path while we are traversing an off-mesh link.
+ state.CancelCurrentPathRequest();
+
+ if (managedLinkInfo.stateMachine == null) {
+ managedLinkInfo.stateMachine = managedLinkInfo.handler != null? managedLinkInfo.handler.GetOffMeshLinkStateMachine(managedLinkInfo.context) : null;
+ }
+ managedLinkInfo.coroutine = managedLinkInfo.stateMachine != null? managedLinkInfo.stateMachine.OnTraverseOffMeshLink(managedLinkInfo.context).GetEnumerator() : JobStartOffMeshLinkTransition.DefaultOnTraverseOffMeshLink(managedLinkInfo.context).GetEnumerator();
+ }
+
+ bool finished;
+ bool error = false;
+ bool popParts = true;
+ try {
+ finished = !managedLinkInfo.coroutine.MoveNext();
+ } catch (AgentOffMeshLinkTraversalContext.AbortOffMeshLinkTraversal) {
+ error = true;
+ finished = true;
+ popParts = false;
+ }
+ catch (System.Exception e) {
+ Debug.LogException(e, managedLinkInfo.context.gameObject);
+ // Teleport the agent to the end of the link as a fallback, if there's an exception
+ managedLinkInfo.context.Teleport(managedLinkInfo.context.link.relativeEnd);
+ finished = true;
+ error = true;
+ }
+
+ if (finished) {
+ try {
+ if (managedLinkInfo.stateMachine != null) {
+ if (error) managedLinkInfo.stateMachine.OnAbortTraversingOffMeshLink();
+ else managedLinkInfo.stateMachine.OnFinishTraversingOffMeshLink(managedLinkInfo.context);
+ }
+ } catch (System.Exception e) {
+ // If an exception happens when exiting the state machine, log it, and then continue with the cleanup
+ Debug.LogException(e, managedLinkInfo.context.gameObject);
+ }
+
+ managedLinkInfo.context.Restore();
+ if (popParts) {
+ // Pop the part leading up to the link, and the link itself
+ state.PopNextLinkFromPath();
+ }
+ }
+ return !finished;
+ }
+ }
+
+ public partial struct JobManagedOffMeshLinkTransitionCleanup : IJobEntity {
+ public void Execute (ManagedAgentOffMeshLinkTraversal managedLinkInfo) {
+ managedLinkInfo.stateMachine.OnAbortTraversingOffMeshLink();
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs.meta
new file mode 100644
index 0000000..1c23899
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobManagedOffMeshLinkTransition.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5a279ed93b8648d4982ea23f87877a3c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobMoveAgent.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobMoveAgent.cs
new file mode 100644
index 0000000..331ab82
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobMoveAgent.cs
@@ -0,0 +1,95 @@
+#if MODULE_ENTITIES
+using Unity.Burst;
+using Unity.Entities;
+using Unity.Mathematics;
+using Unity.Transforms;
+
+namespace Pathfinding.ECS {
+ [BurstCompile]
+ public partial struct JobMoveAgent : IJobEntity {
+ public float dt;
+
+ static void UpdateVelocityEstimate (ref LocalTransform transform, ref MovementStatistics movementStatistics, float dt) {
+ if (dt > 0.000001f) {
+ movementStatistics.estimatedVelocity = (transform.Position - movementStatistics.lastPosition) / dt;
+ }
+ }
+
+ static void ResolveRotation (ref LocalTransform transform, ref MovementState state, in ResolvedMovement resolvedMovement, in MovementSettings movementSettings, in AgentMovementPlane movementPlane, float dt) {
+ var currentRotation = movementPlane.value.ToPlane(transform.Rotation);
+ var currentInternalRotation = currentRotation - state.rotationOffset - state.rotationOffset2;
+ var deltaRotation = math.clamp(AstarMath.DeltaAngle(currentInternalRotation, resolvedMovement.targetRotation), -resolvedMovement.rotationSpeed * dt, resolvedMovement.rotationSpeed * dt);
+ var extraRotationSpeed = math.radians(movementSettings.follower.maxRotationSpeed) * 0.5f;
+ var deltaExtraRotation = math.clamp(AstarMath.DeltaAngle(state.rotationOffset, resolvedMovement.targetRotationOffset), -extraRotationSpeed * dt, extraRotationSpeed * dt);
+ var currentUnsmoothedRotation = currentInternalRotation + state.rotationOffset;
+ var newInternalRotation = currentInternalRotation + deltaRotation;
+ // Keep track of how much extra rotation we are applying. This is done so that
+ // the movement calculation can separate this out when doing its movement calculations.
+ state.rotationOffset += deltaExtraRotation;
+ // Make sure the rotation offset is between -pi/2 and pi/2 radians
+ state.rotationOffset = AstarMath.DeltaAngle(0, state.rotationOffset);
+ var newUnsmoothedRotation = newInternalRotation + state.rotationOffset;
+
+ if (movementSettings.rotationSmoothing > 0) {
+ // Apply compensation to rotationOffset2 to precisely cancel out the agent's rotation during this frame
+ state.rotationOffset2 += currentUnsmoothedRotation - newUnsmoothedRotation;
+
+ state.rotationOffset2 = AstarMath.DeltaAngle(0, state.rotationOffset2);
+
+ // Decay the rotationOffset2. This implicitly adds an exponential moving average to the visual rotation
+ var decay = math.abs(AstarMath.DeltaAngle(currentRotation, resolvedMovement.targetRotationHint)) / movementSettings.rotationSmoothing;
+ var exponentialDecay = decay*dt;
+
+ // In addition to an exponential decay, we also add a linear decay.
+ // This is important to relatively quickly zero out the error when the agent is almost
+ // facing the right direction. With an exponential decay, it would take far too long to look good.
+ const float LINEAR_DECAY_AMOUNT = 0.1f;
+ var linearDecay = (LINEAR_DECAY_AMOUNT/movementSettings.rotationSmoothing)*dt;
+
+ if (math.abs(state.rotationOffset2) > 0) state.rotationOffset2 *= math.max(0, 1 - exponentialDecay - linearDecay/math.abs(state.rotationOffset2));
+ } else if (state.rotationOffset2 != 0) {
+ // Rotation smoothing is disabled, decay the rotation offset very quickly, but still avoid jarring changes
+ state.rotationOffset2 += math.clamp(-state.rotationOffset2, -extraRotationSpeed * dt, extraRotationSpeed * dt);
+ }
+
+ transform.Rotation = movementPlane.value.ToWorldRotation(newInternalRotation + state.rotationOffset + state.rotationOffset2);
+ }
+
+ public static float3 MoveWithoutGravity (ref LocalTransform transform, in ResolvedMovement resolvedMovement, in AgentMovementPlane movementPlane, float dt) {
+ UnityEngine.Assertions.Assert.IsTrue(math.all(math.isfinite(resolvedMovement.targetPoint)));
+ // Move only along the movement plane
+ var localDir = movementPlane.value.ToPlane(resolvedMovement.targetPoint - transform.Position);
+ var magn = math.length(localDir);
+ var localDelta = math.select(localDir, localDir * math.clamp(resolvedMovement.speed * dt / magn, 0, 1.0f), magn > 0.0001f);
+ var delta = movementPlane.value.ToWorld(localDelta, 0);
+ return delta;
+ }
+
+ public static void ResolvePositionSmoothing (float3 movementDelta, ref MovementState state, in MovementSettings movementSettings, float dt) {
+ if (movementSettings.positionSmoothing > 0) {
+ state.positionOffset -= movementDelta;
+ var exponentialDecay = 1f/movementSettings.positionSmoothing*dt;
+ var linearDecay = 0.1f/movementSettings.positionSmoothing*dt;
+ var positionOffsetMagnitude = math.length(state.positionOffset);
+ if (positionOffsetMagnitude > 0) state.positionOffset *= math.max(0, 1 - exponentialDecay - linearDecay/positionOffsetMagnitude);
+ } else {
+ state.positionOffset = float3.zero;
+ }
+ }
+
+ public void Execute (ref LocalTransform transform, in AgentCylinderShape shape, in AgentMovementPlane movementPlane, ref MovementState state, in MovementSettings movementSettings, in ResolvedMovement resolvedMovement, ref MovementStatistics movementStatistics) {
+ MoveAgent(ref transform, in shape, in movementPlane, ref state, in movementSettings, in resolvedMovement, ref movementStatistics, dt);
+ }
+
+ public static void MoveAgent (ref LocalTransform transform, in AgentCylinderShape shape, in AgentMovementPlane movementPlane, ref MovementState state, in MovementSettings movementSettings, in ResolvedMovement resolvedMovement, ref MovementStatistics movementStatistics, float dt) {
+ var delta = MoveWithoutGravity(ref transform, in resolvedMovement, in movementPlane, dt);
+ UnityEngine.Assertions.Assert.IsTrue(math.all(math.isfinite(delta)), "Refusing to set the agent's position to a non-finite vector");
+ transform.Position += delta;
+ ResolvePositionSmoothing(delta, ref state, in movementSettings, dt);
+ ResolveRotation(ref transform, ref state, in resolvedMovement, in movementSettings, in movementPlane, dt);
+ UpdateVelocityEstimate(ref transform, ref movementStatistics, dt);
+ movementStatistics.lastPosition = transform.Position;
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobMoveAgent.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobMoveAgent.cs.meta
new file mode 100644
index 0000000..c989a0d
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobMoveAgent.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ae237f49f264df546bb908b1d4f3d658
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobPrepareAgentRaycasts.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobPrepareAgentRaycasts.cs
new file mode 100644
index 0000000..c6c4081
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobPrepareAgentRaycasts.cs
@@ -0,0 +1,47 @@
+#if MODULE_ENTITIES
+using Pathfinding.Drawing;
+using Unity.Burst;
+using Unity.Collections;
+using Unity.Entities;
+using Unity.Mathematics;
+using Unity.Transforms;
+using UnityEngine;
+
+namespace Pathfinding.ECS {
+ [BurstCompile]
+ public partial struct JobPrepareAgentRaycasts : IJobEntity {
+ public NativeArray raycastCommands;
+ public QueryParameters raycastQueryParameters;
+ public CommandBuilder draw;
+ public float dt;
+ public float gravity;
+
+ void ApplyGravity (ref GravityState gravityState) {
+ gravityState.verticalVelocity += gravity * dt;
+ }
+
+ RaycastCommand CalculateRaycastCommands (ref LocalTransform transform, in AgentCylinderShape shape, in AgentMovementPlane movementPlane, in MovementSettings movementSettings, ref GravityState gravityState) {
+ // TODO: Might be more performant to convert the movement plane to two matrices
+
+ movementPlane.value.ToPlane(transform.Position, out var lastElevation);
+
+ var elevationDelta = gravityState.verticalVelocity * dt;
+ var localPosition = movementPlane.value.ToPlane(transform.Position, out var elevation);
+ var rayStartElevation = math.max(elevation + elevationDelta, lastElevation) + shape.height * 0.5f;
+ var rayStopElevation = math.min(elevation + elevationDelta, lastElevation);
+ float rayLength = rayStartElevation - rayStopElevation; // TODO: Multiply by scale
+ var down = movementPlane.value.ToWorld(0, -1);
+ raycastQueryParameters.layerMask = movementSettings.groundMask;
+ return new RaycastCommand(movementPlane.value.ToWorld(localPosition, rayStartElevation), down, raycastQueryParameters, rayLength);
+ }
+
+ public void Execute (ref LocalTransform transform, in AgentCylinderShape shape, in AgentMovementPlane movementPlane, ref MovementState state, in MovementSettings movementSettings, ref ResolvedMovement resolvedMovement, ref MovementStatistics movementStatistics, ref GravityState gravityState, [Unity.Entities.EntityIndexInQuery] int entityIndexInQuery) {
+ // Move only along the movement plane
+ // JobMoveAgent.MoveAgent(ref transform, in shape, in movementPlane, ref state, in movementSettings, in resolvedMovement, ref movementStatistics, dt);
+
+ ApplyGravity(ref gravityState);
+ raycastCommands[entityIndexInQuery] = CalculateRaycastCommands(ref transform, in shape, in movementPlane, in movementSettings, ref gravityState);
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobPrepareAgentRaycasts.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobPrepareAgentRaycasts.cs.meta
new file mode 100644
index 0000000..7d0a011
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobPrepareAgentRaycasts.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9de01002f503a3e4abd6467cd6de7a44
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobRepairPath.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobRepairPath.cs
new file mode 100644
index 0000000..7f0e193
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobRepairPath.cs
@@ -0,0 +1,277 @@
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+using Unity.Profiling;
+using Unity.Transforms;
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+using GCHandle = System.Runtime.InteropServices.GCHandle;
+
+namespace Pathfinding.ECS {
+ using Pathfinding;
+ using Pathfinding.Util;
+ using Unity.Burst;
+ using Unity.Jobs;
+
+ ///
+ /// Repairs the path of agents.
+ ///
+ /// This job will repair the agent's path based on the agent's current position and its destination.
+ /// It will also recalculate various statistics like how far the agent is from the destination,
+ /// and if it has reached the destination or not.
+ ///
+ public struct JobRepairPath : IJobChunk {
+ public struct Scheduler {
+ [ReadOnly]
+ public ComponentTypeHandle LocalTransformTypeHandleRO;
+ public ComponentTypeHandle MovementStateTypeHandleRW;
+ [ReadOnly]
+ public ComponentTypeHandle AgentCylinderShapeTypeHandleRO;
+ // NativeDisableContainerSafetyRestriction seems to be necessary because otherwise we will get an error:
+ // "The ComponentTypeHandle ... can not be accessed. Nested native containers are illegal in jobs."
+ // However, Unity doesn't seem to check for this at all times. Currently, I can only replicate the error if DoTween Pro is also installed.
+ // I have no idea how this unrelated package influences unity to actually do the check.
+ // We know it is safe to access the managed state because we make sure to never access an entity from multiple threads at the same time.
+ [NativeDisableContainerSafetyRestriction]
+ public ComponentTypeHandle ManagedStateTypeHandleRW;
+ [ReadOnly]
+ public ComponentTypeHandle MovementSettingsTypeHandleRO;
+ [ReadOnly]
+ public ComponentTypeHandle DestinationPointTypeHandleRO;
+ [ReadOnly]
+ public ComponentTypeHandle AgentMovementPlaneTypeHandleRO;
+ public ComponentTypeHandle ReadyToTraverseOffMeshLinkTypeHandleRW;
+ public GCHandle entityManagerHandle;
+ public bool onlyApplyPendingPaths;
+
+ public EntityQueryBuilder GetEntityQuery (Allocator allocator) {
+ return new EntityQueryBuilder(Allocator.Temp)
+ .WithAllRW()
+ .WithAllRW()
+ .WithAllRW()
+ .WithAll()
+ // .WithAny() // TODO: Use WithPresent in newer versions
+ .WithAbsent();
+ }
+
+ public Scheduler(ref SystemState systemState) {
+ entityManagerHandle = GCHandle.Alloc(systemState.EntityManager);
+ LocalTransformTypeHandleRO = systemState.GetComponentTypeHandle(true);
+ MovementStateTypeHandleRW = systemState.GetComponentTypeHandle(false);
+ AgentCylinderShapeTypeHandleRO = systemState.GetComponentTypeHandle(true);
+ DestinationPointTypeHandleRO = systemState.GetComponentTypeHandle(true);
+ AgentMovementPlaneTypeHandleRO = systemState.GetComponentTypeHandle(true);
+ MovementSettingsTypeHandleRO = systemState.GetComponentTypeHandle(true);
+ ReadyToTraverseOffMeshLinkTypeHandleRW = systemState.GetComponentTypeHandle(false);
+ // Need to bypass the T : unmanaged check in systemState.GetComponentTypeHandle
+ ManagedStateTypeHandleRW = systemState.EntityManager.GetComponentTypeHandle(false);
+ onlyApplyPendingPaths = false;
+ }
+
+ public void Dispose () {
+ entityManagerHandle.Free();
+ }
+
+ void Update (ref SystemState systemState) {
+ LocalTransformTypeHandleRO.Update(ref systemState);
+ MovementStateTypeHandleRW.Update(ref systemState);
+ AgentCylinderShapeTypeHandleRO.Update(ref systemState);
+ DestinationPointTypeHandleRO.Update(ref systemState);
+ ManagedStateTypeHandleRW.Update(ref systemState);
+ MovementSettingsTypeHandleRO.Update(ref systemState);
+ AgentMovementPlaneTypeHandleRO.Update(ref systemState);
+ ReadyToTraverseOffMeshLinkTypeHandleRW.Update(ref systemState);
+ }
+
+ public JobHandle ScheduleParallel (ref SystemState systemState, EntityQuery query, JobHandle dependency) {
+ Update(ref systemState);
+ return new JobRepairPath {
+ scheduler = this,
+ onlyApplyPendingPaths = onlyApplyPendingPaths
+ }.ScheduleParallel(query, dependency);
+ }
+ }
+
+ public Scheduler scheduler;
+
+ [NativeDisableContainerSafetyRestriction]
+ public NativeArray indicesScratch;
+ [NativeDisableContainerSafetyRestriction]
+ public NativeList nextCornersScratch;
+ public bool onlyApplyPendingPaths;
+
+
+ public void Execute (in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in Unity.Burst.Intrinsics.v128 chunkEnabledMask) {
+ if (!indicesScratch.IsCreated) {
+ nextCornersScratch = new NativeList(Allocator.Temp);
+ indicesScratch = new NativeArray(8, Allocator.Temp);
+ }
+
+ unsafe {
+ var localTransforms = (LocalTransform*)chunk.GetNativeArray(ref scheduler.LocalTransformTypeHandleRO).GetUnsafeReadOnlyPtr();
+ var movementStates = (MovementState*)chunk.GetNativeArray(ref scheduler.MovementStateTypeHandleRW).GetUnsafePtr();
+ var agentCylinderShapes = (AgentCylinderShape*)chunk.GetNativeArray(ref scheduler.AgentCylinderShapeTypeHandleRO).GetUnsafeReadOnlyPtr();
+ var destinationPoints = (DestinationPoint*)chunk.GetNativeArray(ref scheduler.DestinationPointTypeHandleRO).GetUnsafeReadOnlyPtr();
+ var movementSettings = (MovementSettings*)chunk.GetNativeArray(ref scheduler.MovementSettingsTypeHandleRO).GetUnsafeReadOnlyPtr();
+ var agentMovementPlanes = (AgentMovementPlane*)chunk.GetNativeArray(ref scheduler.AgentMovementPlaneTypeHandleRO).GetUnsafeReadOnlyPtr();
+ var mask = chunk.GetEnabledMask(ref scheduler.ReadyToTraverseOffMeshLinkTypeHandleRW);
+
+ var managedStates = chunk.GetManagedComponentAccessor(ref scheduler.ManagedStateTypeHandleRW, (EntityManager)scheduler.entityManagerHandle.Target);
+
+ for (int i = 0; i < chunk.Count; i++) {
+ Execute(
+ ref localTransforms[i],
+ ref movementStates[i],
+ ref agentCylinderShapes[i],
+ ref agentMovementPlanes[i],
+ ref destinationPoints[i],
+ mask.GetEnabledRefRW(i),
+ managedStates[i],
+ in movementSettings[i],
+ nextCornersScratch,
+ ref indicesScratch,
+ Allocator.Temp,
+ onlyApplyPendingPaths
+ );
+ }
+ }
+ }
+
+ private static readonly ProfilerMarker MarkerRepair = new ProfilerMarker("Repair");
+ private static readonly ProfilerMarker MarkerGetNextCorners = new ProfilerMarker("GetNextCorners");
+ private static readonly ProfilerMarker MarkerUpdateReachedEndInfo = new ProfilerMarker("UpdateReachedEndInfo");
+
+ public static void Execute (ref LocalTransform transform, ref MovementState state, ref AgentCylinderShape shape, ref AgentMovementPlane movementPlane, ref DestinationPoint destination, EnabledRefRW readyToTraverseOffMeshLink, ManagedState managedState, in MovementSettings settings, NativeList nextCornersScratch, ref NativeArray indicesScratch, Allocator allocator, bool onlyApplyPendingPaths) {
+ // Only enabled by the PollPendingPathsSystem
+ if (onlyApplyPendingPaths) {
+ if (managedState.pendingPath != null && managedState.pendingPath.IsDone()) {
+ // The path has been calculated, apply it to the agent
+ // Immediately after this we must also repair the path to ensure that it is valid and that
+ // all properties like #MovementState.reachedEndOfPath are correct.
+ ManagedState.SetPath(managedState.pendingPath, managedState, in movementPlane, ref destination);
+ } else {
+ // The agent has no path that has just been calculated, skip it
+ return;
+ }
+ }
+
+ ref var pathTracer = ref managedState.pathTracer;
+
+ if (pathTracer.hasPath) {
+ MarkerRepair.Begin();
+ // Update the start and end points of the path based on the current position and destination.
+ // This will repair the path if necessary, ensuring that the agent has a valid, if not necessarily optimal, path.
+ // If it cannot be repaired well, the path will be marked as stale.
+ state.closestOnNavmesh = pathTracer.UpdateStart(transform.Position, PathTracer.RepairQuality.High, movementPlane.value, managedState.pathfindingSettings.traversalProvider, managedState.activePath);
+ state.endOfPath = pathTracer.UpdateEnd(destination.destination, PathTracer.RepairQuality.High, movementPlane.value, managedState.pathfindingSettings.traversalProvider, managedState.activePath);
+ MarkerRepair.End();
+
+ if (state.pathTracerVersion != pathTracer.version) {
+ nextCornersScratch.Clear();
+
+ MarkerGetNextCorners.Begin();
+ // Find the next corners of the path. The first corner is our current position,
+ // the second corner is the one we are moving towards and the third corner is the one after that.
+ //
+ // Using GetNextCorners with the default transformation instead of ConvertCornerIndicesToPathProjected
+ // is about 20% faster, but it does not work well at all on spherical worlds.
+ // In the future we might want to switch dynamically between these modes,
+ // but on the other hand, it is very nice to be able to use the exact same code path for everything.
+ // pathTracer.GetNextCorners(nextCornersScratch, 3, ref indicesScratch, allocator);
+ var numCorners = pathTracer.GetNextCornerIndices(ref indicesScratch, pathTracer.desiredCornersForGoodSimplification, allocator, out bool lastCorner, managedState.pathfindingSettings.traversalProvider, managedState.activePath);
+ pathTracer.ConvertCornerIndicesToPathProjected(indicesScratch, numCorners, lastCorner, nextCornersScratch, movementPlane.value.up);
+ MarkerGetNextCorners.End();
+
+ // We need to copy a few fields to a new struct, in order to be able to pass it to a burstified function
+ var pathTracerInfo = new JobRepairPathHelpers.PathTracerInfo {
+ endPointOfFirstPart = pathTracer.endPointOfFirstPart,
+ partCount = pathTracer.partCount,
+ isStale = pathTracer.isStale
+ };
+ var nextCorners = nextCornersScratch.AsUnsafeSpan();
+ JobRepairPathHelpers.UpdateReachedEndInfo(ref nextCorners, ref state, ref movementPlane, ref transform, ref shape, ref destination, settings.stopDistance, ref pathTracerInfo);
+ state.pathTracerVersion = pathTracer.version;
+ } else {
+ JobRepairPathHelpers.UpdateReachedOrientation(ref state, ref transform, ref movementPlane, ref destination);
+ }
+
+ if (pathTracer.startNode != null && !pathTracer.startNode.Destroyed && pathTracer.startNode.Walkable) {
+ state.graphIndex = pathTracer.startNode.GraphIndex;
+ state.hierarchicalNodeIndex = pathTracer.startNode.HierarchicalNodeIndex;
+ } else {
+ state.graphIndex = GraphNode.InvalidGraphIndex;
+ state.hierarchicalNodeIndex = -1;
+ }
+ } else {
+ state.SetPathIsEmpty(transform.Position);
+ }
+
+ if (readyToTraverseOffMeshLink.IsValid) readyToTraverseOffMeshLink.ValueRW = state.reachedEndOfPart && managedState.pathTracer.isNextPartValidLink;
+ }
+ }
+
+ [BurstCompile]
+ static class JobRepairPathHelpers {
+ public struct PathTracerInfo {
+ public float3 endPointOfFirstPart;
+ public int partCount;
+ // Bools are not blittable by burst so we must use a byte instead. Very ugly, but it is what it is.
+ byte isStaleBacking;
+ public bool isStale {
+ get => isStaleBacking != 0;
+ set => isStaleBacking = value ? (byte)1 : (byte)0;
+ }
+ }
+
+ /// Checks if the agent has reached its destination, or the end of the path
+ [BurstCompile]
+ public static void UpdateReachedEndInfo (ref UnsafeSpan nextCorners, ref MovementState state, ref AgentMovementPlane movementPlane, ref LocalTransform transform, ref AgentCylinderShape shape, ref DestinationPoint destination, float stopDistance, ref PathTracerInfo pathTracer) {
+ // TODO: Edit GetNextCorners so that it gets corners until at least stopDistance units from the agent
+ state.nextCorner = nextCorners.length > 1 ? nextCorners[1] : transform.Position;
+ state.remainingDistanceToEndOfPart = PathTracer.RemainingDistanceLowerBound(in nextCorners, in pathTracer.endPointOfFirstPart, in movementPlane.value);
+
+ // TODO: Check if end node is the globally closest node
+ movementPlane.value.ToPlane(pathTracer.endPointOfFirstPart - transform.Position, out var elevationDiffEndOfPart);
+ var validHeightRangeEndOfPart = elevationDiffEndOfPart< shape.height && elevationDiffEndOfPart > -0.5f*shape.height;
+
+ movementPlane.value.ToPlane(destination.destination - transform.Position, out var elevationDiffDestination);
+ var validHeightRangeDestination = elevationDiffDestination< shape.height && elevationDiffDestination > -0.5f*shape.height;
+ var endOfPathToDestination = math.length(movementPlane.value.ToPlane(destination.destination - state.endOfPath));
+ // If reachedEndOfPath is true we allow a slightly larger margin of error for reachedDestination.
+ // This is to ensure that if reachedEndOfPath becomes true, it is very likely that reachedDestination becomes
+ // true during the same frame.
+ const float FUZZ = 0.01f;
+ // When checking if the agent has reached the end of the current part (mostly used for off-mesh-links), we check against
+ // the agent's radius. This is because when there are many agents trying to reach the same off-mesh-link, the agents will
+ // crowd up and it may become hard to get to a point closer than the agent's radius.
+ state.reachedEndOfPart = !pathTracer.isStale && validHeightRangeEndOfPart && state.remainingDistanceToEndOfPart <= shape.radius*1.1f;
+ state.reachedEndOfPath = !pathTracer.isStale && validHeightRangeEndOfPart && pathTracer.partCount == 1 && state.remainingDistanceToEndOfPart <= stopDistance;
+ state.reachedDestination = !pathTracer.isStale && validHeightRangeDestination && pathTracer.partCount == 1 && state.remainingDistanceToEndOfPart + endOfPathToDestination <= stopDistance + (state.reachedEndOfPath ? FUZZ : 0);
+ state.traversingLastPart = pathTracer.partCount == 1;
+ UpdateReachedOrientation(ref state, ref transform, ref movementPlane, ref destination);
+ }
+
+ /// Checks if the agent is oriented towards the desired facing direction
+ public static void UpdateReachedOrientation (ref MovementState state, ref LocalTransform transform, ref AgentMovementPlane movementPlane, ref DestinationPoint destination) {
+ state.reachedEndOfPathAndOrientation = state.reachedEndOfPath;
+ state.reachedDestinationAndOrientation = state.reachedDestination;
+ if (state.reachedEndOfPathAndOrientation || state.reachedDestinationAndOrientation) {
+ var reachedOrientation = ReachedDesiredOrientation(ref transform, ref movementPlane, ref destination);
+ state.reachedEndOfPathAndOrientation &= reachedOrientation;
+ state.reachedDestinationAndOrientation &= reachedOrientation;
+ }
+ }
+
+ static bool ReachedDesiredOrientation (ref LocalTransform transform, ref AgentMovementPlane movementPlane, ref DestinationPoint destination) {
+ var facingDirection2D = math.normalizesafe(movementPlane.value.ToPlane(destination.facingDirection));
+
+ // If no desired facing direction is set, then we always treat the orientation as correct
+ if (math.all(facingDirection2D == 0)) return true;
+
+ var forward2D = math.normalizesafe(movementPlane.value.ToPlane(transform.Forward()));
+ const float ANGLE_THRESHOLD_COS = 0.9999f;
+ return math.dot(forward2D, facingDirection2D) >= ANGLE_THRESHOLD_COS;
+ }
+ }
+}
+#endif
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobRepairPath.cs.meta b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobRepairPath.cs.meta
new file mode 100644
index 0000000..d1ba13b
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobRepairPath.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: db3650f148245bf4ba822d172a34cc65
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobStartOffMeshLinkTransition.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobStartOffMeshLinkTransition.cs
new file mode 100644
index 0000000..994c68d
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/ECS/Jobs/JobStartOffMeshLinkTransition.cs
@@ -0,0 +1,33 @@
+#pragma warning disable 0282 // Allows the 'partial' keyword without warnings
+#if MODULE_ENTITIES
+using Unity.Entities;
+using Unity.Mathematics;
+
+namespace Pathfinding.ECS {
+ public partial struct JobStartOffMeshLinkTransition {
+ public EntityCommandBuffer commandBuffer;
+
+ ///
+ /// This is a fallback for traversing off-mesh links in case the user has not specified a custom traversal method.
+ /// It is a coroutine which will move the agent from the start point of the link to the end point of the link.
+ /// It will also disable RVO for the agent while traversing the link.
+ ///
+ public static System.Collections.Generic.IEnumerable