using System; using UnityEngine; namespace Framework.Support { /// Casts differen collider shapes into the world public class ShapeCaster { #region enum ColliderShape /// Shape of a collider private enum ColliderShape : uint { /// The collider is a perfectly round sphere Sphere = 0, /// The collider is a cylinder with hemispheres at its ends Capsule = 1, /// The collider is a box with different side lengths Box = 2 } #endregion // enum ColliderShape /// Delegate for a shape cast with a single result /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Receives informations about the collider that was hit /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// True if the shape cast hit another collider private delegate bool ShapeCastDelegate( Vector3 position, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ); /// Delegate for a shape cast that returns all hit colliders /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// A list of all colliders hit by the shape cast private delegate RaycastHit[] ShapeCastAllDelegate( Vector3 position, Vector3 direction, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ); /// Delegate for a shape cast that write all hit colliders into an array /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Array that will receive the colliders that were hit /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// The number of colliders that were hit by the shape cast private delegate int ShapeCastNonAllocDelegate( Vector3 position, Vector3 direction, RaycastHit[] results, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ); /// Initialized a new collider caster /// Collider that will be cast public ShapeCaster(Collider collider) { this.shapeCastDelegates = new ShapeCastDelegate[] { sphereCast, capsuleCast, boxCast }; this.shapeCastAllDelegates = new ShapeCastAllDelegate[] { sphereCastAll, capsuleCastAll, boxCastAll }; this.shapeCastNonAllocDelegates = new ShapeCastNonAllocDelegate[] { sphereCastNonAlloc, capsuleCastNonAlloc, boxCastNonAlloc }; changeCollider(collider); } /// Collider the caster is casting /// /// Thrown if the collider was not of a support type /// public Collider Collider { get { switch(this.colliderShape) { case ColliderShape.Sphere: { return this.sphereCollider; } case ColliderShape.Capsule: { return this.capsuleCollider; } case ColliderShape.Box: { return this.boxCollider; } default: { throw new InvalidOperationException("Invalid collider"); } } } set { if(!ReferenceEquals(value, Collider)) { changeCollider(value); } } } /// Casts the shape of the collider into the specified direction /// Position of the game object carrying the collider /// Direction the shape will be cast into /// Receives informations about the collider that was hit /// Maximum distance to consider for collisions /// Layers the shape can hit colliders on /// How to deal with trigger colliders /// True if a collider was hit by the shape cast public bool ShapeCast( Vector3 position, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { return this.shapeCastDelegates[(int)this.colliderShape]( position, direction, out hitInfo, maxDistance, layerMask, queryTriggerInteraction ); } /// Delegate for a shape cast that returns all hit colliders /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// A list of all colliders hit by the shape cast public RaycastHit[] ShapeCastAll( Vector3 position, Vector3 direction, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { return this.shapeCastAllDelegates[(int)this.colliderShape]( position, direction, maxDistance, layerMask, queryTriggerInteraction ); } /// Delegate for a shape cast that write all hit colliders into an array /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Array that will receive the colliders that were hit /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// The number of colliders that were hit by the shape cast public int ShapeCastNonAlloc( Vector3 position, Vector3 direction, RaycastHit[] results, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { return this.shapeCastNonAllocDelegates[(int)this.colliderShape]( position, direction, results, maxDistance, layerMask, queryTriggerInteraction ); } /// Changes the collider that will be cast as a shape /// New collider to cast /// /// Thrown if the collider was not of a support type /// private void changeCollider(Collider collider) { this.sphereCollider = (collider as SphereCollider); this.capsuleCollider = (collider as CapsuleCollider); this.boxCollider = (collider as BoxCollider); if(this.sphereCollider != null) { this.colliderShape = ColliderShape.Sphere; } else if(this.capsuleCollider != null) { this.colliderShape = ColliderShape.Capsule; } else if(this.boxCollider != null) { this.colliderShape = ColliderShape.Box; } else { throw new ArgumentException("Unsupported collider type", "collider"); } } /// Returns informations for the first hit of a sphere cast /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Receives informations about the collider that was hit /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// True if the shape cast hit another collider private bool sphereCast( Vector3 position, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { return Physics.SphereCast( position + this.sphereCollider.center, this.sphereCollider.radius, direction, out hitInfo, maxDistance, layerMask, queryTriggerInteraction ); } /// Returns informations for the first hit of a capsule cast /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Receives informations about the collider that was hit /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// True if the shape cast hit another collider private bool capsuleCast( Vector3 position, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { Vector3 point1 = position + this.capsuleCollider.center; Vector3 point2 = point1; float halfHeight = this.capsuleCollider.height / 2.0f; switch(this.capsuleCollider.direction) { case 0: { point1.x -= halfHeight; point2.x += halfHeight; break; } case 1: { point1.y -= halfHeight; point2.y += halfHeight; break; } case 2: { point1.z -= halfHeight; point2.z += halfHeight; break; } } return Physics.CapsuleCast( point1, point2, this.capsuleCollider.radius, direction, out hitInfo, maxDistance, layerMask, queryTriggerInteraction ); } /// Returns informations for the first hit of a box cast /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Receives informations about the collider that was hit /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// True if the shape cast hit another collider private bool boxCast( Vector3 position, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { Vector3 extents = this.boxCollider.size / 2.0f; return Physics.BoxCast( position + this.boxCollider.center, extents, direction, out hitInfo, this.boxCollider.transform.rotation, maxDistance, layerMask, queryTriggerInteraction ); } /// Returns informations for all colliders hit by a sphere cast /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// A list of all colliders hit by the shape cast private RaycastHit[] sphereCastAll( Vector3 position, Vector3 direction, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { return Physics.SphereCastAll( position + this.sphereCollider.center, this.sphereCollider.radius, direction, maxDistance, layerMask, queryTriggerInteraction ); } /// Returns informations for all colliders hit by a sphere cast /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// A list of all colliders hit by the shape cast private RaycastHit[] capsuleCastAll( Vector3 position, Vector3 direction, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { Vector3 point1 = position + this.capsuleCollider.center; Vector3 point2 = point1; float halfHeight = this.capsuleCollider.height / 2.0f; switch(this.capsuleCollider.direction) { case 0: { point1.x -= halfHeight; point2.x += halfHeight; break; } case 1: { point1.y -= halfHeight; point2.y += halfHeight; break; } case 2: { point1.z -= halfHeight; point2.z += halfHeight; break; } } return Physics.CapsuleCastAll( point1, point2, this.capsuleCollider.radius, direction, maxDistance, layerMask, queryTriggerInteraction ); } /// Returns informations for all colliders hit by a sphere cast /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// A list of all colliders hit by the shape cast private RaycastHit[] boxCastAll( Vector3 position, Vector3 direction, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { Vector3 extents = this.boxCollider.size / 2.0f; return Physics.BoxCastAll( position + this.boxCollider.center, extents, direction, this.boxCollider.transform.rotation, maxDistance, layerMask, queryTriggerInteraction ); } /// Write informations for all colliders hit by a sphere cast into an array /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Array that will receive the colliders that were hit /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// The number of colliders that were hit by the shape cast private int sphereCastNonAlloc( Vector3 position, Vector3 direction, RaycastHit[] results, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { return Physics.SphereCastNonAlloc( position + this.sphereCollider.center, this.sphereCollider.radius, direction, results, maxDistance, layerMask, queryTriggerInteraction ); } /// Write informations for all colliders hit by a capsule cast into an array /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Array that will receive the colliders that were hit /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// The number of colliders that were hit by the shape cast private int capsuleCastNonAlloc( Vector3 position, Vector3 direction, RaycastHit[] results, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { Vector3 point1 = position + this.capsuleCollider.center; Vector3 point2 = point1; float halfHeight = this.capsuleCollider.height / 2.0f; switch(this.capsuleCollider.direction) { case 0: { point1.x -= halfHeight; point2.x += halfHeight; break; } case 1: { point1.y -= halfHeight; point2.y += halfHeight; break; } case 2: { point1.z -= halfHeight; point2.z += halfHeight; break; } } return Physics.CapsuleCastNonAlloc( point1, point2, this.capsuleCollider.radius, direction, results, maxDistance, layerMask, queryTriggerInteraction ); } /// Write informations for all colliders hit by a box cast into an array /// Position of the game object carrying the collider /// Direction into which the shape will be cast /// Array that will receive the colliders that were hit /// Maximum distance to consider for the shape cast /// Layers on which to check for collisions /// How to deal with trigger colliders /// The number of colliders that were hit by the shape cast private int boxCastNonAlloc( Vector3 position, Vector3 direction, RaycastHit[] results, float maxDistance, int layerMask, QueryTriggerInteraction queryTriggerInteraction ) { Vector3 extents = this.boxCollider.size / 2.0f; return Physics.BoxCastNonAlloc( position + this.boxCollider.center, extents, direction, results, this.boxCollider.transform.rotation, maxDistance, layerMask, queryTriggerInteraction ); } /// Delegates for single hit shape casting methods private readonly ShapeCastDelegate[/*2*/] shapeCastDelegates; /// Delegates for multi hit shape casting methods private readonly ShapeCastAllDelegate[/*2*/] shapeCastAllDelegates; /// Delegates for multi hit allocation-free shape casting methods private readonly ShapeCastNonAllocDelegate[/*2*/] shapeCastNonAllocDelegates; /// Shape of the collider that is cast by this caster private ColliderShape colliderShape; /// Sphere collider that will be cast if this is a sphere caster private SphereCollider sphereCollider; /// Sphere collider that will be cast if this is a capsule caster private CapsuleCollider capsuleCollider; /// Sphere collider that will be cast if this is a box caster private BoxCollider boxCollider; } } // namespace Framework.Support