/// <summary> /// Pulls an object from the pool. /// </summary> /// <returns></returns> public static BodyShapeHit Allocate(BodyShapeHit rInstance) { if (rInstance == null) { return(sPool.Allocate()); } else { BodyShapeHit lInstance = sPool.Allocate(); lInstance.Shape = rInstance.Shape; lInstance.StartPosition = rInstance.StartPosition; lInstance.EndPosition = rInstance.EndPosition; lInstance.HitCollider = rInstance.HitCollider; lInstance.HitOrigin = rInstance.HitOrigin; lInstance.HitPoint = rInstance.HitPoint; lInstance.HitNormal = rInstance.HitNormal; lInstance.HitDistance = rInstance.HitDistance; lInstance.HitRootDistance = rInstance.HitRootDistance; lInstance.HitPenetration = rInstance.HitPenetration; lInstance.IsPlatformHit = rInstance.IsPlatformHit; lInstance.Hit = rInstance.Hit; return(lInstance); } }
/// <summary> /// Checks if the shape currently overlaps any colliders /// </summary> /// <param name="rPositionDelta">Movement to add to the current position</param> /// <param name="rLayerMask">Layer mask for determing what we'll collide with</param> /// <returns>Boolean that says if a hit occured or not</returns> public override List <BodyShapeHit> CollisionOverlap(Vector3 rPositionDelta, Quaternion rRotationDelta, int rLayerMask) { List <BodyShapeHit> lHits = new List <BodyShapeHit>(); Vector3 lBodyShapePos1 = rPositionDelta + (_Transform == null ? _Parent.position + ((_Parent.rotation * rRotationDelta) * _Offset) : _Transform.position + ((_Transform.rotation * rRotationDelta) * _Offset)); Vector3 lBodyShapePos2 = rPositionDelta + (_EndTransform == null ? _Parent.position + ((_Parent.rotation * rRotationDelta) * _EndOffset) : _EndTransform.position + ((_EndTransform.rotation * rRotationDelta) * _EndOffset)); Vector3 lPosition = lBodyShapePos1 + ((lBodyShapePos2 - lBodyShapePos1) / 2f); float lOverlapRadius = (Vector3.Distance(lBodyShapePos1, lBodyShapePos2) / 2f) + _Radius; GeometryExt.Ignore = _Parent; Collider[] lColliders = null; int lColliderHits = RaycastExt.SafeOverlapSphere(lPosition, lOverlapRadius, out lColliders, rLayerMask, _Parent); for (int i = 0; i < lColliderHits; i++) { Transform lCurrentTransform = lColliders[i].transform; if (lCurrentTransform == _Transform) { continue; } if (lCurrentTransform == _EndTransform) { continue; } if (_CharacterController != null && _CharacterController.IsIgnoringCollision(lColliders[i])) { continue; } // Once we get here, we have a valid collider Vector3 lLinePoint = Vector3.zero; Vector3 lColliderPoint = Vector3.zero; GeometryExt.ClosestPoints(lBodyShapePos1, lBodyShapePos2, _Radius, lColliders[i], ref lLinePoint, ref lColliderPoint, rLayerMask); if (lLinePoint != Vector3Ext.Null && lColliderPoint != Vector3Ext.Null) { float lDistance = Vector3.Distance(lLinePoint, lColliderPoint); if (lDistance < _Radius + 0.001f) { BodyShapeHit lHit = BodyShapeHit.Allocate(); lHit.StartPosition = lBodyShapePos1; lHit.EndPosition = lBodyShapePos2; lHit.HitCollider = lColliders[i]; lHit.HitOrigin = lLinePoint; lHit.HitPoint = lColliderPoint; lHit.HitDistance = lDistance - _Radius - 0.001f; lHits.Add(lHit); } } } GeometryExt.Ignore = null; GeometryExt.IgnoreArray = null; return(lHits); }
/// <summary> /// Pulls an object from the pool. /// </summary> /// <returns></returns> public static BodyShapeHit Allocate() { // Grab the next available object BodyShapeHit lInstance = sPool.Allocate(); // Return it return(lInstance); }
/// <summary> /// Checks if the shape currently overlaps any colliders /// </summary> /// <param name="rPositionDelta">Movement to add to the current position</param> /// <param name="rLayerMask">Layer mask for determing what we'll collide with</param> /// <returns>Boolean that says if a hit occured or not</returns> public override List <BodyShapeHit> CollisionOverlap(Vector3 rPositionDelta, Quaternion rRotationDelta, int rLayerMask) { List <BodyShapeHit> lHits = new List <BodyShapeHit>(); Vector3 lBodyShapePos1 = rPositionDelta + (_Transform == null ? _Parent.position + ((_Parent.rotation * rRotationDelta) * _Offset) : _Transform.position + ((_Transform.rotation * rRotationDelta) * _Offset)); Collider[] lColliders = null; int lColliderHits = RaycastExt.SafeOverlapSphere(lBodyShapePos1, _Radius, out lColliders, -1, _Parent); for (int i = 0; i < lColliderHits; i++) { if (lColliders[i].transform == _Transform) { continue; } if (_CharacterController != null && _CharacterController.IsIgnoringCollision(lColliders[i])) { continue; } // Once we get here, we have a valid collider Vector3 lLinePoint = lBodyShapePos1; Vector3 lColliderPoint = GeometryExt.ClosestPoint(lBodyShapePos1, _Radius, lColliders[i]); float lDistance = Vector3.Distance(lLinePoint, lColliderPoint); if (lDistance < _Radius + 0.001f) { BodyShapeHit lHit = BodyShapeHit.Allocate(); lHit.StartPosition = lBodyShapePos1; lHit.HitCollider = lColliders[i]; lHit.HitOrigin = lLinePoint; lHit.HitPoint = lColliderPoint; lHit.HitDistance = lDistance - _Radius - 0.001f; lHits.Add(lHit); } } return(lHits); }
/// <summary> /// Returns an element back to the pool. /// </summary> /// <param name="rEdge"></param> public static void Release(BodyShapeHit rInstance) { if (rInstance == null) { return; } // Clear the object rInstance.Shape = null; rInstance.StartPosition = Vector3.zero; rInstance.EndPosition = Vector3.zero; rInstance.HitCollider = null; rInstance.HitOrigin = Vector3.zero; rInstance.HitPoint = Vector3.zero; rInstance.HitNormal = Vector3.zero; rInstance.HitDistance = 0f; rInstance.HitPenetration = false; rInstance.Hit = RaycastExt.EmptyHitInfo; // Allow it to be reused sPool.Release(rInstance); }
/// <summary> /// Casts out a shape to see if a collision will occur. The resulting array /// should NOT be persisted. It is re-used by this function over and over to /// reduce memory allocation. /// </summary> /// <param name="rPositionDelta">Movement to add to the current position</param> /// <param name="rDirection">Direction of the cast</param> /// <param name="rDistance">Distance of the case</param> /// <param name="rLayerMask">Layer mask for determing what we'll collide with</param> /// <returns>Returns an array of BodyShapeHit values representing all the hits that take place</returns> public override BodyShapeHit[] CollisionCastAll(Vector3 rPositionDelta, Vector3 rDirection, float rDistance, int rLayerMask) { Vector3 lBodyShapePos1 = rPositionDelta + (_Transform == null ? _Parent.position + (_Parent.rotation * _Offset) : _Transform.position + (_Transform.rotation * _Offset)); // Clear any existing body shape hits. They are released by the calloer for (int i = 0; i < mBodyShapeHitArray.Length; i++) { mBodyShapeHitArray[i] = null; } // Use the non-allocating version if we can int lHitCount = 0; #if UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 mRaycastHitArray = UnityEngine.Physics.SphereCastAll(lBodyShapePos1, _Radius, rDirection, rDistance + EPSILON, rLayerMask); if (mRaycastHitArray != null) { lHitCount = mRaycastHitArray.Length; mBodyShapeHitArray = new BodyShapeHit[lHitCount]; } #else lHitCount = UnityEngine.Physics.SphereCastNonAlloc(lBodyShapePos1, _Radius, rDirection, mRaycastHitArray, rDistance + EPSILON, rLayerMask, QueryTriggerInteraction.Ignore); #endif int lBodyShapeHitsIndex = 0; for (int i = 0; i < lHitCount; i++) { if (mRaycastHitArray[i].collider.isTrigger) { continue; } if (_CharacterController != null && _CharacterController.IsIgnoringCollision(mRaycastHitArray[i].collider)) { continue; } Transform lCurrentTransform = mRaycastHitArray[i].collider.transform; if (lCurrentTransform == _Transform) { continue; } // Ensure we're not colliding with a transform in our chain bool lIsValidHit = true; while (lCurrentTransform != null) { if (lCurrentTransform == _Parent) { lIsValidHit = false; break; } lCurrentTransform = lCurrentTransform.parent; } if (!lIsValidHit) { continue; } // Once we get here, we have a valid collider BodyShapeHit lBodyShapeHit = BodyShapeHit.Allocate(); lBodyShapeHit.StartPosition = lBodyShapePos1; lBodyShapeHit.Shape = this; lBodyShapeHit.Hit = mRaycastHitArray[i]; lBodyShapeHit.HitOrigin = lBodyShapePos1; lBodyShapeHit.HitCollider = mRaycastHitArray[i].collider; lBodyShapeHit.HitPoint = mRaycastHitArray[i].point; lBodyShapeHit.HitNormal = mRaycastHitArray[i].normal; // This distance is the distance between the surfaces and not the start! lBodyShapeHit.HitDistance = mRaycastHitArray[i].distance; // With the sphere cast all, we can recieve hits for colliders that // start by intruding on the sphere. In this case, the distance is "0". So, // we'll find the true distance ourselves. if (mRaycastHitArray[i].distance == 0f) { Vector3 lColliderPoint = Vector3.zero; //if (lBodyShapeHit.HitCollider is TerrainCollider) //{ // lColliderPoint = GeometryExt.ClosestPoint(lBodyShapePos1, rDirection * rDistance, _Radius, (TerrainCollider)lBodyShapeHit.HitCollider, rLayerMask); //} //else //{ lColliderPoint = GeometryExt.ClosestPoint(lBodyShapePos1, _Radius, lBodyShapeHit.HitCollider, rLayerMask); //} // If we don't have a valid point, we will skip if (lColliderPoint == Vector3.zero) { BodyShapeHit.Release(lBodyShapeHit); continue; } // If the hit is further than our radius, we can skip Vector3 lHitVector = lColliderPoint - lBodyShapePos1; //if (lHitVector.magnitude > _Radius + EPSILON) //{ // BodyShapeHit.Release(lBodyShapeHit); // continue; //} // Setup the remaining info lBodyShapeHit.HitOrigin = lBodyShapePos1; lBodyShapeHit.HitPoint = lColliderPoint; // We want distance between the surfaces. We have the start point and // surface collider point. So, we remove our radius to get to the surface. lBodyShapeHit.HitDistance = lHitVector.magnitude - _Radius; lBodyShapeHit.HitPenetration = (lBodyShapeHit.HitDistance < 0f); // Shoot a ray for the normal RaycastHit lRaycastHitInfo; if (RaycastExt.SafeRaycast(lBodyShapePos1, lHitVector.normalized, out lRaycastHitInfo, Mathf.Max(lBodyShapeHit.HitDistance + _Radius, _Radius + 0.01f))) { lBodyShapeHit.HitNormal = lRaycastHitInfo.normal; } // If the ray is so close that we can't get a result we can end up here else if (lBodyShapeHit.HitDistance < EPSILON) { lBodyShapeHit.HitNormal = (lBodyShapePos1 - lColliderPoint).normalized; } } // Add the collision info if (lBodyShapeHit != null) { // We can't really trust the hit normal we have since it probably came from an edge. So, we'll // shoot a ray along our movement path. This will give us a better angle to look at. However, if we're // falling (probably from gravity), we don't want to replace the edge value. if (rDirection != Vector3.down) { RaycastHit lRaycastHitInfo; if (RaycastExt.SafeRaycast(lBodyShapeHit.HitPoint - (rDirection * rDistance), rDirection, out lRaycastHitInfo, rDistance + _Radius, -1, _Parent)) { lBodyShapeHit.HitNormal = lRaycastHitInfo.normal; } } // Store the distance between the hit point and our character's root lBodyShapeHit.HitRootDistance = _Parent.InverseTransformPoint(lBodyShapeHit.HitPoint).y; // Add the valid hit to our array mBodyShapeHitArray[lBodyShapeHitsIndex] = lBodyShapeHit; lBodyShapeHitsIndex++; } } // Return this array. The array should not be kept return(mBodyShapeHitArray); }
/// <summary> /// Casts out a shape to see if a collision will occur. /// </summary> /// <param name="rPositionDelta">Movement to add to the current position</param> /// <param name="rDirection">Direction of the cast</param> /// <param name="rDistance">Distance of the case</param> /// <param name="rLayerMask">Layer mask for determing what we'll collide with</param> /// <returns>Returns an array of BodyShapeHit values representing all the hits that take place</returns> public override BodyShapeHit[] CollisionCastAll(Vector3 rPositionDelta, Vector3 rDirection, float rDistance, int rLayerMask) { Vector3 lBodyShapePos1 = rPositionDelta + (_Transform == null ? _Parent.position + (_Parent.rotation * _Offset) : _Transform.position + (_Transform.rotation * _Offset)); // Add the additional epsilon to the distance so we can get where we really want to test. RaycastHit[] lRaycastHits = UnityEngine.Physics.SphereCastAll(lBodyShapePos1, _Radius, rDirection, rDistance + EPSILON, rLayerMask); int lBodyShapeHitsIndex = 0; BodyShapeHit[] lBodyShapeHits = new BodyShapeHit[lRaycastHits.Length]; for (int i = 0; i < lRaycastHits.Length; i++) { Transform lCurrentTransform = lRaycastHits[i].collider.transform; if (lCurrentTransform == _Transform) { continue; } // Ensure we're not colliding with a transform in our chain bool lIsValidHit = true; while (lCurrentTransform != null) { if (lCurrentTransform == _Parent) { lIsValidHit = false; break; } lCurrentTransform = lCurrentTransform.parent; } if (!lIsValidHit) { continue; } // Once we get here, we have a valid collider BodyShapeHit lBodyShapeHit = BodyShapeHit.Allocate(); lBodyShapeHit.StartPosition = lBodyShapePos1; lBodyShapeHit.Shape = this; lBodyShapeHit.Hit = lRaycastHits[i]; lBodyShapeHit.HitOrigin = lBodyShapePos1; lBodyShapeHit.HitCollider = lRaycastHits[i].collider; lBodyShapeHit.HitPoint = lRaycastHits[i].point; lBodyShapeHit.HitNormal = lRaycastHits[i].normal; // This distance is the distance between the surfaces and not the start! lBodyShapeHit.HitDistance = lRaycastHits[i].distance; // With the sphere cast all, we can recieve hits for colliders that // start by intruding on the sphere. In this case, the distance is "0". So, // we'll find the true distance ourselves. if (lRaycastHits[i].distance == 0f) { Vector3 lColliderPoint = Vector3.zero; if (lBodyShapeHit.HitCollider is TerrainCollider) { lColliderPoint = GeometryExt.ClosestPoint(lBodyShapePos1, rDirection * rDistance, _Radius, (TerrainCollider)lBodyShapeHit.HitCollider); } else { lColliderPoint = GeometryExt.ClosestPoint(lBodyShapePos1, _Radius, lBodyShapeHit.HitCollider); } // If we don't have a valid point, we will skip if (lColliderPoint == Vector3.zero) { BodyShapeHit.Release(lBodyShapeHit); continue; } // If the hit is further than our radius, we can skip Vector3 lHitVector = lColliderPoint - lBodyShapePos1; //if (lHitVector.magnitude > _Radius + EPSILON) //{ // BodyShapeHit.Release(lBodyShapeHit); // continue; //} // Setup the remaining info lBodyShapeHit.HitOrigin = lBodyShapePos1; lBodyShapeHit.HitPoint = lColliderPoint; // We want distance between the surfaces. We have the start point and // surface collider point. So, we remove our radius to get to the surface. lBodyShapeHit.HitDistance = lHitVector.magnitude - _Radius; lBodyShapeHit.HitPenetration = (lBodyShapeHit.HitDistance < 0f); // Shoot a ray for the normal RaycastHit lRaycastHitInfo; if (RaycastExt.SafeRaycast(lBodyShapePos1, lHitVector.normalized, Mathf.Max(lBodyShapeHit.HitDistance + _Radius, _Radius + 0.01f), out lRaycastHitInfo)) { lBodyShapeHit.HitNormal = lRaycastHitInfo.normal; } // If the ray is so close that we can't get a result we can end up here else if (lBodyShapeHit.HitDistance < EPSILON) { lBodyShapeHit.HitNormal = (lBodyShapePos1 - lColliderPoint).normalized; } } // Add the collision info if (lBodyShapeHit != null) { // We can't really trust the hit normal we have since it probably came from an edge. So, we'll // shoot a ray along our movement path. This will give us a better angle to look at. However, if we're // falling (probably from gravity), we don't want to replace the edge value. if (rDirection != Vector3.down) { RaycastHit lRaycastHitInfo; if (RaycastExt.SafeRaycast(lBodyShapeHit.HitPoint - (rDirection * rDistance), rDirection, rDistance + _Radius, _Parent, out lRaycastHitInfo)) { lBodyShapeHit.HitNormal = lRaycastHitInfo.normal; } } // Store the distance between the hit point and our character's root lBodyShapeHit.HitRootDistance = _Parent.InverseTransformPoint(lBodyShapeHit.HitPoint).y; lBodyShapeHits[lBodyShapeHitsIndex]= lBodyShapeHit; lBodyShapeHitsIndex++; } } return lBodyShapeHits; }
/// <summary> /// Returns an element back to the pool. /// </summary> /// <param name="rEdge"></param> public static void Release(BodyShapeHit rInstance) { // Clear the object rInstance.Shape = null; rInstance.StartPosition = Vector3.zero; rInstance.EndPosition = Vector3.zero; rInstance.HitCollider = null; rInstance.HitOrigin = Vector3.zero; rInstance.HitPoint = Vector3.zero; rInstance.HitNormal = Vector3.zero; rInstance.HitDistance = 0f; rInstance.HitPenetration = false; rInstance.Hit = RaycastExt.EmptyHitInfo; // Allow it to be reused sPool.Release(rInstance); }