示例#1
0
    //Called for every fixedupdate that this module is active
    public override void FixedUpdateModule()
    {
        if (!m_ControlledCollider.GetSideCastInfo().m_HasHitSide || (!m_ControlledCollider.IsPartiallyTouchingWall()))
        {
            return;
        }
        m_WallNormal = m_ControlledCollider.GetSideCastInfo().GetSideNormal();
        float distance = m_ControlledCollider.GetSideCastInfo().GetDistance();

        m_ControlledCollider.GetCapsuleTransform().Move(-m_WallNormal * distance);
        m_RotateMethod = (m_WallNormal.y < 0) ? RotateMethod.FromTop : RotateMethod.FromBottom;
        m_UpDirection  = CState.GetDirectionAlongNormal(Vector3.up, m_WallNormal);
        m_ControlledCollider.RotateToAlignWithNormal(m_UpDirection, m_RotateMethod);

        Vector2 currentVel = m_ControlledCollider.GetVelocity();

        Vector2 fGravity = -m_UpDirection *m_WallRunGravity *Vector2.Dot(-m_UpDirection, Vector2.down);  //Gravity along wall, but with correct velocity

        Vector2 fDrag = -0.5f * (currentVel.sqrMagnitude) * m_CharacterController.GetDragConstant() * currentVel.normalized;

        if (!m_ApplyDrag)
        {
            fDrag = Vector2.zero;
        }
        Vector2 summedF = fGravity + fDrag;

        Vector2 newVel = currentVel + (summedF * Time.fixedDeltaTime);

        m_ControlledCollider.UpdateWithVelocity(newVel);
    }
示例#2
0
    //Query whether this module can be active, given the current state of the character controller (velocity, isGrounded etc.)
    //Called every frame when inactive (to see if it could be) and when active (to see if it should not be)
    public override bool IsApplicable()
    {
        if (m_ControlledCollider.IsGrounded())
        {
            return(false);
        }
        //Wall needs to be hit for the module to be active
        if (m_ControlledCollider.GetSideCastInfo().m_HasHitSide)
        {
            if (!m_ControlledCollider.IsPartiallyTouchingWall())
            {
                return(false);
            }
            m_WallNormal   = m_ControlledCollider.GetSideCastInfo().GetSideNormal();
            m_RotateMethod = (m_WallNormal.y < 0) ? RotateMethod.FromTop : RotateMethod.FromBottom;
            m_UpDirection  = CState.GetDirectionAlongNormal(Vector3.up, m_WallNormal);

            if (!m_ControlledCollider.CanAlignWithNormal(m_UpDirection))
            {
                return(false);
            }
            return(true);
        }
        return(false);
    }
示例#3
0
 //Easy way to get a direction along the ground
 public override Vector2 GetWalkDirection(Vector2 a_Speed)
 {
     if (a_Speed.x == 0 || !m_IsGrounded)
     {
         return(Vector2.zero);
     }
     return(CState.GetDirectionAlongNormal(a_Speed, GetNormal()));
 }
