/// <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)); Vector3 lBodyShapePos2 = rPositionDelta + (_EndTransform == null ? _Parent.position + (_Parent.rotation * _EndOffset) : _EndTransform.position + (_EndTransform.rotation * _EndOffset)); // 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.CapsuleCastAll(lBodyShapePos1, lBodyShapePos2, _Radius, rDirection, rDistance + EPSILON, rLayerMask); if (mRaycastHitArray != null) { lHitCount = mRaycastHitArray.Length; mBodyShapeHitArray = new BodyShapeHit[lHitCount]; } #else lHitCount = UnityEngine.Physics.CapsuleCastNonAlloc(lBodyShapePos1, lBodyShapePos2, _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; } Transform lCurrentTransform = mRaycastHitArray[i].collider.transform; if (lCurrentTransform == _Transform) { continue; } if (lCurrentTransform == _EndTransform) { continue; } if (_CharacterController != null && _CharacterController.IsIgnoringCollision(mRaycastHitArray[i].collider)) { 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.EndPosition = lBodyShapePos2; lBodyShapeHit.Shape = this; lBodyShapeHit.Hit = mRaycastHitArray[i]; lBodyShapeHit.HitCollider = mRaycastHitArray[i].collider; lBodyShapeHit.HitPoint = mRaycastHitArray[i].point; lBodyShapeHit.HitNormal = mRaycastHitArray[i].normal; lBodyShapeHit.HitDistance = mRaycastHitArray[i].distance; // With the capsule cast all, we can recieve hits for colliders that // start by intruding on the capsule. In this case, the distance is "0". So, // we'll find the true distance ourselves. if (mRaycastHitArray[i].distance == 0f) { Vector3 lLinePoint = Vector3.zero; Vector3 lColliderPoint = Vector3.zero; if (lBodyShapeHit.HitCollider is TerrainCollider) { GeometryExt.ClosestPoints(lBodyShapePos1, lBodyShapePos2, rDirection * rDistance, _Radius, (TerrainCollider)lBodyShapeHit.HitCollider, ref lLinePoint, ref lColliderPoint); } else { GeometryExt.ClosestPoints(lBodyShapePos1, lBodyShapePos2, _Radius, lBodyShapeHit.HitCollider, ref lLinePoint, ref lColliderPoint); } // 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 - lLinePoint; //if (lHitVector.magnitude > _Radius + EPSILON) //{ // BodyShapeHit.Release(lBodyShapeHit); // continue; //} // Setup the remaining info lBodyShapeHit.HitOrigin = lLinePoint; lBodyShapeHit.HitPoint = lColliderPoint; // We have the distance between the origin and the surface. Now // we need to find the distances between the surfaces. lBodyShapeHit.HitDistance = lHitVector.magnitude - _Radius; lBodyShapeHit.HitPenetration = (lBodyShapeHit.HitDistance < 0f); RaycastHit lRaycastHitInfo; if (RaycastExt.SafeRaycast(lLinePoint, 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 = (lLinePoint - lColliderPoint).normalized; } } else { lBodyShapeHit.CalculateHitOrigin(); } // 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; } } // TRT 11/20/15 - Given some colision oddities, I think having the hit normal always be the value // between the origin and point is probably right. Will need to test to ensure. lBodyShapeHit.HitNormal = (lBodyShapeHit.HitOrigin - lBodyShapeHit.HitPoint).normalized; // 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); }