diff options
author | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 |
---|---|---|
committer | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 |
commit | 8722a9920c1f6119bf6e769cba270e63097f8e25 (patch) | |
tree | 2eaf9865de7fb1404546de4a4296553d8f68cc3b /Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/GraphCollision.cs | |
parent | 3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff) |
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/GraphCollision.cs')
-rw-r--r-- | Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/GraphCollision.cs | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/GraphCollision.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/GraphCollision.cs new file mode 100644 index 0000000..a21f179 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Graphs/Grid/GraphCollision.cs @@ -0,0 +1,387 @@ +using UnityEngine; +using System.Collections.Generic; +using Unity.Collections; + +namespace Pathfinding.Graphs.Grid { + using Pathfinding.Util; + using Pathfinding.Graphs.Grid.Jobs; + using Pathfinding.Jobs; + + /// <summary> + /// Handles collision checking for graphs. + /// Mostly used by grid based graphs + /// </summary> + [System.Serializable] + public class GraphCollision { + /// <summary> + /// Collision shape to use. + /// See: <see cref="ColliderType"/> + /// </summary> + public ColliderType type = ColliderType.Capsule; + + /// <summary> + /// Diameter of capsule or sphere when checking for collision. + /// When checking for collisions the system will check if any colliders + /// overlap a specific shape at the node's position. The shape is determined + /// by the <see cref="type"/> field. + /// + /// A diameter of 1 means that the shape has a diameter equal to the node's width, + /// or in other words it is equal to <see cref="Pathfinding.GridGraph.nodeSize"/>. + /// + /// If <see cref="type"/> is set to Ray, this does not affect anything. + /// + /// [Open online documentation to see images] + /// </summary> + public float diameter = 1F; + + /// <summary> + /// Height of capsule or length of ray when checking for collision. + /// If <see cref="type"/> is set to Sphere, this does not affect anything. + /// + /// [Open online documentation to see images] + /// + /// Warning: In contrast to Unity's capsule collider and character controller this height does not include the end spheres of the capsule, but only the cylinder part. + /// This is mostly for historical reasons. + /// </summary> + public float height = 2F; + + /// <summary> + /// Height above the ground that collision checks should be done. + /// For example, if the ground was found at y=0, collisionOffset = 2 + /// type = Capsule and height = 3 then the physics system + /// will be queried to see if there are any colliders in a capsule + /// for which the bottom sphere that is made up of is centered at y=2 + /// and the top sphere has its center at y=2+3=5. + /// + /// If type = Sphere then the sphere's center would be at y=2 in this case. + /// </summary> + public float collisionOffset; + + /// <summary> + /// Direction of the ray when checking for collision. + /// If <see cref="type"/> is not Ray, this does not affect anything + /// + /// Deprecated: Only the Both mode is supported now. + /// </summary> + [System.Obsolete("Only the Both mode is supported now")] + public RayDirection rayDirection = RayDirection.Both; + + /// <summary>Layers to be treated as obstacles.</summary> + public LayerMask mask; + + /// <summary>Layers to be included in the height check.</summary> + public LayerMask heightMask = -1; + + /// <summary> + /// The height to check from when checking height ('ray length' in the inspector). + /// + /// As the image below visualizes, different ray lengths can make the ray hit different things. + /// The distance is measured up from the graph plane. + /// + /// [Open online documentation to see images] + /// </summary> + public float fromHeight = 100; + + /// <summary> + /// Toggles thick raycast. + /// See: https://docs.unity3d.com/ScriptReference/Physics.SphereCast.html + /// </summary> + public bool thickRaycast; + + /// <summary> + /// Diameter of the thick raycast in nodes. + /// 1 equals <see cref="Pathfinding.GridGraph.nodeSize"/> + /// </summary> + public float thickRaycastDiameter = 1; + + /// <summary>Make nodes unwalkable when no ground was found with the height raycast. If height raycast is turned off, this doesn't affect anything.</summary> + public bool unwalkableWhenNoGround = true; + + /// <summary> + /// Use Unity 2D Physics API. + /// + /// If enabled, the 2D Physics API will be used, and if disabled, the 3D Physics API will be used. + /// + /// This changes the collider types (see <see cref="type)"/> from 3D versions to their corresponding 2D versions. For example the sphere shape becomes a circle. + /// + /// The <see cref="heightCheck"/> setting will be ignored when 2D physics is used. + /// + /// See: http://docs.unity3d.com/ScriptReference/Physics2D.html + /// </summary> + public bool use2D; + + /// <summary>Toggle collision check</summary> + public bool collisionCheck = true; + + /// <summary> + /// Toggle height check. If false, the grid will be flat. + /// + /// This setting will be ignored when 2D physics is used. + /// </summary> + public bool heightCheck = true; + + /// <summary> + /// Direction to use as UP. + /// See: Initialize + /// </summary> + public Vector3 up; + + /// <summary> + /// <see cref="up"/> * <see cref="height"/>. + /// See: Initialize + /// </summary> + private Vector3 upheight; + + /// <summary>Used for 2D collision queries</summary> + private ContactFilter2D contactFilter; + + /// <summary> + /// Just so that the Physics2D.OverlapPoint method has some buffer to store things in. + /// We never actually read from this array, so we don't even care if this is thread safe. + /// </summary> + private static Collider2D[] dummyArray = new Collider2D[1]; + + /// <summary> + /// <see cref="diameter"/> * scale * 0.5. + /// Where scale usually is <see cref="Pathfinding.GridGraph.nodeSize"/> + /// See: Initialize + /// </summary> + private float finalRadius; + + /// <summary> + /// <see cref="thickRaycastDiameter"/> * scale * 0.5. + /// Where scale usually is <see cref="Pathfinding.GridGraph.nodeSize"/> See: Initialize + /// </summary> + private float finalRaycastRadius; + + /// <summary>Offset to apply after each raycast to make sure we don't hit the same point again in CheckHeightAll</summary> + public const float RaycastErrorMargin = 0.005F; + + /// <summary> + /// Sets up several variables using the specified matrix and scale. + /// See: GraphCollision.up + /// See: GraphCollision.upheight + /// See: GraphCollision.finalRadius + /// See: GraphCollision.finalRaycastRadius + /// </summary> + public void Initialize (GraphTransform transform, float scale) { + up = (transform.Transform(Vector3.up) - transform.Transform(Vector3.zero)).normalized; + upheight = up*height; + finalRadius = diameter*scale*0.5F; + finalRaycastRadius = thickRaycastDiameter*scale*0.5F; + contactFilter = new ContactFilter2D { layerMask = mask, useDepth = false, useLayerMask = true, useNormalAngle = false, useTriggers = false }; + } + + /// <summary> + /// Returns true if the position is not obstructed. + /// If <see cref="collisionCheck"/> is false, this will always return true. + /// </summary> + public bool Check (Vector3 position) { + if (!collisionCheck) { + return true; + } + + if (use2D) { + switch (type) { + case ColliderType.Capsule: + case ColliderType.Sphere: + return Physics2D.OverlapCircle(position, finalRadius, contactFilter, dummyArray) == 0; + default: + return Physics2D.OverlapPoint(position, contactFilter, dummyArray) == 0; + } + } + + position += up*collisionOffset; + switch (type) { + case ColliderType.Capsule: + return !Physics.CheckCapsule(position, position+upheight, finalRadius, mask, QueryTriggerInteraction.Ignore); + case ColliderType.Sphere: + return !Physics.CheckSphere(position, finalRadius, mask, QueryTriggerInteraction.Ignore); + default: + return !Physics.Raycast(position, up, height, mask, QueryTriggerInteraction.Ignore) && !Physics.Raycast(position+upheight, -up, height, mask, QueryTriggerInteraction.Ignore); + } + } + + /// <summary> + /// Returns the position with the correct height. + /// If <see cref="heightCheck"/> is false, this will return position. + /// </summary> + public Vector3 CheckHeight (Vector3 position) { + RaycastHit hit; + bool walkable; + + return CheckHeight(position, out hit, out walkable); + } + + /// <summary> + /// Returns the position with the correct height. + /// If <see cref="heightCheck"/> is false, this will return position. + /// walkable will be set to false if nothing was hit. + /// The ray will check a tiny bit further than to the grids base to avoid floating point errors when the ground is exactly at the base of the grid + /// </summary> + public Vector3 CheckHeight (Vector3 position, out RaycastHit hit, out bool walkable) { + walkable = true; + + if (!heightCheck || use2D) { + hit = new RaycastHit(); + return position; + } + + if (thickRaycast) { + var ray = new Ray(position+up*fromHeight, -up); + if (Physics.SphereCast(ray, finalRaycastRadius, out hit, fromHeight+0.005F, heightMask, QueryTriggerInteraction.Ignore)) { + return VectorMath.ClosestPointOnLine(ray.origin, ray.origin+ray.direction, hit.point); + } + + walkable &= !unwalkableWhenNoGround; + } else { + // Cast a ray from above downwards to try to find the ground + if (Physics.Raycast(position+up*fromHeight, -up, out hit, fromHeight+0.005F, heightMask, QueryTriggerInteraction.Ignore)) { + return hit.point; + } + + walkable &= !unwalkableWhenNoGround; + } + return position; + } + + /// <summary>Internal buffer used by <see cref="CheckHeightAll"/></summary> + RaycastHit[] hitBuffer = new RaycastHit[8]; + + /// <summary> + /// Returns all hits when checking height for position. + /// Warning: Does not work well with thick raycast, will only return an object a single time + /// + /// Warning: The returned array is ephermal. It will be invalidated when this method is called again. + /// If you need persistent results you should copy it. + /// + /// The returned array may be larger than the actual number of hits, the numHits out parameter indicates how many hits there actually were. + /// </summary> + public RaycastHit[] CheckHeightAll (Vector3 position, out int numHits) { + if (!heightCheck || use2D) { + hitBuffer[0] = new RaycastHit { + point = position, + distance = 0, + }; + numHits = 1; + return hitBuffer; + } + + // Cast a ray from above downwards to try to find the ground +#if UNITY_2017_1_OR_NEWER + numHits = Physics.RaycastNonAlloc(position+up*fromHeight, -up, hitBuffer, fromHeight+0.005F, heightMask, QueryTriggerInteraction.Ignore); + if (numHits == hitBuffer.Length) { + // Try again with a larger buffer + hitBuffer = new RaycastHit[hitBuffer.Length*2]; + return CheckHeightAll(position, out numHits); + } + return hitBuffer; +#else + var result = Physics.RaycastAll(position+up*fromHeight, -up, fromHeight+0.005F, heightMask, QueryTriggerInteraction.Ignore); + numHits = result.Length; + return result; +#endif + } + + /// <summary> + /// Returns if the position is obstructed for all nodes using the Ray collision checking method. + /// collisionCheckResult[i] = true if there were no obstructions, false otherwise + /// </summary> + public void JobCollisionRay (NativeArray<Vector3> nodePositions, NativeArray<bool> collisionCheckResult, Vector3 up, Allocator allocationMethod, JobDependencyTracker dependencyTracker) { + var collisionRaycastCommands1 = dependencyTracker.NewNativeArray<RaycastCommand>(nodePositions.Length, allocationMethod); + var collisionRaycastCommands2 = dependencyTracker.NewNativeArray<RaycastCommand>(nodePositions.Length, allocationMethod); + var collisionHits1 = dependencyTracker.NewNativeArray<RaycastHit>(nodePositions.Length, allocationMethod); + var collisionHits2 = dependencyTracker.NewNativeArray<RaycastHit>(nodePositions.Length, allocationMethod); + + // Fire rays from above down to the nodes' positions + new JobPrepareRaycasts { + origins = nodePositions, + originOffset = up * (height + collisionOffset), + direction = -up, + distance = height, + mask = mask, + physicsScene = Physics.defaultPhysicsScene, + raycastCommands = collisionRaycastCommands1, + }.Schedule(dependencyTracker); + + // Fire rays from the node up towards the sky + new JobPrepareRaycasts { + origins = nodePositions, + originOffset = up * collisionOffset, + direction = up, + distance = height, + mask = mask, + physicsScene = Physics.defaultPhysicsScene, + raycastCommands = collisionRaycastCommands2, + }.Schedule(dependencyTracker); + + dependencyTracker.ScheduleBatch(collisionRaycastCommands1, collisionHits1, 2048); + dependencyTracker.ScheduleBatch(collisionRaycastCommands2, collisionHits2, 2048); + + new JobMergeRaycastCollisionHits { + hit1 = collisionHits1, + hit2 = collisionHits2, + result = collisionCheckResult, + }.Schedule(dependencyTracker); + } + +#if UNITY_2022_2_OR_NEWER + public void JobCollisionCapsule (NativeArray<Vector3> nodePositions, NativeArray<bool> collisionCheckResult, Vector3 up, Allocator allocationMethod, JobDependencyTracker dependencyTracker) { + var commands = dependencyTracker.NewNativeArray<OverlapCapsuleCommand>(nodePositions.Length, allocationMethod); + var collisionHits = dependencyTracker.NewNativeArray<ColliderHit>(nodePositions.Length, allocationMethod); + new JobPrepareCapsuleCommands { + origins = nodePositions, + originOffset = up * collisionOffset, + direction = up * height, + radius = finalRadius, + mask = mask, + commands = commands, + physicsScene = Physics.defaultPhysicsScene, + }.Schedule(dependencyTracker); + dependencyTracker.ScheduleBatch(commands, collisionHits, 2048); + new JobColliderHitsToBooleans { + hits = collisionHits, + result = collisionCheckResult, + }.Schedule(dependencyTracker); + } + + public void JobCollisionSphere (NativeArray<Vector3> nodePositions, NativeArray<bool> collisionCheckResult, Vector3 up, Allocator allocationMethod, JobDependencyTracker dependencyTracker) { + var commands = dependencyTracker.NewNativeArray<OverlapSphereCommand>(nodePositions.Length, allocationMethod); + var collisionHits = dependencyTracker.NewNativeArray<ColliderHit>(nodePositions.Length, allocationMethod); + new JobPrepareSphereCommands { + origins = nodePositions, + originOffset = up * collisionOffset, + radius = finalRadius, + mask = mask, + commands = commands, + physicsScene = Physics.defaultPhysicsScene, + }.Schedule(dependencyTracker); + dependencyTracker.ScheduleBatch(commands, collisionHits, 2048); + new JobColliderHitsToBooleans { + hits = collisionHits, + result = collisionCheckResult, + }.Schedule(dependencyTracker); + } +#endif + } + + /// <summary> + /// Determines collision check shape. + /// See: <see cref="GraphCollision"/> + /// </summary> + public enum ColliderType { + /// <summary>Uses a Sphere, Physics.CheckSphere. In 2D this is a circle instead.</summary> + Sphere, + /// <summary>Uses a Capsule, Physics.CheckCapsule. This will behave identically to the Sphere mode in 2D.</summary> + Capsule, + /// <summary>Uses a Ray, Physics.Linecast. In 2D this is a single point instead.</summary> + Ray + } + + /// <summary>Determines collision check ray direction</summary> + public enum RayDirection { + Up, /// <summary>< Casts the ray from the bottom upwards</summary> + Down, /// <summary>< Casts the ray from the top downwards</summary> + Both /// <summary>< Casts two rays in both directions</summary> + } +} |