示例#4
0
    //This takes the results of a sidecast and tries to determine if a wall is found.
    //Using the sidecast information, it sends out three probes (from the center of the top and bottom hemispheres and the center of the capsule).
    //It counts the amount of hits that are valid and stores this for other classes to access.
    void WallCast(RaycastHit a_SidecastHit)
    {
        if (Vector3.Angle(Vector3.up, a_SidecastHit.normal) > m_CapsuleCollider.GetMaxWallAngle() || Vector3.Angle(Vector3.up, a_SidecastHit.normal) < m_CapsuleCollider.GetMaxGroundedAngle())
        {
            m_WallCastCount = 0;
            return;
        }
        float   wallCastMargin   = m_CapsuleCollider.GetWallCastMargin();
        float   wallCastDistance = m_CapsuleCollider.GetWallCastDistance();
        Vector3 hitPos           = a_SidecastHit.point;
        Vector3 normal           = a_SidecastHit.normal;
        Vector3 currentUp        = m_CapsuleCollider.GetUpDirection();
        Vector3 direction        = CState.GetDirectionAlongNormal(Vector3.up, normal);
        Vector3 startHitPos1     = Vector3.zero;
        Vector3 startHitPos2     = Vector3.zero;
        Vector3 startHitPos3     = Vector3.zero;
        Vector3 normalOff        = normal * (m_CapsuleCollider.GetRadius() - wallCastMargin);

        //Determines where the sidecast has hit. This to orient the probes in the correct way.
        if (Vector3.Dot(currentUp, hitPos) <= Vector3.Dot(currentUp, m_CapsuleCollider.GetDownCenter()))//Hit on the lower hemisphere
        {
            startHitPos1 = m_CapsuleCollider.GetDownCenter() + direction * m_CapsuleCollider.GetDefaultLength() - normalOff;
            startHitPos2 = m_CapsuleCollider.GetDownCenter() - normalOff;
            startHitPos3 = m_CapsuleCollider.GetDownCenter() + direction * m_CapsuleCollider.GetDefaultLength() * 0.5f - normalOff;
        }
        else if (Vector3.Dot(currentUp, hitPos) >= Vector3.Dot(currentUp, m_CapsuleCollider.GetUpCenter()))//Hit on the upper hemisphere
        {
            startHitPos1 = m_CapsuleCollider.GetUpCenter() - direction * m_CapsuleCollider.GetDefaultLength() - normalOff;
            startHitPos2 = m_CapsuleCollider.GetUpCenter() - normalOff;
            startHitPos3 = m_CapsuleCollider.GetUpCenter() - direction * m_CapsuleCollider.GetDefaultLength() * 0.5f - normalOff;
        }
        else //Hit somewhere in the middle
        {
            startHitPos1 = m_CapsuleCollider.GetDownCenter(true) + direction * m_CapsuleCollider.GetDefaultLength() - normalOff;
            startHitPos2 = m_CapsuleCollider.GetDownCenter(true) - normalOff;
            startHitPos3 = m_CapsuleCollider.GetDownCenter(true) + direction * m_CapsuleCollider.GetDefaultLength() * 0.5f - normalOff;
        }

        RaycastHit newHit;

        m_WallCastCount = 0;
        if (Physics.Raycast(startHitPos1, -normal, out newHit, wallCastDistance + wallCastMargin, m_CapsuleCollider.GetLayerMask()))
        {
            m_WallCastCount++;
        }
        if (Physics.Raycast(startHitPos2, -normal, out newHit, wallCastDistance + wallCastMargin, m_CapsuleCollider.GetLayerMask()))
        {
            m_WallCastCount++;
        }
        if (Physics.Raycast(startHitPos3, -normal, out newHit, wallCastDistance + wallCastMargin, m_CapsuleCollider.GetLayerMask()))
        {
            m_WallCastCount++;
        }
    }
示例#5
0
    //Called for every fixedupdate that this module is active
    public override void FixedUpdateModule()
    {
        m_WallNormal   = m_ControlledCollider.GetSideCastInfo().GetSideNormal();
        m_RotateMethod = (m_WallNormal.y < 0) ? RotateMethod.FromTop : RotateMethod.FromBottom;
        m_UpDirection  = CState.GetDirectionAlongNormal(Vector3.up, m_WallNormal);
        m_ControlledCollider.RotateToAlignWithNormal(m_UpDirection, m_RotateMethod);

        float distance = m_ControlledCollider.GetSideCastInfo().GetDistance();

        m_ControlledCollider.GetCapsuleTransform().Move(-m_WallNormal * distance);

        m_ControlledCollider.UpdateWithVelocity(Vector2.zero);
    }
示例#6
0
    void FixedUpdate()
    {
        CSideCastInfo sideCastInfo = m_Collider.GetSideCastInfo();

        if (sideCastInfo.m_WallCastCount >= 2)
        {
            Vector2 currentVel = m_Collider.GetVelocity();
            float   dot        = Vector3.Dot(currentVel, CState.GetDirectionAlongNormal(currentVel, sideCastInfo.GetSideNormal()));
            if (dot >= m_Threshold)
            {
                m_ParticleSystem.transform.position = sideCastInfo.GetSidePoint();
                m_ParticleSystem.transform.LookAt(sideCastInfo.GetSidePoint() + new Vector3(sideCastInfo.GetSideNormal().x, sideCastInfo.GetSideNormal().y, 0.0f), Vector3.back);
                m_ParticleSystem.Emit(m_EmissionCount);
            }
        }
    }
    //Called for every fixedupdate that this module is active
    public override void FixedUpdateModule()
    {
        m_WallNormal   = m_ControlledCollider.GetSideCastInfo().GetSideNormal();
        m_RotateMethod = (m_WallNormal.y < 0) ? RotateMethod.FromTop : RotateMethod.FromBottom;
        m_UpDirection  = CState.GetDirectionAlongNormal(Vector3.up, m_WallNormal);
        m_ControlledCollider.RotateToAlignWithNormal(m_UpDirection, m_RotateMethod);

        //if touching a wall during a jump, cut it short when jump is released
        if (m_HonorJumpCut)
        {
            m_CharacterController.UpdateJumpCut();
        }

        Vector2 currentVel = m_ControlledCollider.GetVelocity();

        Vector2 fInput = m_CharacterController.GetDirectedInputMovement() * m_EscapeVelocity;

        if (m_WallNormal.y >= 0.1f)
        {
            fInput = Vector2.zero;
        }
        float inputDotToNormal = Vector2.Dot(fInput, m_WallNormal);

        if (inputDotToNormal <= 0)//Moving into wall, or not moving at all
        {
            fInput = Vector2.zero;
            float distance = m_ControlledCollider.GetSideCastInfo().GetDistance();
            m_ControlledCollider.GetCapsuleTransform().Move(-m_WallNormal * distance);
        }

        Vector2 fGravity = -m_UpDirection * m_SlideGravity;//

        if (!m_UseSameGravityForAllSlopes)
        {
            fGravity *= Vector2.Dot(-m_UpDirection, Vector2.down);//Gravity along wall, but with corrected gravity
        }
        Vector2 fDrag = -0.5f * (currentVel.sqrMagnitude) * m_CharacterController.GetDragConstant() * currentVel.normalized;

        Vector2 summedF = fInput + fGravity + fDrag;

        Vector2 newVel = currentVel + (summedF * Time.fixedDeltaTime);

        newVel += GetFrictionAlongWall(newVel);

        m_ControlledCollider.UpdateWithVelocity(newVel);
    }
