예제 #1
0
        private void Awake()
        {
            charController = GetComponent <CharacterController>();
            headBob        = transform.Find("Camera").GetComponent <Headbob>();
            lifeHandler    = GetComponent <PlayerLifeHandler>();
            cameraChild    = transform.Find("Camera");

            localViewAnimator = transform.Find("LocalViewAvatarContainer").GetChild(0).GetComponent <Animator>();
            enemyViewAnimator = transform.Find("Avatar").GetComponent <Animator>();
            animParamSpeed    = Animator.StringToHash("MoveSpeed");
            animParamSlide    = Animator.StringToHash("IsSliding");
            animParamClimb    = Animator.StringToHash("IsClimbing");
            animParamVault    = Animator.StringToHash("IsVaulting");
            animParamJump     = Animator.StringToHash("IsJumping");
            animParamHeight   = Animator.StringToHash("PlayerHeight");
            animParamYOffset  = Animator.StringToHash("AvatarYOffset");
            animParamCameraY  = Animator.StringToHash("CameraYOffset");
            animParamCameraZ  = Animator.StringToHash("CameraZOffset");

            RunSpeed            = DefaultRunSpeed;
            velocity            = Vector3.zero;
            midAirChecksEnabled = true;
            wasGrounded         = false;
            hasOverhead         = false;
            previousHasOverhead = false;
            currentMotion       = DefinedMotion.NONE;
            motionTargets       = new List <Vector3>();
        }
예제 #2
0
        public void ResetState()
        {
            currentMotion = DefinedMotion.NONE;
            SetAnimFloat(animParamSpeed, 0.0f);
            SetAnimBool(animParamClimb, false);
            SetAnimBool(animParamSlide, false);
            velocity = Vector3.zero;
            RunSpeed = DefaultRunSpeed;

            FirstPersonCameraVertical cameraVerticalControl = cameraChild.GetComponent <FirstPersonCameraVertical>();

            cameraChild.localRotation = Quaternion.identity;
            cameraVerticalControl.ResetState();
        }
예제 #3
0
        private void UpdateSlideMotion()
        {
            float   movementZ  = 1.0f;
            float   movementX  = InputSplitter.GetHorizontalAxis(PlayerID);
            Vector3 moveVector = new Vector3(movementX, 0, movementZ);

            if (moveVector != Vector3.zero)
            {
                float moveMagnitude = moveVector.magnitude;
                moveVector   /= moveMagnitude;
                moveMagnitude = Mathf.Min(1.0f, moveMagnitude);

                moveVector = (transform.rotation * moveVector) * RunSpeed * moveMagnitude;
            }

            velocity.x = moveVector.x;
            velocity.z = moveVector.z;

            // Apply Gravity
            velocity.y -= 9.81f * Time.fixedDeltaTime;

            // Actually move
            // TODO: Check new velocity after update, we might need to exit slide (e.g if we slide into a wall)
            MoveAndUpdateVelocity();

            if (charController.isGrounded)
            {
                velocity.y = 0.0f;
            }

            // Apply sliding slowdown
            RunSpeed -= SlideDeceleration * Time.fixedDeltaTime;

            // We should stop sliding if we are either:
            //  1) No longer moving fast enough to warrant a slide
            //  2) No longer holding down the slide key
            bool shouldStopSlide = false;

            shouldStopSlide = shouldStopSlide || (RunSpeed < SlideStopSpeedThreshold);
            shouldStopSlide = shouldStopSlide || (!InputSplitter.GetSlide(PlayerID));
            if (shouldStopSlide)
            {
                headBob.enabled = true;

                currentMotion = DefinedMotion.NONE;
                SetAnimBool(animParamSlide, false);
                RunSpeed = DefaultRunSpeed;
            }
        }
예제 #4
0
        /// <summary>
        /// Updates the character based on the current state of hanging
        /// </summary>
        private void UpdateHangMotion()
        {
            if (motionProgress < motionTargets.Count)
            {
                float   motionMoveDistance = 1.5f * ClimbSpeed * Time.fixedDeltaTime;
                Vector3 targetOffset       = motionTargets[motionProgress] - transform.position;
                if (targetOffset.sqrMagnitude < motionMoveDistance * motionMoveDistance)
                {
                    charController.Move(targetOffset);
                    ++motionProgress;

                    if (motionProgress == motionTargets.Count)
                    {
                        SetAnimSpeed(0.0f);
                    }
                }
                else
                {
                    Vector3 targetDirection = targetOffset.normalized;
                    charController.Move(targetDirection * motionMoveDistance);
                }
            }
            else
            {
                bool shouldExit = false;
                if (shouldJump)
                {
                    shouldExit = true;
                    CheckForVaultClimbMotion();
                    if (currentMotion == DefinedMotion.HANG)
                    {
                        Debug.Log("We tried to climb but couldn't! AAAAAAAAAAAHHH (This shouldnt happen =/)");
                    }
                }
                if (shouldSlide)
                {
                    currentMotion = DefinedMotion.NONE;
                    shouldExit    = true;
                }

                if (shouldExit)
                {
                    SetAnimSpeed(1.0f);
                    SetAnimBool(animParamClimb, false);
                    GetComponent <FirstPersonCameraHorizontal>().enabled = true;
                }
            }
        }
