using System.Collections; using System.Collections.Generic; using UnityEngine; using static scr_Models; // allows you to use data members of all classes in the scr_Models script public class scr_CharacterController : MonoBehaviour { private CharacterController characterController; // a reference to the character controller component (assigned a value in the awake method) private DefaultInput defaultInput; // makes a variable of type DefaultInput called defaultInput private Rigidbody rb; // a reference to the Rigidbody component of the Player CursorLockMode lockMode; // a variable for storing the CursorLockMode [Header("Input Monitoring")] [Tooltip("Used to monitor the raw input of movement.")] [ReadOnly] public Vector2 input_Movement; // use to monitor raw input [Tooltip("Used to monitor the raw input of viewing.")] [ReadOnly] public Vector2 input_View; // use to monitor raw input private Vector3 newCameraRotation; // the rotation the cameraHolder will be set to in CalculateView private Vector3 newCharacterRotation; // the rotation the character will be set to in CalculateView [Header("References")] [Tooltip("A reference to the cameraHolder component of the player.")] public Transform cameraHolder; // a reference to the CameraHolder component of the Player [Tooltip("A reference to the feetTransform component of the player.")] public Transform feetTransform; // a reference to the FeetTransform component of the Player [Header("View and Movement Settings")] [Tooltip("A reference to the PlayerSettingsModel class in the scr_Models script.")] public PlayerSettingsModel playerSettings; // a reference to the PlayerSettingsModel class in the scr_Models script [Tooltip("The lowest you can look.")] public float viewClampYMin = -70; // the lowest you can look [Tooltip("The highest you can look.")] public float viewClampYMax = 80; // the highest you can look [Tooltip("Used to exclude the player.")] public LayerMask playerMask; // used to exclude the player [Tooltip("NOTE: For monitoring purposes only. To be made private later.")] [ReadOnly] public Vector3 movementSpeed; // the variable the player actually moves based off of in CalculateMovement private Vector3 newMovementSpeed; // used in the CalculateMovement method to help calculate movementSpeed private Vector3 newMovementSpeedVelocity; [Header("Weapon")] public scr_WeaponController currentWeapon; // creates a reference to our weapon controller [Header("Gravity Settings")] [Tooltip("How much gravity is applied.")] public float gravityAmount; // how much gravity is applied [Tooltip("The minimum that gravity can reach, i.e. terminal velocity essentially.")] public float gravityMin; // the minimum that gravity can reach, i.e. terminal velocity essentially [Tooltip("The current amount of gravity the character is experiencing. Used for monitoring only.")] [ReadOnly] public float characterGravity; // the current amount of gravity the character is experiencing [Header("Jumping Force Settings and Monitoring")] [Tooltip("Force applied whenever the character jumps.")] [ReadOnly] public Vector3 jumpingForce; // declares a Vector3 class called jumpingForce private Vector3 jumpingForceVelocity; [Header("Dash Settings and Monitoring")] [Tooltip("How much you dash, i.e. distance or speed.")] public float dashForce; // for Dash method [Tooltip("How long the dash lasts for in seconds.")] public float dashDuration; // for Dash method private bool isDashing = false; // for Dash method [Header("Stance Settings")] [Tooltip("An enum representing the current stance of the player.")] public PlayerStance playerStance; [Tooltip("Time it takes to transition between stances.")] public float playerStanceSmoothing; [Tooltip("A variable of type CharacterStance containing information related to standing.")] public CharacterStance playerStandStance; // creates a CharacterStance class variable for standing [Tooltip("A variable of type CharacterStance containing information related to crouching.")] public CharacterStance playerCrouchStance; // creates a CharacterStance class variable for crouching private float stanceCheckErrorMargin = 0.05f; private float cameraHeight; // The height the cameraHolder.localPosition.y will be set to based on your stance (CameraStandHeight or CameraCrouchHeight) private float cameraHeightVelocity; private Vector3 stanceCapsuleCenterVelocity; private float stanceCapsuleHeightVelocity; private void Awake() { lockMode = CursorLockMode.Locked; Cursor.lockState = lockMode; #region -- INPUTS defaultInput = new DefaultInput(); // instantiates the defaultInput defaultInput.Character.Movement.performed += e => input_Movement = e.ReadValue(); // sets the value of input_Movement based on WSAD input defaultInput.Character.View.performed += e => input_View = e.ReadValue(); // sets the value of input_View based on raw mouse input defaultInput.Character.Jump.performed += e => Jump(); // calls the Jump function when the Jump action from the input controller is performed defaultInput.Character.Crouch.performed += e => Crouch(); // calls the Crouch function when the Crouch action from the input controller is performed defaultInput.Character.Dash.performed += e => Dash(); // calls the Dash function when the Dash action from the input controller is performed defaultInput.Enable(); // inputs won't work until defaultInput is enabled #endregion newCameraRotation = cameraHolder.localRotation.eulerAngles; // sets the initial value of newCameraRotation (which is basically just the cameraHolder's default rotation) newCharacterRotation = transform.localRotation.eulerAngles; // sets the initial value of newCharacterRotation characterController = GetComponent(); // assigns characterController variable an actual value of the CharacterController component rb = GetComponent(); // assigns rb variable an actual value of the Rigidbody component cameraHeight = cameraHolder.localPosition.y; // may not be necessary; sets the initial value of cameraHeight // initializes the currentWeapon; assigns this scr_CharacterController to be characterController in currentWeapon if (currentWeapon) { currentWeapon.Initialize(this); } } private void Update() { CalculateView(); CalculateMovement(); CalculateJump(); CalculateStance(); } private void CalculateView() { newCharacterRotation.y += playerSettings.ViewXSensitivity * (playerSettings.ViewXInverted ? -input_View.x : input_View.x) * Time.deltaTime; // calculates left and right float of newCharacterRotation transform.localRotation = Quaternion.Euler(newCharacterRotation); // sets the local rotation of the character (which is a quaternion) to be equal to newCharacterRotation (which is a Vector3) converted into a quaternion newCameraRotation.x += playerSettings.ViewYSensitivity * (playerSettings.ViewYInverted ? input_View.y : -input_View.y) * Time.deltaTime; // calculates up and down float of newCameraRotation newCameraRotation.x = Mathf.Clamp(newCameraRotation.x, viewClampYMin, viewClampYMax); // clamps up and down rotation cameraHolder.localRotation = Quaternion.Euler(newCameraRotation); // sets the local rotation of the cameraHolder (which is a quaternion) to be equal to newCameraRotation (which is a Vector3) converted into a quaternion } // sets the value of movementSpeed, which is what moves our character. private void CalculateMovement() { var verticalSpeed = playerSettings.WalkingForwardSpeed; // local float called verticalSpeed given default value var horizontalSpeed = playerSettings.WalkingStrafeSpeed; // local float called horizontalSpeed given default value #region -- Effectors if (!characterController.isGrounded) // if you aren't grounded, have the FallingSpeedEffector be in place { playerSettings.SpeedEffector = playerSettings.FallingSpeedEffector; } else if(playerStance == PlayerStance.Crouch) // if you are crouched, have the CrouchSpeedEffector be in place { playerSettings.SpeedEffector = playerSettings.CrouchSpeedEffector; } else { playerSettings.SpeedEffector = 1; } verticalSpeed *= playerSettings.SpeedEffector; // has SpeedEffector affect verticalSpeed horizontalSpeed *= playerSettings.SpeedEffector; // has SpeedEffector affect horizontalSpeed #endregion #region -- Calculate newMovementSpeed Vector3 and local targetMovementSpeed Vector3 Vector3 targetMovementSpeed = new Vector3(horizontalSpeed * input_Movement.x * Time.deltaTime, 0, verticalSpeed * input_Movement.y * Time.deltaTime); // makes a new local Vector3 instance called targetMovementSpeed using horizontalSpeed and verticalSpeed newMovementSpeed = Vector3.SmoothDamp(newMovementSpeed, targetMovementSpeed, ref newMovementSpeedVelocity, characterController.isGrounded ? playerSettings.MovementSmoothing : playerSettings.FallingSmoothing); // makes newMovementSpeed's value smoothdamp to targetMovementSpeed (also checks to see if you are grounded so it knows whether to use MovementSmoothing or FallingSmoothing #endregion movementSpeed = transform.TransformDirection(newMovementSpeed); // makes a new local variable called movementSpeed and makes it equal to newMovementSpeed but relative to the player's rotation #region --Calculate characterGravity // if character hasn't reached terminal velocity, keep reducing their gravity if (characterGravity > gravityMin) { characterGravity -= gravityAmount * Time.deltaTime; } // so gravity doesn't grow to a crazy value when the player is grounded, so we only increase while in the air if (characterGravity < -0.1f && characterController.isGrounded) { characterGravity = -0.1f; // ensures we will stick to the ground but won't clip through it due to too much gravity } #endregion movementSpeed.y += characterGravity; // has characterGravity affect the y axis of movementSpeed movementSpeed += jumpingForce * Time.deltaTime; // has jumpingForce affect movementSpeed playerSettings.CurrentMovementSpeed = new Vector3(playerSettings.WalkingStrafeSpeed * input_Movement.x, 0, playerSettings.WalkingForwardSpeed * input_Movement.y); // just for monitoring your current movement speed // IMPORTANT: What actually moves the character. if(!isDashing) { characterController.Move(movementSpeed); // actually moves the character based on movementSpeed } } // calculates jumpingForce based off of a SmoothDamp function private void CalculateJump() { jumpingForce = Vector3.SmoothDamp(jumpingForce, Vector3.zero, ref jumpingForceVelocity, playerSettings.JumpingFalloff); } // sets the local variable (of type CharacterStance) currentStance based on playerStance enum // calculates cameraHeight variable and has it affect the cameraHolder.localPosition // makes the height and center of the characterController match the height and center of your currentStance.StanceCollider private void CalculateStance() { var currentStance = playerStandStance; // makes a local CharacterStance class variable called currentStance and gives it a default value of playerStandStance // if playerStance enum is set to crouch, set currentStance to be equal to playerCrouchStance if (playerStance == PlayerStance.Crouch) { currentStance = playerCrouchStance; } cameraHeight = Mathf.SmoothDamp(cameraHolder.localPosition.y, currentStance.CameraHeight, ref cameraHeightVelocity, playerStanceSmoothing); // calculates cameraHeight cameraHolder.localPosition = new Vector3(cameraHolder.localPosition.x, cameraHeight, cameraHolder.localPosition.z); // has cameraHeight affect the cameraHolder's local position characterController.height = Mathf.SmoothDamp(characterController.height, currentStance.StanceCollider.height, ref stanceCapsuleHeightVelocity, playerStanceSmoothing); // changes the height of the character controller to match the currentStance.StanceCollider.height characterController.center = Vector3.SmoothDamp(characterController.center, currentStance.StanceCollider.center, ref stanceCapsuleCenterVelocity, playerStanceSmoothing); // changes the center of the character controller to match the currentStance.StanceCollider.center } // makes the character jump or stand up based on their current playerStance // makes the character jump by changing the jumpingForce and characterGravity private void Jump() { // if you are crouching and you choose the jump action, stand instead if (playerStance == PlayerStance.Crouch) { Crouch(); return; } // if the character is not grounded, don't jump if (!characterController.isGrounded) { return; // if not grounded, do not perform anything else of the Jump method (so do not Jump) } // Jump script jumpingForce = Vector3.up * playerSettings.JumpingHeight; // sets jumpingForce based on JumpingHeight characterGravity = 0; // when jumping, set characterGravity to 0 } // toggles the playerStance private void Crouch() { // if the character is not grounded, don't allow them to manually change their stance if (!characterController.isGrounded) { return; // if not grounded, don't do anything } // if the player is standing when they press crouch, make them crouch if (playerStance == PlayerStance.Stand) { playerStance = PlayerStance.Crouch; return; } // if the player is crouching when they press crouch, make them stand if (playerStance == PlayerStance.Crouch) { // if there will be a collision if you stand if(StanceCheck(playerStandStance.StanceCollider.height)) { return; } else // if there won't be a collision, make the player stand { playerStance = PlayerStance.Stand; } } } // tests to see if there will be an obstruction above you if you stand up private bool StanceCheck(float stanceCheckHeight) { var start = new Vector3(feetTransform.position.x, feetTransform.position.y + characterController.radius + stanceCheckErrorMargin, feetTransform.position.z); var end = new Vector3(feetTransform.position.x, feetTransform.position.y - characterController.radius - stanceCheckErrorMargin + stanceCheckHeight, feetTransform.position.z); return Physics.CheckCapsule(start, end, characterController.radius, playerMask); } private void Dash() { // if you are crouching, don't dash if (playerStance == PlayerStance.Crouch) { Debug.Log("Cannot dash. You are crouching."); return; } else { // Dashing script // NOTE: Uses "coroutines". "a coroutine is a method that can pause execution and return control to Unity but then continue where it left off on the following frame" var currentInput = new Vector3(input_Movement.x, 0, input_Movement.y); // the current left and right input as a Vector3 // if you aren't providing any movement input, simply have the dash move forward if (currentInput == Vector3.zero) { currentInput = Vector3.forward; //Debug.Log("No input."); } StartCoroutine(DashingCoroutine()); // declares the start of the DashingCoroutine IEnumerator DashingCoroutine() // defines the DashingCoroutine method { isDashing = true; // sets isDashing to true rb.AddForce(transform.TransformDirection(currentInput) * dashForce, ForceMode.VelocityChange); // applies the force to rb Debug.Log(currentInput); //Debug.Log("You are dashing."); yield return new WaitForSeconds(dashDuration); // makes the code wait dashDuration in seconds before executing the rest rb.velocity = Vector3.zero; // stops the rb from moving isDashing = false; // sets isDashing to false } } } }