示例#8
0
    protected override void GeneratePath()
    {
        CEdgeCastInfo info = m_ControlledCollider.GetEdgeCastInfo();

        m_Path.Clear();
        CapsuleTransform copy = m_ControlledCollider.GetCapsuleTransformCopy();

        //First node is in edgehang alignment, moving away from the edge mildly
        CapsuleMovementPathNode newNode = m_Path.CreateFirstNode(copy);
        Vector3 upCenter = info.GetProposedHeadPoint();

        upCenter += m_ControlledCollider.GetEdgeCastInfo().GetWallNormal() * 0.03f;
        copy.SetUpCenter(upCenter);
        copy.Rotate(info.GetUpDirection(), RotateMethod.FromTop);
        newNode = m_Path.DuplicateAndAddLastNode();
        newNode.CopyFromTransform(copy);

        //Second node moves up along local up, until the bottom can slide over the edge
        newNode            = m_Path.DuplicateAndAddLastNode();
        newNode.m_Duration = m_MoveUpTime;
        float contactDot  = Vector3.Dot(info.GetEdgeNormal(), info.GetEdgePoint());
        float bottomDot   = Vector3.Dot(info.GetEdgeNormal(), copy.GetDownCenter()) - m_ControlledCollider.GetRadius() - m_MoveUpMargin;
        float normalDot   = Vector3.Dot(info.GetEdgeNormal(), copy.GetUpDirection());
        float rawDistance = contactDot - bottomDot;
        float distance    = rawDistance / normalDot;

        newNode.m_Position += copy.GetUpDirection() * distance;
        newNode.ApplyEntireMovement(copy);
        //Third node snaps the capsule to the "upright" position for the ground it's going to stand on
        newNode                = m_Path.DuplicateAndAddLastNode();
        newNode.m_Duration     = 0.0f;
        newNode.m_UpDirection  = (m_CharacterController.GetAlignsToGround()) ? info.GetEdgeNormal() : Vector3.up;
        newNode.m_RotateMethod = RotateMethod.FromBottom;
        newNode.ApplyEntireMovement(copy);
        newNode.m_Position = copy.GetPosition();
        //Final node moves the capsule over the ground
        newNode            = m_Path.DuplicateAndAddLastNode();
        newNode.m_Duration = m_MoveSideTime;
        Vector3 direction = CState.GetDirectionAlongNormal(-info.GetWallNormal(), info.GetEdgeNormal());

        newNode.m_Position += direction * m_MoveSideDistance;
        newNode.ApplyEntireMovement(copy);
    }
示例#9
0
    //Called for every fixedupdate that this module is active
    public override void FixedUpdateModule()
    {
        m_WallNormal   = m_ControlledCollider.GetSideCastInfo().GetSideNormal();
        m_RotateMethod = (m_WallNormal.y < 0) ? RotateMethod.FromTop : RotateMethod.FromBottom;
        m_UpDirection  = CState.GetDirectionAlongNormal(Vector3.up, m_WallNormal);
        m_ControlledCollider.RotateToAlignWithNormal(m_UpDirection, m_RotateMethod);

        float distance = m_ControlledCollider.GetSideCastInfo().GetDistance();

        m_ControlledCollider.GetCapsuleTransform().Move(-m_WallNormal * distance);

        Vector2 newVel = Vector2.zero;

        if (GetDirInput("Move").m_Direction == DirectionInput.Direction.Down || GetDirInput("Move").m_Direction == DirectionInput.Direction.Up)
        {
            newVel = m_UpDirection * GetDirInput("Move").m_ClampedInput.y *m_Speed;
        }
        m_ControlledCollider.UpdateWithVelocity(newVel);
    }