예제 #5
0
        private void CheckForSlideMotion()
        {
            float horizontalVelocityThreshold = 1.0f;

            if ((movementZ > 0.1f) &&
                (HorizontalVelocity.sqrMagnitude > horizontalVelocityThreshold *
                 horizontalVelocityThreshold))
            {
                SlideSound.Play();
                hasOverhead   = true;
                currentMotion = DefinedMotion.SLIDE;
                SetAnimBool(animParamSlide, true);
                headBob.enabled      = false;
                transform.localScale = new Vector3(1.0f, 0.5f, 1.0f);
                SetAvatarScale(new Vector3(1.3f, 2.0f * 1.3f, 1.3f));
            }
        }
예제 #6
0
        private void FixedUpdate()
        {
            float   avatarYOffset  = GetAnimFloat(animParamYOffset);
            Vector3 localAvatarLoc = localViewAnimator.transform.localPosition;

            localAvatarLoc.y = avatarYOffset;
            localViewAnimator.transform.localPosition = localAvatarLoc;
            Vector3 enemyAvatarLoc = enemyViewAnimator.transform.localPosition;

            enemyAvatarLoc.y = avatarYOffset;
            enemyViewAnimator.transform.localPosition = enemyAvatarLoc;

            switch (currentMotion)
            {
            case DefinedMotion.NONE:
            {
                previousHasOverhead = hasOverhead;
                hasOverhead         = ((charController.collisionFlags & CollisionFlags.Above) != 0);
                if (!hasOverhead && previousHasOverhead)
                {
                    hasOverhead = Physics.Raycast(transform.position, Vector3.up, 1.8f, 1 << 0);
                    if (!hasOverhead)
                    {
                        transform.localScale = Vector3.one;
                        SetAvatarScale(1.3f * Vector3.one);
                    }
                }
                UpdateOnPlayerInput();
            } break;

            case DefinedMotion.HANG:
            {
                UpdateHangMotion();
            } break;

            case DefinedMotion.VAULT:
            case DefinedMotion.CLIMB:
            {
                bool motionComplete = UpdateVaultClimbMotion();
                if (motionComplete)
                {
                    cameraChild.localPosition = new Vector3(0.0f, 1.7f, 0.0f);
                    headBob.enabled           = true;
                    currentMotion             = DefinedMotion.NONE;
                    SetAnimBool(animParamClimb, false);
                    SetAnimBool(animParamVault, false);
                }
            } break;

            case DefinedMotion.SLIDE:
            {
                UpdateSlideMotion();
            } break;

            default:
                Debug.Log("Attempt to update on unrecognized motion");
                break;
            }

            if (currentMotion == DefinedMotion.NONE)
            {
                SetAnimBool(animParamJump, !charController.isGrounded);
            }
            else
            {
                SetAnimBool(animParamJump, false);
            }
        }
