#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