示例#1
0
        /// <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);
        }