/// <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); }