Exemplo n.º 1
0
        /// <summary>
        /// Compute 'ground' hit info casting downwards the character's volume.
        /// Returns true when the intersects any 'ground' collider, otherwise false.
        /// </summary>
        /// <param name="hitInfo">If found any 'ground', this will contain more information about it</param>
        /// <param name="scanDistance">The maximum scan distnce (cast distance)</param>

        public bool ComputeGroundHit(out GroundHit hitInfo, float scanDistance = Mathf.Infinity)
        {
            var p = transform.position;
            var q = transform.rotation;

            return(ComputeGroundHit(p, q, out hitInfo, scanDistance));
        }
Exemplo n.º 2
0
        public override bool FindGround(Vector3 direction, out RaycastHit hitInfo, float distance = Mathf.Infinity,
                                        float backstepDistance = kBackstepDistance)
        {
            var radius = capsuleCollider.radius;

            var height = Mathf.Max(0.0f, capsuleCollider.height * 0.5f - radius);
            var center = capsuleCollider.center - Vector3.up * height;

            var origin = ObjAdapter.transform.TransformPoint(center);

            if (!SphereCast(origin, radius, direction, out hitInfo, distance, backstepDistance) ||
                Vector3.Angle(hitInfo.normal, Vector3.up) >= 89.0f)
            {
                return(false);
            }

            var p = ObjAdapter.transform.position - ObjAdapter.transform.up * hitInfo.distance;
            var q = ObjAdapter.transform.rotation;

            var groundHitInfo = new GroundHit(hitInfo);

            DetectLedgeAndSteps(p, q, ref groundHitInfo, castDistance, hitInfo.point, hitInfo.normal);

            groundHitInfo.isOnGround    = true;
            groundHitInfo.isValidGround = !groundHitInfo.isOnLedgeEmptySide &&
                                          Vector3.Angle(groundHitInfo.surfaceNormal, Vector3.up) < groundLimit;

            return(groundHitInfo.isOnGround && groundHitInfo.isValidGround);
        }
Exemplo n.º 3
0
        public override bool ComputeGroundHit(Vector3 position, Quaternion rotation, ControllerHitInfo controllerHitInfo,
                                              ref GroundHit groundHitInfo, float distance = Mathf.Infinity)
        {
            // Downward cast from campsule's bottom sphere (filter any 'wall')

            RaycastHit hitInfo;

            if (BottomSphereCast(position, rotation, out hitInfo, distance) &&
                Vector3.Angle(hitInfo.normal, rotation * Vector3.up) < 89.0f)
            {
                // Update ground hit info

                groundHitInfo.SetFrom(hitInfo);

                // Check if standing on a ledge or step

                DetectLedgeAndSteps(position, rotation, ref groundHitInfo, distance, hitInfo.point, hitInfo.normal);

                // Inform we found 'ground' and if is valid

                groundHitInfo.isOnGround    = true;
                groundHitInfo.isValidGround = !groundHitInfo.isOnLedgeEmptySide &&
                                              Vector3.Angle(groundHitInfo.surfaceNormal, Vector3.up) < groundLimit;

                return(true);
            }

            // If initial sphere cast fails, or found a 'wall',
            // fallback to a raycast from character's bottom position

            if (!BottomRaycast(position, rotation, out hitInfo, distance))
            {
                if (!controllerHitInfo.Valid)
                {
                    return(false);
                }

                groundHitInfo.SetFrom(controllerHitInfo);
                groundHitInfo.surfaceNormal = controllerHitInfo.HitNormal;
                groundHitInfo.isOnGround    = true;
                groundHitInfo.isValidGround = Vector3.Angle(groundHitInfo.surfaceNormal, Vector3.up) < groundLimit;
                //DebugDraw.DebugArrow(controllerHitInfo.HitPoint, controllerHitInfo.HitNormal * 2, Color.magenta, 0, false);
            }
            else
            {
                // If found 'ground', update ground info and return

                groundHitInfo.SetFrom(hitInfo);
                groundHitInfo.surfaceNormal = hitInfo.normal;
                groundHitInfo.isOnGround    = true;
                groundHitInfo.isValidGround = Vector3.Angle(groundHitInfo.surfaceNormal, Vector3.up) < groundLimit;
            }

            return(true);
        }
        /// <summary>
        /// Reset the 'ground' hit info.
        /// </summary>

        public virtual void ResetGroundInfo()
        {
            var up = transform.up;

            prevGroundHit  = new GroundHit(_groundHitInfo);
            _groundHitInfo = new GroundHit
            {
                groundPoint   = transform.position,
                groundNormal  = up,
                surfaceNormal = up
            };
        }