示例#10
0
    void FixedUpdate()
    {
        if (m_Collider.IsGrounded())
        {
            CGroundedInfo groundedInfo = m_Collider.GetGroundedInfo();
            Vector2       currentVel   = m_Collider.GetVelocity();
            float         dot          = Vector3.Dot(currentVel, CState.GetDirectionAlongNormal(currentVel, groundedInfo.GetNormal()));

            if (dot >= m_LowThreshold)
            {
                int emission = m_LowEmissionCount;
                if (dot >= m_HighThreshold)
                {
                    emission = m_HighEmissionCount;
                }
                m_ParticleSystem.transform.position = groundedInfo.GetPoint();
                m_ParticleSystem.transform.LookAt(groundedInfo.GetPoint() + new Vector3(groundedInfo.GetNormal().x, groundedInfo.GetNormal().y, 0.0f), Vector3.back);
                m_ParticleSystem.Emit(emission);
            }
        }
    }
示例#11
0
 //Query whether this module can be active, given the current state of the character controller (velocity, isGrounded etc.)
 //Called every frame when inactive (to see if it could be) and when active (to see if it should not be)
 public override bool IsApplicable()
 {
     if (m_ControlledCollider.IsGrounded())
     {
         return(false);
     }
     //Wall needs to be hit for the module to be active
     if (m_ControlledCollider.GetSideCastInfo().m_HasHitSide)
     {
         if (m_IsActive)
         {
             if (!m_ControlledCollider.IsPartiallyTouchingWall())
             {
                 return(false);
             }
         }
         else
         {
             if (!m_ControlledCollider.IsCompletelyTouchingWall())
             {
                 return(false);
             }
         }
         m_WallNormal   = m_ControlledCollider.GetSideCastInfo().GetSideNormal();
         m_RotateMethod = (m_WallNormal.y < 0) ? RotateMethod.FromTop : RotateMethod.FromBottom;
         m_UpDirection  = CState.GetDirectionAlongNormal(Vector3.up, m_WallNormal);
         if (!m_ControlledCollider.CanAlignWithNormal(m_UpDirection))
         {
             return(false);
         }
         RaycastHit hit;
         //If moving down, and possibly entering a position where it cannot rotate back (and therefore escape), stop before this happens
         if (Vector3.Dot(m_ControlledCollider.GetVelocity(), m_UpDirection) < 0 && Physics.Raycast(m_ControlledCollider.GetDownCenter(), -m_UpDirection, out hit, m_ControlledCollider.GetRadius() + m_EscapeCornerRaycastDistance, m_ControlledCollider.GetLayerMask()))
         {
             return(false);
         }
         return(true);
     }
     return(false);
 }
示例#12
0
    //Execute jump (lasts one update)
    //Called for every fixedupdate that this module is active
    public override void FixedUpdateModule()
    {
        Vector2 currentVel = m_ControlledCollider.GetVelocity();

        //Make sure that current velocity is aligned to the side wall
        currentVel = CState.GetDirectionAlongNormal(currentVel, m_SideNormal) * currentVel.magnitude;
        //If sliding down the wall when jumping, reset that velocity
        if (m_ResetVerticalSpeedIfFalling)
        {
            currentVel.y = Mathf.Clamp(currentVel.y, 0.0f, float.MaxValue);
        }
        //Possibly inherit some velocity from before jumping, leading to increased velocity on jump
        currentVel.y *= m_VerticalVelocityInheritanceFactor;
        currentVel.x *= m_HorizontalVelocityInheritanceFactor;
        Vector2 up            = (m_UseWallUpDirection) ? CState.GetDirectionAlongNormal(Vector2.up, m_SideNormal) : Vector2.up;
        Vector2 jumpDirection = Vector2.Lerp(up, m_SideNormal, m_JumpAlignedToWallFactor).normalized;
        Vector2 jumpVelocity  = jumpDirection * m_JumpVelocity;

        m_CharacterController.Jump(currentVel + jumpVelocity);

        m_ControlledCollider.UpdateWithVelocity(m_ControlledCollider.GetVelocity());
    }
