Esempio n. 1
0
        /// <summary>
        /// Solve collisions of moving objects.
        /// This is automatically called by FixedUpdate, but if you change the translation.position from code,
        /// you have to call this method to be sure collisions are solved before rendering the object.
        /// </summary>
        protected DynamicCollisionPoint DoSolveDynamicCollisions()
        {
            Vector3 vDisp = (transform.position - m_prevPos);
            Vector3 vDir  = vDisp.normalized;

            Vector3 vLocalDisp = transform.rotation != Quaternion.identity ? Quaternion.Inverse(transform.rotation) * vDisp : vDisp;
            //NOTE: One Way collisions will be checked separately
            LayerMask             layerCollision        = LayerCollision & ~(OneWayCollisionRight | OneWayCollisionLeft | OneWayCollisionUp | OneWayCollisionDown);
            bool                  flipX                 = transform.localScale.x < 0;
            bool                  flipY                 = transform.localScale.y < 0;
            List <Vector3>        vRightCheckPoints     = flipX ? m_vLeftCheckPoints : m_vRightCheckPoints;
            List <Vector3>        vLeftCheckPoints      = flipX ? m_vRightCheckPoints : m_vLeftCheckPoints;
            List <Vector3>        vTopCheckPoints       = flipY ? m_vBottomCheckPoints : m_vTopCheckPoints;
            List <Vector3>        vBottomCheckPoints    = flipY ? m_vTopCheckPoints : m_vBottomCheckPoints;
            DynamicCollisionPoint closestCollisionPoint = new DynamicCollisionPoint(float.MaxValue, Vector3.zero);

            // check for right movement
            //if (vLocalDisp.x >= 0.5f * SkinRightWidth * Mathf.Abs(transform.localScale.x))
            if (vLocalDisp.x >= Vector3.kEpsilon)  //NOTE: safer, but slower
            {
                closestCollisionPoint = DynamicCollisionPoint.Min(closestCollisionPoint, _DoSolveDynamicCollisionSide(vRightCheckPoints, vDisp, vDir, Vector3.zero, layerCollision));
                closestCollisionPoint = DynamicCollisionPoint.Min(closestCollisionPoint, _DoSolveDynamicCollisionSide(vRightCheckPoints, vDisp, vDir, transform.right, OneWayCollisionRight));
            }
            // check for left movement
            //else if (vLocalDisp.x <= -SkinLeftWidth * Mathf.Abs(transform.localScale.x))
            else if (vLocalDisp.x <= -Vector3.kEpsilon)
            {
                closestCollisionPoint = DynamicCollisionPoint.Min(closestCollisionPoint, _DoSolveDynamicCollisionSide(vLeftCheckPoints, vDisp, vDir, Vector3.zero, layerCollision));
                closestCollisionPoint = DynamicCollisionPoint.Min(closestCollisionPoint, _DoSolveDynamicCollisionSide(vLeftCheckPoints, vDisp, vDir, -transform.right, OneWayCollisionLeft));
            }

            // check for up movement
            //if (vLocalDisp.y >= SkinTopWidth * Mathf.Abs(transform.localScale.y))
            if (vLocalDisp.y >= Vector3.kEpsilon)
            {
                closestCollisionPoint = DynamicCollisionPoint.Min(closestCollisionPoint, _DoSolveDynamicCollisionSide(vTopCheckPoints, vDisp, vDir, Vector3.zero, layerCollision));
                closestCollisionPoint = DynamicCollisionPoint.Min(closestCollisionPoint, _DoSolveDynamicCollisionSide(vTopCheckPoints, vDisp, vDir, transform.up, OneWayCollisionUp));
            }
            // check for down movement
            //else if (vLocalDisp.y <= -SkinBottomWidth * Mathf.Abs(transform.localScale.y))
            else if (vLocalDisp.y <= -Vector3.kEpsilon)
            {
                closestCollisionPoint = DynamicCollisionPoint.Min(closestCollisionPoint, _DoSolveDynamicCollisionSide(vBottomCheckPoints, vDisp, vDir, Vector3.zero, layerCollision));
                closestCollisionPoint = DynamicCollisionPoint.Min(closestCollisionPoint, _DoSolveDynamicCollisionSide(vBottomCheckPoints, vDisp, vDir, -transform.up, OneWayCollisionDown));
            }

            // check if there was a collision
            if (closestCollisionPoint.distance < float.MaxValue)
            {
                // new body
                Vector3 scaledSize = Vector3.Scale(Size, transform.localScale);
                DebugEx.DebugDrawRect(transform.transform.TransformPoint(Center - (Vector3)Size / 2f), new Rect(0f, 0f, scaledSize.x, scaledSize.y), Color.red, 0.5f);

                transform.position = m_prevPos + (closestCollisionPoint.distance - k_SkinMinWidth) * vDir; //NOTE: subtracting k_SkinMinWidth avoid precision errors
            }
            return(closestCollisionPoint);
        }
        protected void DoClimbing()
        {
            float fHorAxis = 0f;
            float fVerAxis = 0f;

            if (GetActionState(eControllerActions.Right))
            {
                fHorAxis += m_horSpeedScale;
            }
            if (GetActionState(eControllerActions.Left))
            {
                fHorAxis -= m_horSpeedScale;
            }
            if (GetActionState(eControllerActions.Up))
            {
                fVerAxis += m_verSpeedScale;
            }
            if (GetActionState(eControllerActions.Down))
            {
                fVerAxis -= m_verSpeedScale;
            }
            if (m_isClimbing)
            {
                if (
                    GetIfActionHasChanged(eControllerActions.Jump) && GetActionState(eControllerActions.Jump) ||
                    GetIfActionHasChanged(eControllerActions.PlatformDropDown) && GetActionState(eControllerActions.PlatformDropDown))
                {
                    m_isClimbing = false;
                    m_currentClimbingCollider = null;
                    m_isGrounded = true;
                    return;
                }

                // isLadder is true when the collider width is small enough to avoid moving horizontal and center the player in the center of the collider.
                // By default, it is true when collider width is less than two times the smart rect collider width
                bool isLadder = m_currentClimbingCollider.bounds.size.x < m_ladderWidthFactor * m_smartCollider.Size.x;

                Vector3 vDisp = new Vector3(isLadder ? 0f : fHorAxis, fVerAxis);

                //debug climbing area
                DebugEx.DebugDrawRect(Vector2.zero, new Rect(m_currentClimbingCollider.bounds.min, m_currentClimbingCollider.bounds.size), Color.blue);


                if (isLadder)
                {
                    // Snap to ladder
                    Vector3 center  = m_currentClimbingCollider.bounds.center;
                    Vector3 snapPos = Vector3.Project(transform.position - center, m_currentClimbingCollider.transform.up); // this allow rotated ladders, like in pirate ship demo
                    snapPos           += center;
                    snapPos.z          = transform.position.z;
                    transform.position = Vector3.Lerp(transform.position, snapPos, 0.5f);
                }

                if (vDisp.magnitude > 0.2f)
                {
                    transform.position += transform.rotation * vDisp * ClimbingSpeed * Time.deltaTime;
                }

                m_isGrounded = m_smartCollider.enabled && m_smartCollider.IsGrounded();
            }

            // Check if going down and there is a climbing collider below
            float      SkinBottomWidthFactor = 1.1f; //NOTE: set a value > 1f to allow climbing down when climb collision and platform collision are close
            Collider2D climbingColliderBelow = GetClimbingColliderBelow(SkinBottomWidthFactor);
            Collider2D climbingColliderAbove = GetClimbingColliderAbove();

            if (fVerAxis < -0.5f && m_currentClimbingCollider == null)
            {
                if (climbingColliderBelow != null)
                {
                    if (m_currentClimbingCollider == null && climbingColliderAbove == null)
                    {
                        //Teleport the player. TeleportTo will skip any collider in between the current position and the new position to skip any platform in between
                        m_smartCollider.TeleportTo(transform.position - transform.up * m_smartCollider.SkinBottomWidth * SkinBottomWidthFactor);
                    }
                    m_currentClimbingCollider = climbingColliderBelow;
                    StartClimbing();
                }
                else
                {
                    StopClimbing();
                }
            }
            // Check if going up and it is inside a climbing collider
            else if (fVerAxis > 0.5f)
            {
                if (climbingColliderAbove != null && !GetIfActionHasChanged(eControllerActions.Jump))
                {
                    m_currentClimbingCollider = climbingColliderAbove;
                    StartClimbing();
                }
                else if (m_smartCollider.SkinBottomRayContacts.Contains(true) || climbingColliderBelow == null)
                {
                    StopClimbing();
                }
            }
            // Stop climbing once the top is reached
            else if (m_isGrounded || (climbingColliderBelow == null && climbingColliderAbove == null))
            {
                StopClimbing();
            }
        }