Exemplo n.º 5
0
        public GroundHit(GroundHit other) : this()
        {
            isOnGround    = other.isOnGround;
            isValidGround = other.isValidGround;

            isOnLedgeSolidSide = other.isOnLedgeSolidSide;
            isOnLedgeEmptySide = other.isOnLedgeEmptySide;
            ledgeDistance      = other.ledgeDistance;

            isOnStep   = other.isOnStep;
            stepHeight = other.stepHeight;

            groundPoint     = other.groundPoint;
            groundNormal    = other.groundNormal;
            groundDistance  = Mathf.Max(0.0f, other.groundDistance);
            groundCollider  = other.groundCollider;
            groundRigidbody = other.groundRigidbody;

            surfaceNormal = other.surfaceNormal;
        }
Exemplo n.º 6
0
        /// <summary>
        /// Check if we are standing on a ledge or a step and update 'grounding' info.
        /// </summary>
        /// <param name="position">A probing position. This can be different from character's position.</param>
        /// <param name="rotation">A probing rotation. This can be different from character's rotation.</param>
        /// <param name="distance">The cast distance.</param>
        /// <param name="point">The current contact point.</param>
        /// <param name="normal">The current contact normal.</param>
        /// <param name="groundHitInfo">If found any 'ground', hitInfo will contain more information about it.</param>

        private void DetectLedgeAndSteps(Vector3 position, Quaternion rotation, ref GroundHit groundHitInfo,
                                         float distance, Vector3 point, Vector3 normal)
        {
            Vector3 up = rotation * Vector3.up, down = -up;
            var     projectedNormal = Vector3.ProjectOnPlane(normal, up).normalized;

            var nearPoint           = point + projectedNormal * kHorizontalOffset;
            var farPoint            = point - projectedNormal * kHorizontalOffset;

            var ledgeStepDistance = Mathf.Max(kMinLedgeDistance, Mathf.Max(stepOffset, distance));

            RaycastHit nearHitInfo;
            var        nearHit           = Raycast(nearPoint, down, out nearHitInfo, ledgeStepDistance);
            var        isNearGroundValid = nearHit && Vector3.Angle(nearHitInfo.normal, up) < groundLimit;

            RaycastHit farHitInfo;
            var        farHit           = Raycast(farPoint, down, out farHitInfo, ledgeStepDistance);
            var        isFarGroundValid = farHit && Vector3.Angle(farHitInfo.normal, up) < groundLimit;

            // Flush

            if (farHit && !isFarGroundValid)
            {
                groundHitInfo.surfaceNormal = farHitInfo.normal;

                // Attemp to retrieve the 'ground' below us

                RaycastHit secondaryHitInfo;
                if (BottomRaycast(position, rotation, out secondaryHitInfo, distance))
                {
                    // Update ground info and return

                    groundHitInfo.SetFrom(secondaryHitInfo);
                    groundHitInfo.surfaceNormal = secondaryHitInfo.normal;
                }

                return;
            }

            // Steps

            if (isNearGroundValid && isFarGroundValid)
            {
                // Choose nearest normal

                groundHitInfo.surfaceNormal =
                    (point - nearHitInfo.point).sqrMagnitude < (point - farHitInfo.point).sqrMagnitude
                        ? nearHitInfo.normal
                        : farHitInfo.normal;

                // Check if max vertical distance between points is steep enough to be considered as a step

                var nearHeight = Vector3.Dot(point - nearHitInfo.point, up);
                var farHeight  = Vector3.Dot(point - farHitInfo.point, up);

                var height = Mathf.Max(nearHeight, farHeight);
                if (height > kMinLedgeDistance && height < stepOffset)
                {
                    groundHitInfo.isOnStep   = true;
                    groundHitInfo.stepHeight = height;
                }

                return;
            }

            // Ledges

            // If only one of the near / far rays hit we are on a ledge

            var isOnLedge = isNearGroundValid != isFarGroundValid;

            if (!isOnLedge)
            {
                return;
            }

            // On ledge, compute ledge info and check which side of the ledge we are

            groundHitInfo.surfaceNormal = isFarGroundValid ? farHitInfo.normal : nearHitInfo.normal;

            groundHitInfo.ledgeDistance = Vector3.ProjectOnPlane(point - position, up).magnitude;

            if (isFarGroundValid && groundHitInfo.ledgeDistance > ledgeOffset)
            {
                // On possible ledge 'empty' side,
                // cast downwards using de ledgeOffset as radius

                groundHitInfo.isOnLedgeEmptySide = true;

                var radius = ledgeOffset;
                var offset = Mathf.Max(0.0f, capsuleCollider.height * 0.5f - radius);

                var bottomSphereCenter = capsuleCollider.center - Vector3.up * offset;
                var bottomSphereOrigin = position + rotation * bottomSphereCenter;

                RaycastHit hitInfo;
                if (SphereCast(bottomSphereOrigin, radius, down, out hitInfo, Mathf.Max(stepOffset, distance)))
                {
                    var verticalSquareDistance = Vector3.Project(point - hitInfo.point, up).sqrMagnitude;
                    if (verticalSquareDistance <= stepOffset * stepOffset)
                    {
                        groundHitInfo.isOnLedgeEmptySide = false;
                    }
                }
            }

            groundHitInfo.isOnLedgeSolidSide = !groundHitInfo.isOnLedgeEmptySide;
        }