示例#13
0
    Vector2 GetFrictionAlongWall(Vector2 a_Velocity)
    {
        Vector2 directionAlongWall = CState.GetDirectionAlongNormal(a_Velocity, new Vector2(m_WallNormal.x, m_WallNormal.y));

        if (!m_ApplyFrictionIfGoingUpwards && directionAlongWall.y > 0)//Going upwards
        {
            return(Vector2.zero);
        }
        Vector2 frictionDirection = -directionAlongWall;

        Vector2 maxFrictionSpeedChange = frictionDirection * m_SlideFriction * Time.fixedDeltaTime;

        Vector2 velInDirection = Mathf.Abs(Vector2.Dot(a_Velocity, frictionDirection)) * frictionDirection;

        if (velInDirection.magnitude > maxFrictionSpeedChange.magnitude)
        {
            return(maxFrictionSpeedChange);
        }
        else
        {
            return(velInDirection);
        }
    }
示例#14
0
    //Side cast receives the results of two capsulecasts (to the left and the right of the capsule).
    //It uses this first to determine whether there is a hit to the side of the capsule, and then whether or not this hit could signify a wall.
    //Mostvalidhit stores information on this sidecast. Sidecast information can sometimes be used without wallcast information
    public void UpdateWithCollisions(List <RaycastHit> leftHitResults, List <RaycastHit> rightHitResults)
    {
        m_HasHitSide    = false;
        m_WallCastCount = 0;
        if (leftHitResults.Count == 0 && rightHitResults.Count == 0)
        {
            return;
        }
        List <RaycastHit> resultsToUse = null;

        //First determine which side to use (based on distance)
        if (leftHitResults.Count == 0)
        {
            resultsToUse = rightHitResults;
        }
        else if (rightHitResults.Count == 0)
        {
            resultsToUse = leftHitResults;
        }
        else
        {
            float lowestSideDistance = float.MaxValue;
            for (int i = 0; i < leftHitResults.Count; i++)
            {
                if (leftHitResults[i].point.y < lowestSideDistance)
                {
                    lowestSideDistance = leftHitResults[i].distance;
                    resultsToUse       = leftHitResults;
                }
            }
            for (int i = 0; i < rightHitResults.Count; i++)
            {
                if (rightHitResults[i].point.y < lowestSideDistance)
                {
                    lowestSideDistance = rightHitResults[i].distance;
                    resultsToUse       = rightHitResults;
                }
            }
        }
        m_HasHitSide   = true;
        m_MostValidHit = resultsToUse[0];
        float lowestDistance = float.MaxValue;

        for (int i = 0; i < resultsToUse.Count; i++)
        {
            if (resultsToUse[i].distance < lowestDistance)
            {
                lowestDistance = resultsToUse[i].distance;
                m_MostValidHit = resultsToUse[i];
            }
        }

        //Capsulecasts can give unusable results on corners.
        //Cast a little bit up and down from the initial point to get a better normal.
        RaycastHit correctingCast1, correctingCast2;
        Vector3    dirAlongNormal  = CState.GetDirectionAlongNormal(Vector3.up, m_MostValidHit.normal);
        bool       correctCast1Hit = Physics.Raycast(m_MostValidHit.point + m_MostValidHit.normal * 0.02f + dirAlongNormal * 0.01f, -m_MostValidHit.normal, out correctingCast1, 0.03f, m_CapsuleCollider.GetLayerMask());
        bool       correctCast2Hit = Physics.Raycast(m_MostValidHit.point + m_MostValidHit.normal * 0.02f - dirAlongNormal * 0.01f, -m_MostValidHit.normal, out correctingCast2, 0.03f, m_CapsuleCollider.GetLayerMask());

        if (correctCast1Hit || correctCast2Hit)
        {
            RaycastHit correctCastToUse = m_MostValidHit;
            if (correctCast1Hit && correctCast2Hit)
            {
                float dot1 = Vector3.Dot(m_MostValidHit.normal, correctingCast1.normal);
                float dot2 = Vector3.Dot(m_MostValidHit.normal, correctingCast2.normal);
                if (dot1 < 0.95f && dot2 < 0.95f)
                {
                    if (dot1 > dot2)
                    {
                        correctCastToUse = correctingCast1;
                    }
                    else
                    {
                        correctCastToUse = correctingCast2;
                    }
                }
            }
            else if (correctCast1Hit)
            {
                if (Vector3.Dot(m_MostValidHit.normal, correctingCast1.normal) < 0.95f)
                {
                    correctCastToUse = correctingCast1;
                }
            }
            else
            {
                if (Vector3.Dot(m_MostValidHit.normal, correctingCast2.normal) < 0.95f)
                {
                    correctCastToUse = correctingCast2;
                }
            }
            m_MostValidHit = correctCastToUse;
        }
        m_Distance = lowestDistance - m_CapsuleCollider.GetSideCastMargin();
        WallCast(m_MostValidHit);
    }
