123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713 |
- using UnityEngine;
- using System.Collections.Generic;
- using Pathfinding.Util;
- using Pathfinding.Serialization;
- namespace Pathfinding {
- /// <summary>
- /// Exposes internal methods for graphs.
- /// This is used to hide methods that should not be used by any user code
- /// but still have to be 'public' or 'internal' (which is pretty much the same as 'public'
- /// as this library is distributed with source code).
- ///
- /// Hiding the internal methods cleans up the documentation and IntelliSense suggestions.
- /// </summary>
- public interface IGraphInternals {
- string SerializedEditorSettings { get; set; }
- void OnDestroy();
- void DestroyAllNodes();
- IEnumerable<Progress> ScanInternal();
- void SerializeExtraInfo(GraphSerializationContext ctx);
- void DeserializeExtraInfo(GraphSerializationContext ctx);
- void PostDeserialization(GraphSerializationContext ctx);
- void DeserializeSettingsCompatibility(GraphSerializationContext ctx);
- }
- /// <summary>Base class for all graphs</summary>
- public abstract class NavGraph : IGraphInternals {
- /// <summary>Reference to the AstarPath object in the scene</summary>
- public AstarPath active;
- /// <summary>
- /// Used as an ID of the graph, considered to be unique.
- /// Note: This is Pathfinding.Util.Guid not System.Guid. A replacement for System.Guid was coded for better compatibility with iOS
- /// </summary>
- [JsonMember]
- public Guid guid;
- /// <summary>Default penalty to apply to all nodes</summary>
- [JsonMember]
- public uint initialPenalty;
- /// <summary>Is the graph open in the editor</summary>
- [JsonMember]
- public bool open;
- /// <summary>Index of the graph, used for identification purposes</summary>
- public uint graphIndex;
- /// <summary>
- /// Name of the graph.
- /// Can be set in the unity editor
- /// </summary>
- [JsonMember]
- public string name;
- /// <summary>
- /// Enable to draw gizmos in the Unity scene view.
- /// In the inspector this value corresponds to the state of
- /// the 'eye' icon in the top left corner of every graph inspector.
- /// </summary>
- [JsonMember]
- public bool drawGizmos = true;
- /// <summary>
- /// Used in the editor to check if the info screen is open.
- /// Should be inside UNITY_EDITOR only \<see cref="ifs"/> but just in case anyone tries to serialize a NavGraph instance using Unity, I have left it like this as it would otherwise cause a crash when building.
- /// Version 3.0.8.1 was released because of this bug only
- /// </summary>
- [JsonMember]
- public bool infoScreenOpen;
- /// <summary>Used in the Unity editor to store serialized settings for graph inspectors</summary>
- [JsonMember]
- string serializedEditorSettings;
- /// <summary>True if the graph exists, false if it has been destroyed</summary>
- internal bool exists { get { return active != null; } }
- /// <summary>
- /// Number of nodes in the graph.
- /// Note that this is, unless the graph type has overriden it, an O(n) operation.
- ///
- /// This is an O(1) operation for grid graphs and point graphs.
- /// For layered grid graphs it is an O(n) operation.
- /// </summary>
- public virtual int CountNodes () {
- int count = 0;
- GetNodes(node => count++);
- return count;
- }
- /// <summary>Calls a delegate with all nodes in the graph until the delegate returns false</summary>
- public void GetNodes (System.Func<GraphNode, bool> action) {
- bool cont = true;
- GetNodes(node => {
- if (cont) cont &= action(node);
- });
- }
- /// <summary>
- /// Calls a delegate with all nodes in the graph.
- /// This is the primary way of iterating through all nodes in a graph.
- ///
- /// Do not change the graph structure inside the delegate.
- ///
- /// <code>
- /// var gg = AstarPath.active.data.gridGraph;
- ///
- /// gg.GetNodes(node => {
- /// // Here is a node
- /// Debug.Log("I found a node at position " + (Vector3)node.position);
- /// });
- /// </code>
- ///
- /// If you want to store all nodes in a list you can do this
- ///
- /// <code>
- /// var gg = AstarPath.active.data.gridGraph;
- ///
- /// List<GraphNode> nodes = new List<GraphNode>();
- ///
- /// gg.GetNodes((System.Action<GraphNode>)nodes.Add);
- /// </code>
- ///
- /// See: <see cref="Pathfinding.AstarData.GetNodes"/>
- /// </summary>
- public abstract void GetNodes(System.Action<GraphNode> action);
- /// <summary>
- /// A matrix for translating/rotating/scaling the graph.
- /// Deprecated: Use the transform field (only available on some graph types) instead
- /// </summary>
- [System.Obsolete("Use the transform field (only available on some graph types) instead", true)]
- public Matrix4x4 matrix = Matrix4x4.identity;
- /// <summary>
- /// Inverse of matrix.
- /// Deprecated: Use the transform field (only available on some graph types) instead
- /// </summary>
- [System.Obsolete("Use the transform field (only available on some graph types) instead", true)]
- public Matrix4x4 inverseMatrix = Matrix4x4.identity;
- /// <summary>
- /// Use to set both matrix and inverseMatrix at the same time.
- /// Deprecated: Use the transform field (only available on some graph types) instead
- /// </summary>
- [System.Obsolete("Use the transform field (only available on some graph types) instead", true)]
- public void SetMatrix (Matrix4x4 m) {
- matrix = m;
- inverseMatrix = m.inverse;
- }
- /// <summary>
- /// Moves nodes in this graph.
- /// Deprecated: Use RelocateNodes(Matrix4x4) instead.
- /// To keep the same behavior you can call RelocateNodes(newMatrix * oldMatrix.inverse).
- /// </summary>
- [System.Obsolete("Use RelocateNodes(Matrix4x4) instead. To keep the same behavior you can call RelocateNodes(newMatrix * oldMatrix.inverse).")]
- public void RelocateNodes (Matrix4x4 oldMatrix, Matrix4x4 newMatrix) {
- RelocateNodes(newMatrix * oldMatrix.inverse);
- }
- /// <summary>
- /// Throws an exception if it is not safe to update internal graph data right now.
- ///
- /// It is safe to update graphs when graphs are being scanned, or inside a work item.
- /// In other cases pathfinding could be running at the same time, which would not appreciate graph data changing under its feet.
- ///
- /// See: <see cref="AstarPath.AddWorkItem"/>
- /// </summary>
- protected void AssertSafeToUpdateGraph () {
- if (!active.IsAnyWorkItemInProgress && !active.isScanning) {
- throw new System.Exception("Trying to update graphs when it is not safe to do so. Graph updates must be done inside a work item or when a graph is being scanned. See AstarPath.AddWorkItem");
- }
- }
- /// <summary>
- /// Moves the nodes in this graph.
- /// Multiplies all node positions by deltaMatrix.
- ///
- /// For example if you want to move all your nodes in e.g a point graph 10 units along the X axis from the initial position
- /// <code>
- /// var graph = AstarPath.data.pointGraph;
- /// var m = Matrix4x4.TRS (new Vector3(10,0,0), Quaternion.identity, Vector3.one);
- /// graph.RelocateNodes (m);
- /// </code>
- ///
- /// Note: For grid graphs, navmesh graphs and recast graphs it is recommended to
- /// use their custom overloads of the RelocateNodes method which take parameters
- /// for e.g center and nodeSize (and additional parameters) instead since
- /// they are both easier to use and are less likely to mess up pathfinding.
- ///
- /// Warning: This method is lossy for PointGraphs, so calling it many times may
- /// cause node positions to lose precision. For example if you set the scale
- /// to 0 in one call then all nodes will be scaled/moved to the same point and
- /// you will not be able to recover their original positions. The same thing
- /// happens for other - less extreme - values as well, but to a lesser degree.
- /// </summary>
- public virtual void RelocateNodes (Matrix4x4 deltaMatrix) {
- GetNodes(node => node.position = ((Int3)deltaMatrix.MultiplyPoint((Vector3)node.position)));
- }
- /// <summary>
- /// Returns the nearest node to a position.
- /// See: Pathfinding.NNConstraint.None
- /// </summary>
- /// <param name="position">The position to try to find a close node to</param>
- public NNInfoInternal GetNearest (Vector3 position) {
- return GetNearest(position, NNConstraint.None);
- }
- /// <summary>Returns the nearest node to a position using the specified NNConstraint.</summary>
- /// <param name="position">The position to try to find a close node to</param>
- /// <param name="constraint">Can for example tell the function to try to return a walkable node. If you do not get a good node back, consider calling GetNearestForce.</param>
- public NNInfoInternal GetNearest (Vector3 position, NNConstraint constraint) {
- return GetNearest(position, constraint, null);
- }
- /// <summary>Returns the nearest node to a position using the specified NNConstraint.</summary>
- /// <param name="position">The position to try to find a close node to</param>
- /// <param name="hint">Can be passed to enable some graph generators to find the nearest node faster.</param>
- /// <param name="constraint">Can for example tell the function to try to return a walkable node. If you do not get a good node back, consider calling GetNearestForce.</param>
- public virtual NNInfoInternal GetNearest (Vector3 position, NNConstraint constraint, GraphNode hint) {
- // This is a default implementation and it is pretty slow
- // Graphs usually override this to provide faster and more specialised implementations
- float maxDistSqr = constraint == null || constraint.constrainDistance ? AstarPath.active.maxNearestNodeDistanceSqr : float.PositiveInfinity;
- float minDist = float.PositiveInfinity;
- GraphNode minNode = null;
- float minConstDist = float.PositiveInfinity;
- GraphNode minConstNode = null;
- // Loop through all nodes and find the closest suitable node
- GetNodes(node => {
- float dist = (position-(Vector3)node.position).sqrMagnitude;
- if (dist < minDist) {
- minDist = dist;
- minNode = node;
- }
- if (dist < minConstDist && dist < maxDistSqr && (constraint == null || constraint.Suitable(node))) {
- minConstDist = dist;
- minConstNode = node;
- }
- });
- var nnInfo = new NNInfoInternal(minNode);
- nnInfo.constrainedNode = minConstNode;
- if (minConstNode != null) {
- nnInfo.constClampedPosition = (Vector3)minConstNode.position;
- } else if (minNode != null) {
- nnInfo.constrainedNode = minNode;
- nnInfo.constClampedPosition = (Vector3)minNode.position;
- }
- return nnInfo;
- }
- /// <summary>
- /// Returns the nearest node to a position using the specified <see cref="Pathfinding.NNConstraint"/>.
- /// Returns: an NNInfo. This method will only return an empty NNInfo if there are no nodes which comply with the specified constraint.
- /// </summary>
- public virtual NNInfoInternal GetNearestForce (Vector3 position, NNConstraint constraint) {
- return GetNearest(position, constraint);
- }
- /// <summary>
- /// Function for cleaning up references.
- /// This will be called on the same time as OnDisable on the gameObject which the AstarPath script is attached to (remember, not in the editor).
- /// Use for any cleanup code such as cleaning up static variables which otherwise might prevent resources from being collected.
- /// Use by creating a function overriding this one in a graph class, but always call base.OnDestroy () in that function.
- /// All nodes should be destroyed in this function otherwise a memory leak will arise.
- /// </summary>
- protected virtual void OnDestroy () {
- DestroyAllNodes();
- }
- /// <summary>
- /// Destroys all nodes in the graph.
- /// Warning: This is an internal method. Unless you have a very good reason, you should probably not call it.
- /// </summary>
- protected virtual void DestroyAllNodes () {
- GetNodes(node => node.Destroy());
- }
- /// <summary>
- /// Scan the graph.
- /// Deprecated: Use AstarPath.Scan() instead
- /// </summary>
- [System.Obsolete("Use AstarPath.Scan instead")]
- public void ScanGraph () {
- Scan();
- }
- /// <summary>
- /// Scan the graph.
- ///
- /// Consider using AstarPath.Scan() instead since this function only scans this graph and if you are using multiple graphs
- /// with connections between them, then it is better to scan all graphs at once.
- /// </summary>
- public void Scan () {
- active.Scan(this);
- }
- /// <summary>
- /// Internal method to scan the graph.
- /// Called from AstarPath.ScanAsync.
- /// Override this function to implement custom scanning logic.
- /// Progress objects can be yielded to show progress info in the editor and to split up processing
- /// over several frames when using async scanning.
- /// </summary>
- protected abstract IEnumerable<Progress> ScanInternal();
- /// <summary>
- /// Serializes graph type specific node data.
- /// This function can be overriden to serialize extra node information (or graph information for that matter)
- /// which cannot be serialized using the standard serialization.
- /// Serialize the data in any way you want and return a byte array.
- /// When loading, the exact same byte array will be passed to the DeserializeExtraInfo function.
- /// These functions will only be called if node serialization is enabled.
- /// </summary>
- protected virtual void SerializeExtraInfo (GraphSerializationContext ctx) {
- }
- /// <summary>
- /// Deserializes graph type specific node data.
- /// See: SerializeExtraInfo
- /// </summary>
- protected virtual void DeserializeExtraInfo (GraphSerializationContext ctx) {
- }
- /// <summary>
- /// Called after all deserialization has been done for all graphs.
- /// Can be used to set up more graph data which is not serialized
- /// </summary>
- protected virtual void PostDeserialization (GraphSerializationContext ctx) {
- }
- /// <summary>
- /// An old format for serializing settings.
- /// Deprecated: This is deprecated now, but the deserialization code is kept to
- /// avoid loosing data when upgrading from older versions.
- /// </summary>
- protected virtual void DeserializeSettingsCompatibility (GraphSerializationContext ctx) {
- guid = new Guid(ctx.reader.ReadBytes(16));
- initialPenalty = ctx.reader.ReadUInt32();
- open = ctx.reader.ReadBoolean();
- name = ctx.reader.ReadString();
- drawGizmos = ctx.reader.ReadBoolean();
- infoScreenOpen = ctx.reader.ReadBoolean();
- }
- /// <summary>Draw gizmos for the graph</summary>
- public virtual void OnDrawGizmos (RetainedGizmos gizmos, bool drawNodes) {
- if (!drawNodes) {
- return;
- }
- // This is a relatively slow default implementation.
- // subclasses of the base graph class may override
- // this method to draw gizmos in a more optimized way
- var hasher = new RetainedGizmos.Hasher(active);
- GetNodes(node => hasher.HashNode(node));
- // Update the gizmo mesh if necessary
- if (!gizmos.Draw(hasher)) {
- using (var helper = gizmos.GetGizmoHelper(active, hasher)) {
- GetNodes((System.Action<GraphNode>)helper.DrawConnections);
- }
- }
- if (active.showUnwalkableNodes) DrawUnwalkableNodes(active.unwalkableNodeDebugSize);
- }
- protected void DrawUnwalkableNodes (float size) {
- Gizmos.color = AstarColor.UnwalkableNode;
- GetNodes(node => {
- if (!node.Walkable) Gizmos.DrawCube((Vector3)node.position, Vector3.one*size);
- });
- }
- #region IGraphInternals implementation
- string IGraphInternals.SerializedEditorSettings { get { return serializedEditorSettings; } set { serializedEditorSettings = value; } }
- void IGraphInternals.OnDestroy () { OnDestroy(); }
- void IGraphInternals.DestroyAllNodes () { DestroyAllNodes(); }
- IEnumerable<Progress> IGraphInternals.ScanInternal () { return ScanInternal(); }
- void IGraphInternals.SerializeExtraInfo (GraphSerializationContext ctx) { SerializeExtraInfo(ctx); }
- void IGraphInternals.DeserializeExtraInfo (GraphSerializationContext ctx) { DeserializeExtraInfo(ctx); }
- void IGraphInternals.PostDeserialization (GraphSerializationContext ctx) { PostDeserialization(ctx); }
- void IGraphInternals.DeserializeSettingsCompatibility (GraphSerializationContext ctx) { DeserializeSettingsCompatibility(ctx); }
- #endregion
- }
- /// <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="Pathfinding.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
- /// </summary>
- 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.
- /// 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</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:
- switch (rayDirection) {
- case RayDirection.Both:
- return !Physics.Raycast(position, up, height, mask, QueryTriggerInteraction.Ignore) && !Physics.Raycast(position+upheight, -up, height, mask, QueryTriggerInteraction.Ignore);
- case RayDirection.Up:
- return !Physics.Raycast(position, up, height, mask, QueryTriggerInteraction.Ignore);
- default:
- return !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
- }
- public void DeserializeSettingsCompatibility (GraphSerializationContext ctx) {
- type = (ColliderType)ctx.reader.ReadInt32();
- diameter = ctx.reader.ReadSingle();
- height = ctx.reader.ReadSingle();
- collisionOffset = ctx.reader.ReadSingle();
- rayDirection = (RayDirection)ctx.reader.ReadInt32();
- mask = (LayerMask)ctx.reader.ReadInt32();
- heightMask = (LayerMask)ctx.reader.ReadInt32();
- fromHeight = ctx.reader.ReadSingle();
- thickRaycast = ctx.reader.ReadBoolean();
- thickRaycastDiameter = ctx.reader.ReadSingle();
- unwalkableWhenNoGround = ctx.reader.ReadBoolean();
- use2D = ctx.reader.ReadBoolean();
- collisionCheck = ctx.reader.ReadBoolean();
- heightCheck = ctx.reader.ReadBoolean();
- }
- }
- /// <summary>
- /// Determines collision check shape.
- /// See: <see cref="Pathfinding.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>
- }
- }
|