using System; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; public class playerMovement : MonoBehaviour { //Assingables public Transform playerCam; public Transform orientation; //Other private Rigidbody rb; //Rotation and look public bool lockLook; private float xRotation; private float sensitivity = 50f; private float sensMultiplier = 1f; public int health, regen; int maxHealth; //Movement public Vector3 inputVector; public float moveSpeed = 4500; public bool grounded, onSlope, fullAirControl; public LayerMask whatIsGround; public float counterMovement = 0.175f; private float threshold = 0.01f; public float maxSlopeAngle = 35f; //Base speed handling public float startBaseSpeed = 15f, baseSpeed, maxBaseSpeed; public float baseSpeedAccel, baseSpeedDeccel; public float bSAccelPoint, bSDeccelPoint, slowDownPoint; public float dragToSlowDown; //Crouch & Slide private Vector3 crouchScale = new Vector3(1, 0.5f, 1); private Vector3 playerScale; public float slideForce = 400; public float slideCounterMovement = 0.2f; public float crouchGravityMultiplier; //Jumping private bool readyToJump = true; private float jumpCooldown = 0.25f; public float jumpForce = 550f; public int startDoubleJumps = 1; int doubleJumpsLeft; //Input public float x, y; bool jumping, sprinting, crouching; //Air contorl public float airForwardForce; //AirDash //Sliding private Vector3 normalVector = Vector3.up; private Vector3 wallNormalVector; public float slopeDownwardForce; //Wallrunning public LayerMask whatIsWall; RaycastHit wallHitR, wallHitL; public bool isWallRight, isWallLeft; public float maxWallrunTime; public float wallrunForce, wallrunUpwardForce, wallSpeedAdd; public int wallJumps, wallJumpsLeft; public bool readyToWallrun, isWallRunning; public bool resetDoubleJumpsOnWall; public GameObject lastWall; //CamTilt public float maxWallRunCameraTilt; public float wallRunCameraTilt = 0; //Climbing public float climbForce, climbSpeedAdd; public LayerMask whatIsLadder; bool alreadyStoppedAtLadder; //Slow-Mo public GameObject slowMoPlane; public float slowMoCooldown, slowMoTime; [Range(0f, 1f)] public float slowMoStrength; // bool readyForSlowMo = true; //Speed Display public float speedRecord; public TextMeshProUGUI baseVelocityDisplay, currentVelocityDisplay, recordVelocityDisplay; void Awake() { rb = GetComponent(); maxHealth = health; baseSpeed = startBaseSpeed; } void Start() { playerScale = transform.localScale; Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; allowDrag = true; } private void FixedUpdate() { Movement(); } private void Update() { MyInput(); if (!lockLook) Look(); CheckForWall(); //Stuff if (Input.GetKeyDown(KeyCode.L)) SceneManager.LoadScene(1); //Trail renderer //if (rb.velocity.magnitude <= 25) GetComponent().startWidth //Set speed display if (currentVelocityDisplay != null) currentVelocityDisplay.SetText("currVel: " + Mathf.RoundToInt(rb.velocity.magnitude)); if (baseVelocityDisplay != null) baseVelocityDisplay.SetText("baseVel: " + baseSpeed); if (recordVelocityDisplay != null) recordVelocityDisplay.SetText("recordVel: " + speedRecord); //new speed record if (speedRecord < rb.velocity.magnitude) speedRecord = Mathf.RoundToInt(rb.velocity.magnitude); //reset Record if (Input.GetKeyDown(KeyCode.R)) speedRecord = 0; } private void MyInput() { x = Input.GetAxisRaw("Horizontal"); y = Input.GetAxisRaw("Vertical"); jumping = Input.GetButton("Jump"); crouching = Input.GetKey(KeyCode.LeftControl); //Crouching if (Input.GetKeyDown(KeyCode.LeftControl)) StartCrouch(); if (Input.GetKeyUp(KeyCode.LeftControl)) StopCrouch(); //If holding jump && ready to jump, then jump if (readyToJump && jumping && grounded) Jump(); //Double Jumping if (Input.GetButtonDown("Jump") && !grounded && doubleJumpsLeft >= 1) { Jump(); } //Wall jumping if(Input.GetButtonDown("Jump") && wallJumpsLeft >= 1 && isWallRunning) { Jump(); } //Dashing //Wallrun if (isWallRight && !grounded && readyToWallrun) StartWallrun(); if (isWallLeft && !grounded && readyToWallrun) StartWallrun(); //reset wallrun if (!isWallRight && !isWallLeft && !readyToWallrun) readyToWallrun = true; //Climbing if (Physics.Raycast(transform.position, orientation.forward, 1, whatIsLadder) && y > .9f) Climb(); else alreadyStoppedAtLadder = false; //Slow-Mo // if (Input.GetKeyDown(KeyCode.LeftControl) && readyForSlowMo) StartSlowMo(); } private void Movement() { //Extra gravity //Needed that the Ground Check works better! float gravityMultiplier = 10f; if (crouching) gravityMultiplier = crouchGravityMultiplier; rb.AddForce(Vector3.down * Time.deltaTime * gravityMultiplier); //Find actual velocity relative to where player is looking Vector2 mag = FindVelRelativeToLook(); float xMag = mag.x, yMag = mag.y; //Counteract sliding and sloppy movement CounterMovement(x, y, mag); //Air Control //if (!grounded) rb.AddForce(orientation.forward * Time.deltaTime * airForwardForce); //ResetStuff when touching ground if (grounded){ doubleJumpsLeft = startDoubleJumps; wallJumpsLeft = wallJumps; } //Set max speed float maxSpeed = this.baseSpeed; if (crouching && grounded && readyToJump) { rb.AddForce(Vector3.down * Time.deltaTime * 3000); return; } //Build up speed on slope if (crouching && onSlope) { rb.AddForce(Vector3.down * Time.deltaTime * slopeDownwardForce); } //If speed is larger than maxSpeed, cancel out the input so you don't go over max speed if (x > 0 && xMag > maxSpeed) x = 0; if (x < 0 && xMag < -maxSpeed) x = 0; if (y > 0 && yMag > maxSpeed) y = 0; if (y < 0 && yMag < -maxSpeed) y = 0; //Slow down, don't allow going to fast ///if (rb.velocity.magnitude > baseSpeed) SlowDown(); //Some multipliers float multiplier = 1f, multiplierV = 1f; // Movement in air if (!grounded && !fullAirControl) { multiplier = 0.5f; multiplierV = 0.5f; } if (fullAirControl) { multiplier = 0.35f; } // Movement while sliding if (grounded && crouching) multiplierV = 0f; //Apply forces to move player rb.AddForce(orientation.transform.forward * y * moveSpeed * Time.deltaTime * multiplier * multiplierV); rb.AddForce(orientation.transform.right * x * moveSpeed * Time.deltaTime * multiplier); //Base speed handling (old ///if (rb.velocity.magnitude > baseSpeed - 3) IncreaseBaseSpeed(); ///else if (baseSpeed > startBaseSpeed) DecreaseBaseSpeed(); //Base speed handling if (rb.velocity.magnitude > baseSpeed + bSAccelPoint) IncreaseBaseSpeed(); if (rb.velocity.magnitude < baseSpeed - bSDeccelPoint) DecreaseBaseSpeed(); //Slow down if curr vel reaches slowDownPoint if (rb.velocity.magnitude > baseSpeed + slowDownPoint) SlowDown(); else rb.drag = 0; } private void StartCrouch() { transform.localScale = crouchScale; transform.position = new Vector3(transform.position.x, transform.position.y - 0.5f, transform.position.z); if (rb.velocity.magnitude > 0.5f) { if (grounded) { rb.AddForce(orientation.transform.forward * slideForce); } } } private void StopCrouch() { transform.localScale = playerScale; transform.position = new Vector3(transform.position.x, transform.position.y + 0.5f, transform.position.z); } private void Jump() { if (grounded) { readyToJump = false; //Add jump forces rb.AddForce(Vector3.up * jumpForce * 1.5f); rb.AddForce(normalVector * jumpForce * 0.5f); //If jumping while falling, reset y velocity. Vector3 vel = rb.velocity; if (rb.velocity.y < 0.5f) rb.velocity = new Vector3(vel.x, 0, vel.z); else if (rb.velocity.y > 0) rb.velocity = new Vector3(vel.x, vel.y / 2, vel.z); Invoke(nameof(ResetJump), jumpCooldown); } if (!grounded && !isWallRunning && doubleJumpsLeft >= 1){ readyToJump = false; doubleJumpsLeft--; //Debug.Log("DoubleJump"); //Add jump forces rb.AddForce(orientation.forward * jumpForce * 1f); rb.AddForce(Vector2.up * jumpForce * 1.7f); rb.AddForce(normalVector * jumpForce * 0.7f); //Dampen y velocity rb.velocity = new Vector3(rb.velocity.x, rb.velocity.y * 0.4f, rb.velocity.z); Invoke(nameof(ResetJump), jumpCooldown); } //Walljump if (isWallRunning && wallJumpsLeft >= 1) { //Debug.Log("WallJump"); readyToJump = false; wallJumpsLeft--; //normal jump rb.AddForce(Vector2.up * jumpForce * 0.85f); rb.AddForce(normalVector * jumpForce * 0.5f); rb.AddForce(orientation.forward * jumpForce * 0.5f); if (isWallRight) rb.AddForce(-orientation.right * jumpForce * 1.5f); if (isWallLeft) rb.AddForce(orientation.right * jumpForce * 1.5f); /*sidwards wallhop if (isWallRight||isWallLeft && Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D)) rb.AddForce(-orientation.up * jumpForce * 1f); if (isWallRight && Input.GetKey(KeyCode.A)) rb.AddForce(-orientation.right * jumpForce * 3.2f); if (isWallLeft && Input.GetKey(KeyCode.D)) rb.AddForce(orientation.right * jumpForce * 3.2f); */ //Always add forward force rb.AddForce(orientation.forward * jumpForce * 1f); //Reset Velocity rb.velocity = Vector3.zero; Invoke(nameof(ResetJump), jumpCooldown); } } private void ResetJump() { readyToJump = true; } float elapsedWallTime; private void StartWallrun() { //Debug.Log("Wallrunning"); //When to stop if (grounded) StopWallRun(); //Count up timer elapsedWallTime += Time.deltaTime; //Leave wall run when timer reaches maximum if (elapsedWallTime > maxWallrunTime) { //StopWallRun(); } //rb.useGravity = false; isWallRunning = true; //Add upward force rb.AddForce(orientation.up * wallrunUpwardForce * Time.deltaTime); if (rb.velocity.magnitude <= baseSpeed + wallSpeedAdd) { rb.AddForce(orientation.forward * wallrunForce * Time.deltaTime); //Make sure char sticks to wall if (isWallRight) rb.AddForce(orientation.right * wallrunForce / 5 * Time.deltaTime); else rb.AddForce(-orientation.right * wallrunForce / 5* Time.deltaTime); } } private void StopWallRun() { isWallRunning = false; readyToWallrun = false; //Reset timer elapsedWallTime = 0; } private void CheckForWall() { isWallRight = Physics.Raycast(transform.position, orientation.right, out wallHitR, 1f, whatIsGround); isWallLeft = Physics.Raycast(transform.position, -orientation.right, out wallHitL, 1f, whatIsGround); //if (!isWallLeft && !isWallRight) wallJumpsLeft = wallJumps; if (!isWallLeft && !isWallRight && isWallRunning) StopWallRun(); if ((isWallLeft || isWallRight) && resetDoubleJumpsOnWall) doubleJumpsLeft = startDoubleJumps; } private void Climb() { //Makes possible to climb even when falling down fast Vector3 vel = rb.velocity; if (rb.velocity.y < 0.5f && !alreadyStoppedAtLadder){ rb.velocity = new Vector3(vel.x, 0, vel.z); //Make sure char get's at wall alreadyStoppedAtLadder = true; rb.AddForce(orientation.forward * 500 * Time.deltaTime); } //Push character up if (rb.velocity.magnitude < baseSpeed + climbSpeedAdd) rb.AddForce(orientation.up * climbForce * Time.deltaTime); //Doesn't Push into the wall if (!Input.GetKey(KeyCode.S)) y = 0; } private void StartSlowMo() { // readyForSlowMo = false; slowMoPlane.SetActive(true); Time.timeScale = slowMoStrength; Invoke(nameof(StopSlowMo), slowMoTime * slowMoStrength); } private void StopSlowMo() { slowMoPlane.SetActive(false); Time.timeScale = 1f; Invoke(nameof(ResetSlowMo), slowMoCooldown); } private void ResetSlowMo() { // readyForSlowMo = true; } private float desiredX; private void Look() { float mouseX = Input.GetAxis("Mouse X") * sensitivity * Time.fixedDeltaTime * sensMultiplier; float mouseY = Input.GetAxis("Mouse Y") * sensitivity * Time.fixedDeltaTime * sensMultiplier; //Find current look rotation Vector3 rot = playerCam.transform.localRotation.eulerAngles; desiredX = rot.y + mouseX; //Rotate, and also make sure we dont over- or under-rotate. xRotation -= mouseY; xRotation = Mathf.Clamp(xRotation, -90f, 90f); //Perform the rotations playerCam.transform.localRotation = Quaternion.Euler(xRotation, desiredX, wallRunCameraTilt); orientation.transform.localRotation = Quaternion.Euler(0, desiredX, 0); //While Wallrunning //Tilts camera in .5 second if (Math.Abs(wallRunCameraTilt) < maxWallRunCameraTilt && isWallRunning && isWallRight) wallRunCameraTilt += Time.deltaTime * maxWallRunCameraTilt * 2; if (Math.Abs(wallRunCameraTilt) < maxWallRunCameraTilt && isWallRunning && isWallLeft) wallRunCameraTilt -= Time.deltaTime * maxWallRunCameraTilt * 2; //Tilts camera back again if (wallRunCameraTilt > 0 && !isWallRight && !isWallLeft) wallRunCameraTilt -= Time.deltaTime * maxWallRunCameraTilt * 2; if (wallRunCameraTilt < 0 && !isWallRight && !isWallLeft) wallRunCameraTilt += Time.deltaTime * maxWallRunCameraTilt * 2; } float timer1, timer2; float extraBaseDeccel; //Exponentially decrease base speed private void IncreaseBaseSpeed() { if (baseSpeed >= maxBaseSpeed) return; ///Debug.Log("Decreasing BaseSpeed"); //Only increase in .1 ticks timer1 += Time.deltaTime * baseSpeedAccel; extraBaseDeccel = 0; if (timer1 > 1f) { baseSpeed += 0.1f; timer1 = 0; } } private void DecreaseBaseSpeed() { if (baseSpeed <= startBaseSpeed) return; ///Debug.Log("Increasing BaseSpeed"); //Only decrease in .1 ticks timer2 += Time.deltaTime * baseSpeedDeccel * extraBaseDeccel; extraBaseDeccel += Time.deltaTime * 0.5f; if (timer2 > 1f) { baseSpeed -= 0.1f; timer2 = 0; } } private bool allowDrag = true; private void SlowDown() { //Debug.Log("SlowingDown"); ///Vector3 baseVelVector = rb.velocity.normalized * baseSpeed; ///rb.AddForce(-rb.velocity * 1f * Time.deltaTime, ForceMode.Impulse); //Debug.Log("Drag = 1"); if (allowDrag) rb.drag = 1; } private void CounterMovement(float x, float y, Vector2 mag) { /*Limit diagonal running. Only when holding down W and D or W and A if (x != 0 && y != 0) { if (Mathf.Sqrt((Mathf.Pow(rb.velocity.x, 2) + Mathf.Pow(rb.velocity.z, 2))) > baseSpeed) { float fallspeed = rb.velocity.y; Vector3 n = rb.velocity.normalized * baseSpeed; rb.velocity = new Vector3(n.x, fallspeed, n.z); } } */ //if (!grounded || jumping) return; if (!grounded || jumping || isWallRunning) return; //Slow down sliding if (crouching) { rb.AddForce(moveSpeed * Time.deltaTime * -rb.velocity.normalized * slideCounterMovement); return; } //Counter movement: ///Counters when no Input and still moving || Input in opposite direction then velocity if (Math.Abs(mag.x) > threshold && Math.Abs(x) < 0.05f || (mag.x < -threshold && x > 0) || (mag.x > threshold && x < 0)) { rb.AddForce(moveSpeed * orientation.transform.right * Time.deltaTime * -mag.x * counterMovement); } if (Math.Abs(mag.y) > threshold && Math.Abs(y) < 0.05f || (mag.y < -threshold && y > 0) || (mag.y > threshold && y < 0)) { rb.AddForce(moveSpeed * orientation.transform.forward * Time.deltaTime * -mag.y * counterMovement); } //Limit diagonal running. This will also cause a full stop if sliding fast and un-crouching, so not optimal. if (Mathf.Sqrt((Mathf.Pow(rb.velocity.x, 2) + Mathf.Pow(rb.velocity.z, 2))) > baseSpeed) { float fallspeed = rb.velocity.y; Vector3 n = rb.velocity.normalized * baseSpeed; rb.velocity = new Vector3(n.x, fallspeed, n.z); } } /// Find the velocity relative to where the player is looking /// Useful for vectors calculations regarding movement and limiting movement public Vector2 FindVelRelativeToLook() { float lookAngle = orientation.transform.eulerAngles.y; float moveAngle = Mathf.Atan2(rb.velocity.x, rb.velocity.z) * Mathf.Rad2Deg; float u = Mathf.DeltaAngle(lookAngle, moveAngle); float v = 90 - u; float magnitue = rb.velocity.magnitude; float yMag = magnitue * Mathf.Cos(u * Mathf.Deg2Rad); float xMag = magnitue * Mathf.Cos(v * Mathf.Deg2Rad); return new Vector2(xMag, yMag); } private bool IsFloor(Vector3 v) { float angle = Vector3.Angle(Vector3.up, v); return angle < maxSlopeAngle; } private bool cancellingGrounded; /// Handle ground detection private void OnCollisionEnter(Collision collision) { //Debug.Log(Vector3.Angle(transform.up, collision.contacts[0].normal)); } private void OnCollisionStay(Collision other) { //Make sure we are only checking for walkable layers int layer = other.gameObject.layer; if (whatIsGround != (whatIsGround | (1 << layer))) return; //Iterate through every collision in a physics update for (int i = 0; i < other.contactCount; i++) { Vector3 normal = other.contacts[i].normal; //FLOOR if (IsFloor(normal)) { onSlope = false; grounded = true; cancellingGrounded = false; normalVector = normal; CancelInvoke(nameof(StopGrounded)); } else { onSlope = true; } //Save lastWall if (isWallRunning) { if (lastWall != other.gameObject) { // Debug.Log("WallChanged!"); lastWall = other.gameObject; wallJumpsLeft = wallJumps; } } } //Invoke ground/wall cancel, since we can't check normals with CollisionExit float delay = 3f; if (!cancellingGrounded) { cancellingGrounded = true; Invoke(nameof(StopGrounded), Time.deltaTime * delay); } } private void StopGrounded() { grounded = false; } #region abilityFunctions public void DashInDirection(Vector3 dir, float force) { rb.AddForce(dir * force, ForceMode.Impulse); } public void PreventDrag(float time) { allowDrag = false; Invoke(nameof(ResetAllowDrag), time); } private void ResetAllowDrag() { allowDrag = true; } #endregion }