示例#15
0
    void Update()
    {
        if (m_CharacterController == null || m_CharacterController.GetCollider() == null)
        {
            Debug.LogError("Sprite animator can't find properly set-up character");
            this.enabled = false;
            return;
        }
        if (Mathf.Abs(m_CharacterController.GetCollider().GetVelocity().x) >= m_MoveSpeedThreshhold)
        {
            m_LastGoodDirection = m_CharacterController.GetCollider().GetVelocity().normalized;
        }
        else if (Mathf.Abs(m_CharacterController.GetInputMovement().x) > 0.0f)
        {
            m_LastGoodDirection = Vector2.right * m_CharacterController.GetInputMovement().x;
        }
        //Rotate and position the capsule according to character velocity and context
        float zRot   = 0.0f;
        float xScale = 1.0f;

        if (m_CurrentAnimationName != "")
        {
            if (m_CharacterController.GetCollider().IsGrounded())
            {
                Vector2 normal = m_CharacterController.GetCollider().GetGroundedInfo().GetNormal();

                xScale = (m_LastGoodDirection.x >= 0.0f) ? 1.0f : -1.0f;
                zRot   = -Mathf.Atan2(normal.x, normal.y) * Mathf.Rad2Deg;
            }
            else if (m_CharacterController.GetCollider().IsPartiallyTouchingWall())
            {
                Vector2 normal = m_CharacterController.GetCollider().GetSideCastInfo().GetSideNormal();
                Vector2 up     = CState.GetDirectionAlongNormal(Vector2.up, normal);
                xScale = ((normal.x <= 0.0f) ? 1.0f : -1.0f);
                zRot   = -Mathf.Atan2(up.x, up.y) * Mathf.Rad2Deg;

                m_LastGoodDirection = -normal;
            }
            else if (m_CharacterController.GetCollider().IsTouchingEdge())
            {
                Vector2 normal = m_CharacterController.GetCollider().GetEdgeCastInfo().GetWallNormal();
                Vector2 up     = CState.GetDirectionAlongNormal(Vector2.up, normal);
                xScale = ((normal.x <= 0.0f) ? 1.0f : -1.0f);
                zRot   = -Mathf.Atan2(up.x, up.y) * Mathf.Rad2Deg;

                m_LastGoodDirection = -normal;
            }
            else
            {
                zRot   = 0.0f;
                xScale = (m_LastGoodDirection.x >= 0.0f) ? 1.0f : -1.0f;
                Vector2 up = m_CharacterController.GetCurrentVisualUp();
                if (up != Vector2.up)
                {
                    zRot = -Mathf.Atan2(up.x, up.y) * Mathf.Rad2Deg;
                }
            }
        }
        if (!m_BlockRotation)
        {
            zRot = Mathf.Lerp(m_LastZRot, zRot, 0.1f);
            m_SpriteTransformHook.transform.rotation = Quaternion.Euler(0.0f, 0.0f, zRot);
        }
        m_SpriteTransformHook.transform.localScale = new Vector3(xScale, 1.0f, 1.0f);
        m_LastZRot = zRot;

        Vector3 startPosition = Vector3.zero;

        //We are being controlled by a different entity, block interpolation
        if (m_CharacterController.IsMovementLocked())
        {
            startPosition = m_CharacterController.transform.position;
        }
        else
        {
            startPosition = GetInterpolatedPosition();
        }
        //Take the bottom of the capsule collider to rotate from
        //Animations themselves might adjust position via localposition
        Vector3 heightAdjustment = m_CapsuleCollider.GetUpDirection() * (-m_CapsuleCollider.GetLength() * 0.5f);

        m_SpriteTransformHook.transform.position = startPosition + heightAdjustment;
        StartAnimation(m_CharacterController.GetCurrentSpriteState());
    }
