// MIT License // // Copyright (c) 2017 Justin Larrabee // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. using UnityEngine; using System; using System.Collections; using System.Collections.Generic; public static class PhysicsExtensions { // // Box // public static bool BoxCast(BoxCollider box, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center, halfExtents; Quaternion orientation; box.ToWorldSpaceBox( out center, out halfExtents, out orientation ); return Physics.BoxCast( center, halfExtents, direction, orientation, maxDistance, layerMask, queryTriggerInteraction ); } public static bool BoxCast(BoxCollider box, Vector3 direction, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center, halfExtents; Quaternion orientation; box.ToWorldSpaceBox( out center, out halfExtents, out orientation ); return Physics.BoxCast( center, halfExtents, direction, out hitInfo, orientation, maxDistance, layerMask, queryTriggerInteraction ); } public static RaycastHit[] BoxCastAll(BoxCollider box, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center, halfExtents; Quaternion orientation; box.ToWorldSpaceBox( out center, out halfExtents, out orientation ); return Physics.BoxCastAll( center, halfExtents, direction, orientation, maxDistance, layerMask, queryTriggerInteraction ); } public static int BoxCastNonAlloc(BoxCollider box, Vector3 direction, RaycastHit[] results, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center, halfExtents; Quaternion orientation; box.ToWorldSpaceBox( out center, out halfExtents, out orientation ); return Physics.BoxCastNonAlloc( center, halfExtents, direction, results, orientation, maxDistance, layerMask, queryTriggerInteraction ); } public static bool CheckBox(BoxCollider box, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center, halfExtents; Quaternion orientation; box.ToWorldSpaceBox( out center, out halfExtents, out orientation ); return Physics.CheckBox( center, halfExtents, orientation, layerMask, queryTriggerInteraction ); } public static Collider[] OverlapBox(BoxCollider box, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center, halfExtents; Quaternion orientation; box.ToWorldSpaceBox( out center, out halfExtents, out orientation ); return Physics.OverlapBox( center, halfExtents, orientation, layerMask, queryTriggerInteraction ); } public static int OverlapBoxNonAlloc(BoxCollider box, Collider[] results, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center, halfExtents; Quaternion orientation; box.ToWorldSpaceBox( out center, out halfExtents, out orientation ); return Physics.OverlapBoxNonAlloc( center, halfExtents, results, orientation, layerMask, queryTriggerInteraction ); } public static void ToWorldSpaceBox(this BoxCollider box, out Vector3 center, out Vector3 halfExtents, out Quaternion orientation) { orientation = box.transform.rotation; center = box.transform.TransformPoint( box.center ); var lossyScale = box.transform.lossyScale; var scale = AbsVec3( lossyScale ); halfExtents = Vector3.Scale( scale, box.size ) * 0.5f; } // // Sphere // public static bool SphereCast(SphereCollider sphere, Vector3 direction, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center; float radius; sphere.ToWorldSpaceSphere( out center, out radius ); return Physics.SphereCast( center, radius, direction, out hitInfo, maxDistance, layerMask, queryTriggerInteraction ); } public static RaycastHit[] SphereCastAll(SphereCollider sphere, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center; float radius; sphere.ToWorldSpaceSphere( out center, out radius ); return Physics.SphereCastAll( center, radius, direction, maxDistance, layerMask, queryTriggerInteraction ); } public static int SphereCastNonAlloc(SphereCollider sphere, Vector3 direction, RaycastHit[] results, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center; float radius; sphere.ToWorldSpaceSphere( out center, out radius ); return Physics.SphereCastNonAlloc( center, radius, direction, results, maxDistance, layerMask, queryTriggerInteraction ); } public static bool CheckSphere(SphereCollider sphere, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center; float radius; sphere.ToWorldSpaceSphere( out center, out radius ); return Physics.CheckSphere( center, radius, layerMask, queryTriggerInteraction ); } public static Collider[] OverlapSphere(SphereCollider sphere, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center; float radius; sphere.ToWorldSpaceSphere( out center, out radius ); return Physics.OverlapSphere( center, radius, layerMask, queryTriggerInteraction ); } public static int OverlapSphereNonAlloc(SphereCollider sphere, Collider[] results, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 center; float radius; sphere.ToWorldSpaceSphere( out center, out radius ); return Physics.OverlapSphereNonAlloc( center, radius, results, layerMask, queryTriggerInteraction ); } public static void ToWorldSpaceSphere(this SphereCollider sphere, out Vector3 center, out float radius) { center = sphere.transform.TransformPoint( sphere.center ); radius = sphere.radius * MaxVec3( AbsVec3( sphere.transform.lossyScale ) ); } // // Capsule // public static bool CapsuleCast(CapsuleCollider capsule, Vector3 direction, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 point0, point1; float radius; capsule.ToWorldSpaceCapsule( out point0, out point1, out radius ); return Physics.CapsuleCast( point0, point1, radius, direction, out hitInfo, maxDistance, layerMask, queryTriggerInteraction ); } public static RaycastHit[] CapsuleCastAll(CapsuleCollider capsule, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 point0, point1; float radius; capsule.ToWorldSpaceCapsule( out point0, out point1, out radius ); return Physics.CapsuleCastAll( point0, point1, radius, direction, maxDistance, layerMask, queryTriggerInteraction ); } public static int CapsuleCastNonAlloc(CapsuleCollider capsule, Vector3 direction, RaycastHit[] results, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 point0, point1; float radius; capsule.ToWorldSpaceCapsule( out point0, out point1, out radius ); return Physics.CapsuleCastNonAlloc( point0, point1, radius, direction, results, maxDistance, layerMask, queryTriggerInteraction ); } public static bool CheckCapsule(CapsuleCollider capsule, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 point0, point1; float radius; capsule.ToWorldSpaceCapsule( out point0, out point1, out radius ); return Physics.CheckCapsule( point0, point1, radius, layerMask, queryTriggerInteraction ); } public static Collider[] OverlapCapsule(CapsuleCollider capsule, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 point0, point1; float radius; capsule.ToWorldSpaceCapsule( out point0, out point1, out radius ); return Physics.OverlapCapsule( point0, point1, radius, layerMask, queryTriggerInteraction ); } public static int OverlapCapsuleNonAlloc(CapsuleCollider capsule, Collider[] results, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal) { Vector3 point0, point1; float radius; capsule.ToWorldSpaceCapsule( out point0, out point1, out radius ); return Physics.OverlapCapsuleNonAlloc( point0, point1, radius, results, layerMask, queryTriggerInteraction ); } public static void ToWorldSpaceCapsule(this CapsuleCollider capsule, out Vector3 point0, out Vector3 point1, out float radius) { var center = capsule.transform.TransformPoint( capsule.center ); radius = 0f; float height = 0f; Vector3 lossyScale = AbsVec3( capsule.transform.lossyScale ); Vector3 dir = Vector3.zero; switch (capsule.direction) { case 0: // x radius = Mathf.Max( lossyScale.y, lossyScale.z ) * capsule.radius; height = lossyScale.x * capsule.height; dir = capsule.transform.TransformDirection( Vector3.right ); break; case 1: // y radius = Mathf.Max( lossyScale.x, lossyScale.z ) * capsule.radius; height = lossyScale.y * capsule.height; dir = capsule.transform.TransformDirection( Vector3.up ); break; case 2: // z radius = Mathf.Max( lossyScale.x, lossyScale.y ) * capsule.radius; height = lossyScale.z * capsule.height; dir = capsule.transform.TransformDirection( Vector3.forward ); break; } if (height < radius * 2f) { dir = Vector3.zero; } point0 = center + dir * ( height * 0.5f - radius ); point1 = center - dir * ( height * 0.5f - radius ); } // // Util // public static void SortClosestToFurthest(RaycastHit[] hits, int hitCount = -1) { if (hitCount == 0) { return; } if (hitCount < 0) { hitCount = hits.Length; } Array.Sort( hits, 0, hitCount, ascendDistance ); } // // Private // private class AscendingDistanceComparer : IComparer { public int Compare(RaycastHit h1, RaycastHit h2) { return h1.distance < h2.distance ? -1 : ( h1.distance > h2.distance ? 1 : 0 ); } } private static AscendingDistanceComparer ascendDistance = new AscendingDistanceComparer(); private static Vector3 AbsVec3(Vector3 v) { return new Vector3( Mathf.Abs( v.x ), Mathf.Abs( v.y ), Mathf.Abs( v.z ) ); } private static float MaxVec3(Vector3 v) { return Mathf.Max( v.x, Mathf.Max( v.y, v.z ) ); } }