예제 #7
0
        private void CheckForVaultClimbMotion()
        {
            float epsilon         = 0.02f;
            float horizontalSpeed = HorizontalVelocity.magnitude;

            // NOTE: We compute the time it would take the player to jump to the max vault height here
            //       This should prevent 'super-jumps' that come from colliding with an obstacle while
            //       jumping and getting bounced into the air
            float accelerateTerm      = -0.5f * Physics.gravity.y;
            float velocityTerm        = -1.0f * JumpForce;
            float distanceTerm        = MaximumVaultHeight;
            float discriminantSquared = (velocityTerm * velocityTerm) - (4 * accelerateTerm * distanceTerm);
            float discriminant        = Mathf.Sqrt(discriminantSquared);
            float solution1           = (-velocityTerm + discriminant) / (2.0f * accelerateTerm);
            float solution2           = (-velocityTerm - discriminant) / (2.0f * accelerateTerm);

            // NOTE: We're assuming here that we get solutions where sol1 > sol2 and sol1,sol2 > 0
            float vaultCollideTime = solution2;

            float motionCheckTime     = Mathf.Max(ObstacleCheckTime, vaultCollideTime);
            float motionCheckDistance = horizontalSpeed * motionCheckTime;

            motionCheckDistance = Mathf.Max(motionCheckDistance, MinimumObstacleCheckDistance);

            Vector3 currentPosition = transform.position;
            Vector3 forwardDir      = transform.forward;

            int checkLayerMask = ~((1 << 12) | (1 << 11) | (1 << 13) | (1 << 14) | (1 << 15) | (1 << 16));

            RaycastHit vaultCheckInfo;
            Vector3    vaultCheckPosition = currentPosition + new Vector3(0.0f, MinimumObstacleScanHeight, 0.0f);

            Debug.DrawLine(vaultCheckPosition, vaultCheckPosition + (forwardDir * motionCheckDistance), Color.green, 1.0f, false);
            bool canVault = Physics.Raycast(vaultCheckPosition, forwardDir, out vaultCheckInfo, motionCheckDistance, checkLayerMask);

            float      climbCheckHeight = MaximumVaultHeight + epsilon;
            RaycastHit climbCheckInfo;
            Vector3    climbCheckPosition = currentPosition + new Vector3(0.0f, climbCheckHeight, 0.0f);

            Debug.DrawLine(climbCheckPosition, climbCheckPosition + (forwardDir * motionCheckDistance), Color.magenta, 1.0f, false);
            bool canClimb = Physics.Raycast(climbCheckPosition, forwardDir, out climbCheckInfo, motionCheckDistance, checkLayerMask);

            // NOTE: If you can vault and climb 2 different objects then
            //       you clearly cant climb the vault object, so don't climb
            if (canVault && canClimb && (vaultCheckInfo.transform.gameObject != climbCheckInfo.transform.gameObject))
            {
                canClimb = false;
            }

            if (canVault && !canClimb && (horizontalSpeed > MinimumSpeedToVault))
            {
                // TODO: At the moment our vault picks us up, shifts us conveniently over the obstacle, and puts us down nicely on the other side
                //       While this is really neat, it also doesnt feel right, it feels far more like climbing than like anything called a "vault"
                //       The player should be able to be running, and without noticing a difference in speed/smoothness, vault over a low object
                Vector3 vaultCheckPoint       = vaultCheckInfo.point;
                Vector3 vaultCeilingBasePoint = vaultCheckPoint + (forwardDir * epsilon);
                float   vaultHeight           = MaximumVaultHeight;
                if (velocity.y < 0.0f)
                {
                    vaultHeight *= 0.5f;                                              // NOTE: If you're falling you shouldn't be able to jump up very much
                }
                float   vaultCeilingHeight = vaultHeight - MinimumObstacleScanHeight; // We subtract the height from our initial ray
                Vector3 vaultCeiling       = vaultCeilingBasePoint + new Vector3(0.0f, vaultCeilingHeight, 0.0f);

                RaycastHit vaultApexInfo;
                if (Physics.Raycast(vaultCeiling, Vector3.down,
                                    out vaultApexInfo, MaximumVaultHeight))
                {
                    Vector3 vaultApex     = vaultApexInfo.point + new Vector3(0.0f, 0.1f, 0.0f);
                    Vector3 vaultMidpoint = vaultCheckPoint - (forwardDir * charController.radius);
                    vaultMidpoint.y = vaultApex.y;

                    velocity.y = 0.0f; // Reset vertical velocity to zero so we don't do an extra hop afterwards
                    VaultSound.Play();
                    currentMotion = DefinedMotion.VAULT;
                    SetAnimBool(animParamJump, false);
                    SetAnimBool(animParamVault, true);
                    motionProgress = 0;
                    motionTargets.Clear();
                    motionTargets.Add(vaultMidpoint);

                    RaycastHit vaultBackCheckInfo;
                    bool       vaultEndInRange = false;
                    if (MaximumVaultDistance > vaultCheckInfo.distance)
                    {
                        vaultEndInRange = Physics.Raycast(currentPosition + (forwardDir * MaximumVaultDistance),
                                                          -forwardDir, out vaultBackCheckInfo,
                                                          MaximumVaultDistance - vaultCheckInfo.distance);
                    }
                    else
                    {
                        // NOTE: This is a complete hack to get around vaultBackCheckInfo being unassigned
                        vaultBackCheckInfo = new RaycastHit();
                    }

                    // NOTE: If we know where the end of the object is (IE so we can get over it and down the other side)
                    //       Then we move over it in 3 steps (up, along, down). If it has no end (or is too long) then we
                    //       move over it in 2 steps (up, slightly along)
                    if (vaultEndInRange)
                    {
                        Vector3 vaultBackPoint   = vaultBackCheckInfo.point;
                        Vector3 vaultEndMidpoint = vaultBackPoint + (forwardDir * charController.radius);
                        vaultEndMidpoint.y = vaultApex.y;

                        Vector3 vaultStartApexOffset = vaultMidpoint - currentPosition;
                        vaultStartApexOffset.y *= -1.0f;
                        Vector3 vaultEndPoint = vaultEndMidpoint + vaultStartApexOffset;

                        Debug.DrawLine(currentPosition, vaultMidpoint, Color.red, 10.0f, false);
                        Debug.DrawLine(vaultMidpoint, vaultEndMidpoint, Color.red, 10.0f, false);
                        Debug.DrawLine(vaultEndMidpoint, vaultEndPoint, Color.red, 10.0f, false);

                        motionTargets.Add(vaultEndMidpoint);
                        motionTargets.Add(vaultEndPoint);
                    }
                    else
                    {
                        Vector3 vaultEndPoint = vaultMidpoint + (transform.forward * epsilon);

                        motionTargets.Add(vaultEndPoint);
                    }
                }
            }
            else if (canClimb)
            {
                Transform climbCheckTransform = climbCheckInfo.transform;
                Vector3   climbCheckPoint     = climbCheckInfo.point + (forwardDir * epsilon);
                float     climbCeilingHeight  = MaximumClimbHeight - climbCheckHeight; // Subtract the height form our initial ray
                Vector3   climbCeiling        = climbCheckPoint + new Vector3(0.0f, climbCeilingHeight, 0.0f);

                RaycastHit climbCeilingInfo;
                if (Physics.Raycast(climbCeiling, Vector3.down,
                                    out climbCeilingInfo, MaximumClimbHeight))
                {
                    Transform climbCeilingTransform = climbCeilingInfo.transform;
                    if (climbCheckTransform == climbCeilingTransform)
                    {
                        bool shouldHang = !charController.isGrounded;
                        if (!charController.isGrounded)
                        {
                            RaycastHit hangCheckInfo;
                            Vector3    hangCheckOrigin = transform.position + new Vector3(0.0f, HangClimbThresholdHeight, 0.0f);
                            Debug.DrawLine(hangCheckOrigin, hangCheckOrigin + forwardDir * motionCheckDistance, Color.blue, 10.0f, false);
                            if (!Physics.Raycast(hangCheckOrigin, forwardDir, out hangCheckInfo, motionCheckDistance))
                            {
                                shouldHang = false;
                            }
                        }

                        if (shouldHang && (velocity.y < 0.0f) && (velocity.y > -15.0f))
                        {
                            Vector3 hangTarget = climbCheckInfo.point - forwardDir * charController.radius;
                            hangTarget.y = climbCeilingInfo.point.y - charController.height + epsilon;

                            velocity.y    = 0.0f;
                            currentMotion = DefinedMotion.HANG;

                            // NOTE: Here we force the player to look at the position where the raycast hit
                            //       and freeze horizontal rotation, this is to prevent the player not looking
                            //       at the ledge they're hanging on and therefore not being able to climb up
                            // TODO: Check that this actually solves the problem? It kinda seems like it doesnt do
                            //       anything because our raycast goes out in the transform.forward direction anyways
                            Vector3 cameraTargetLoc  = hangTarget + cameraChild.localPosition;
                            Vector3 cameraLookTarget = climbCheckInfo.point;
                            cameraLookTarget.y = cameraTargetLoc.y;
                            Quaternion cameraHangOrientation = Quaternion.LookRotation(cameraLookTarget - cameraTargetLoc, Vector3.up);
                            transform.rotation = cameraHangOrientation;
                            GetComponent <FirstPersonCameraHorizontal>().enabled = false;

                            SetAnimBool(animParamJump, false);
                            SetAnimBool(animParamClimb, true);
                            motionTargets.Clear();
                            motionTargets.Add(hangTarget);
                            motionProgress = 0;

                            midAirChecksEnabled = false;
                        }
                        else if (velocity.y > -epsilon)
                        {
                            Vector3 climbTarget   = climbCeilingInfo.point + new Vector3(0.0f, 0.05f, 0.0f);
                            Vector3 climbMidpoint = climbTarget;
                            climbMidpoint -= forwardDir * epsilon * 10;

                            Debug.DrawLine(currentPosition, climbMidpoint, Color.red, 10.0f, false);
                            Debug.DrawLine(climbMidpoint, climbTarget, Color.red, 10.0f, false);

                            velocity.y = 0.0f;
                            ClimbSound.Play();
                            headBob.enabled = false;
                            currentMotion   = DefinedMotion.CLIMB;
                            SetAnimBool(animParamJump, false);
                            SetAnimBool(animParamClimb, true);
                            motionTargets.Clear();
                            motionTargets.Add(climbMidpoint);
                            motionTargets.Add(climbTarget);
                            motionProgress = 0;
                        }
                    }
                }
            }
        }