示例#16
0
    //This context uses raycasts to determine if a ledge is present.
    //The raycasts originate from above and to the left/right of the character, and are casted downwards
    public void UpdateWithCollisions()
    {
        RaycastHit rightHit;
        RaycastHit leftHit;
        bool       rightHasHit = false;
        bool       leftHasHit  = false;

        m_HasHitEdge = false;
        Vector3 upCenter       = m_CapsuleCollider.GetUpCenter();
        Vector3 upDirection    = m_CapsuleCollider.GetUpDirection();
        Vector3 transformRight = m_CapsuleCollider.transform.right;
        //Orient against wall to the side, even if not able to wallsliding
        //This allows the character to grab onto ledges on slanted walls
        CSideCastInfo sideInfo = m_CapsuleCollider.GetSideCastInfo();

        if (sideInfo.m_HasHitSide)
        {
            Vector3 sideNormal = sideInfo.GetSideNormal();
            Vector3 sidePoint  = sideInfo.GetSidePoint();
            Vector3 sideUp     = CState.GetDirectionAlongNormal(Vector3.up, sideNormal);

            RaycastHit properSideHit;
            if (Physics.Raycast(sidePoint + sideNormal * 0.2f + sideUp * 0.05f, -sideNormal, out properSideHit, 0.3f, m_CapsuleCollider.GetLayerMask()))
            {
                Vector3 newPoint  = properSideHit.point;
                Vector3 newNormal = properSideHit.normal;
                Vector3 newUp     = CState.GetDirectionAlongNormal(Vector3.up, newNormal);

                Vector3          pivot       = upCenter - newPoint;
                Quaternion       rotation    = Quaternion.FromToRotation(upDirection, newUp);
                Vector3          newUpCenter = newPoint + rotation * pivot;
                CapsuleTransform transform   = m_CapsuleCollider.GetCapsuleTransformCopy();
                transform.SetUpDirection(newUp);
                transform.SetUpCenter(newUpCenter);

                upCenter       = newUpCenter;
                upDirection    = newUp;
                transformRight = transform.GetRightDirection();
            }
        }
        //Determine the starting positions of the raycasts
        Vector3 centerStart = upCenter + upDirection * (m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastVerticalMargin());
        Vector3 sideOffSet  = transformRight * (m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastHorizontalDistance());
        Vector3 grabLength  = -upDirection * (m_CapsuleCollider.GetEdgeCastVerticalDistance() + m_CapsuleCollider.GetEdgeCastVerticalMargin());

        //The raycasts originate from above and to the left/right of the character, and are casted downwards
        //The angle of the slope they hit can't be too steep, or the character will slide off.
        if (Physics.Raycast(centerStart + sideOffSet, -upDirection, out rightHit, grabLength.magnitude, m_CapsuleCollider.GetLayerMask()))
        {
            if (Vector3.Angle(rightHit.normal, upDirection) < m_CapsuleCollider.GetMaxGrabAngle())
            {
                rightHasHit = true;
            }
        }
        if (Physics.Raycast(centerStart - sideOffSet, -upDirection, out leftHit, grabLength.magnitude, m_CapsuleCollider.GetLayerMask()))
        {
            if (Vector3.Angle(leftHit.normal, upDirection) < m_CapsuleCollider.GetMaxGrabAngle())
            {
                leftHasHit = true;
            }
        }
        if (rightHasHit || leftHasHit)
        {
            RaycastHit castToUse;
            Vector3    wallProbeDirection;
            if (rightHasHit && leftHasHit)
            {
                if (leftHit.distance < rightHit.distance)
                {
                    castToUse          = leftHit;
                    wallProbeDirection = -transformRight;
                }
                else
                {
                    castToUse          = rightHit;
                    wallProbeDirection = transformRight;
                }
            }
            else if (rightHasHit)
            {
                castToUse          = rightHit;
                wallProbeDirection = transformRight;
            }
            else
            {
                castToUse          = leftHit;
                wallProbeDirection = -transformRight;
            }

            //Check if the character can even hold on. The distance between the character and the start of the raycastpoint should not be blocked by a collider (no grabbing on ledges on the other side of walls
            if (Physics.Raycast(centerStart, wallProbeDirection, m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastHorizontalDistance(), m_CapsuleCollider.GetLayerMask()))
            {
                return;
            }

            wallProbeDirection = CState.GetDirectionAlongNormal(wallProbeDirection, castToUse.normal);
            //After detecting that collider can hang onto edge, orient properly against surface
            RaycastHit probeHit;
            Vector3    probedNormal = CState.GetDirectionAlongNormal(-wallProbeDirection, Vector3.up);
            if (Physics.Raycast(upCenter, wallProbeDirection, out probeHit, m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeAlignProbeDistance(), m_CapsuleCollider.GetLayerMask()))
            {
                float angle = Vector3.Angle(probeHit.normal, Vector3.up);
                if (angle < m_CapsuleCollider.GetMaxEdgeAlignAngle())
                {
                    probedNormal = probeHit.normal;
                }
            }
            Vector3 newUpDirection = CState.GetDirectionAlongNormal(Vector3.up, probedNormal);
            Vector3 headPoint      = castToUse.point + probedNormal * (m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastHorizontalDistance()) - newUpDirection * m_CapsuleCollider.GetRadius();
            //Block check
            //Check if something is obstructing the proposed headposition (and fix if so)
            RaycastHit blockHit;
            if (Physics.Raycast(headPoint, -probedNormal, out blockHit, m_CapsuleCollider.GetRadius(), m_CapsuleCollider.GetLayerMask()))
            {
                if (blockHit.distance < m_CapsuleCollider.GetRadius())
                {
                    Vector3 alongNormal = -CState.GetDirectionAlongNormal(wallProbeDirection, castToUse.normal);
                    castToUse.point += alongNormal * (m_CapsuleCollider.GetRadius() - blockHit.distance) / Vector3.Dot(alongNormal, blockHit.normal);

                    headPoint = castToUse.point + probedNormal * (m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastHorizontalDistance()) - newUpDirection * m_CapsuleCollider.GetRadius();
                }
            }

            //Rotation check, check if character is aligned to wall or dangling
            //then check if that rotation can be achieved
            CapsuleTransform copy = m_CapsuleCollider.GetCapsuleTransformCopy();
            copy.SetUpCenter(headPoint);
            if (Vector3.Angle(Vector3.up, probedNormal) > m_CapsuleCollider.GetMaxWallAngle() || Vector3.Angle(Vector3.up, probedNormal) < m_CapsuleCollider.GetMaxGroundedAngle())
            {
                return;
            }

            if (copy.CanRotate(newUpDirection, RotateMethod.FromTop))
            {
                m_EdgePoint            = castToUse.point;
                m_EdgeNormal           = castToUse.normal;
                m_EdgeTransform        = castToUse.transform;
                m_WallNormal           = probedNormal;
                m_UpDirection          = newUpDirection;
                m_ProposedHeadPosition = headPoint;
                m_HasHitEdge           = true;
            }
        }
    }