Exemplo n.º 7
0
 public override bool ComputeGroundHit(Vector3 position, Quaternion rotation, ref GroundHit groundHitInfo,
                                       float distance = Mathf.Infinity)
 {
     throw new System.NotImplementedException();
 }
        /// <summary>
        /// Compute 'ground' hit info casting downwards the character's volume,
        /// if found any 'ground' groundHitInfo will contain additional information about it.
        /// Returns true when intersects any 'ground' collider, otherwise false.
        /// </summary>
        /// <param name="position">A probing position. This can be different from character's position.</param>
        /// <param name="rotation">A probing rotation. This can be different from character's rotation.</param>
        /// <param name="groundHitInfo">If true is returned, will contain more information about where the collider was hit.</param>
        /// <param name="distance">The length of the cast.</param>

        public abstract bool ComputeGroundHit(Vector3 position, Quaternion rotation, ref GroundHit groundHitInfo,
                                              float distance = Mathf.Infinity);
Exemplo n.º 9
0
        /// <summary>
        /// Compute 'ground' hit info casting downwards the character's volume,
        /// if found any 'ground' groundHitInfo will contain additional information about it.
        /// Returns true when intersects any 'ground' collider, otherwise false.
        /// </summary>
        /// <param name="probingPosition">A probing position, this can be different from character's position.</param>
        /// <param name="probingRotation">A probing position, this can be different from character's rotation.</param>
        /// <param name="groundHitInfo">If found any 'ground', this will contain more information about it</param>
        /// <param name="scanDistance">The maximum scan distnce (cast distance)</param>

        public bool ComputeGroundHit(Vector3 probingPosition, Quaternion probingRotation, out GroundHit groundHitInfo,
                                     float scanDistance = Mathf.Infinity)
        {
            groundHitInfo = new GroundHit();
            return(groundDetection.ComputeGroundHit(probingPosition, probingRotation, ref groundHitInfo, scanDistance));
        }