示例#17
0
    //Query whether this module can be active, given the current state of the character controller (velocity, isGrounded etc.)
    //Called every frame when inactive (to see if it could be) and when active (to see if it should not be)
    public override bool IsApplicable()
    {
        if (m_ControlledCollider.IsGrounded())
        {
            return(false);
        }
        //Do allow if we just jumped up
        if (m_CharacterController.DidJustJump())
        {
            m_IsAlreadyTouchingWall = false;
        }
        if ((DoesInputExist("Crouch") && GetButtonInput("Crouch").m_IsPressed) ||
            GetDirInput("Move").m_Direction == DirectionInput.Direction.Down)
        {
            return(false);
        }
        //Disable when already touching wall beforehand, except when coming from standing position
        if (!m_IsActive && m_AllowOnlyWhenJustTouchingWall && (Time.time - m_CharacterController.GetLastGroundedTime() >= 0.02f))
        {
            if (m_ControlledCollider.GetSideCastInfo().m_HasHitSide&& (m_ControlledCollider.IsCompletelyTouchingWall()))
            {
                if (m_IsAlreadyTouchingWall)
                {
                    return(false);
                }
                m_IsAlreadyTouchingWall = true;
            }
            else
            {
                m_IsAlreadyTouchingWall = false;
                return(false);
            }
        }
        if (m_ControlledCollider.GetSideCastInfo().m_HasHitSide)
        {
            if (m_IsActive)
            {
                if (!m_ControlledCollider.IsPartiallyTouchingWall())
                {
                    return(false);
                }
            }
            else
            {
                if (!m_ControlledCollider.IsCompletelyTouchingWall())
                {
                    return(false);
                }
            }

            m_WallNormal   = m_ControlledCollider.GetSideCastInfo().GetSideNormal();
            m_RotateMethod = (m_WallNormal.y < 0) ? RotateMethod.FromTop : RotateMethod.FromBottom;
            m_UpDirection  = CState.GetDirectionAlongNormal(Vector3.up, m_WallNormal);
            float upVelDot = Vector2.Dot(m_UpDirection, m_ControlledCollider.GetVelocity());
            if (m_DisableWhenBelowVelocity && upVelDot <= m_MinimumUpVelocityOnStart) //Already going downwards, not applicable
            {
                return(false);
            }
            if (!m_ControlledCollider.CanAlignWithNormal(m_UpDirection))
            {
                return(false);
            }
            return(true);
        }
        return(false);
    }