From 611fe6c01454af80c9cf875c7ace6b566c37101e Mon Sep 17 00:00:00 2001 From: Pasha Bibko Date: Fri, 28 Mar 2025 11:25:24 +0000 Subject: [PATCH] Added wall running --- Assets/DefaultPhysicsMat.physicMaterial | 4 +- Assets/Scenes/SampleScene.unity | 6 +- Assets/Scripts/Player/PlayerMovement.cs | 244 ++++++++++++++++-------- 3 files changed, 171 insertions(+), 83 deletions(-) diff --git a/Assets/DefaultPhysicsMat.physicMaterial b/Assets/DefaultPhysicsMat.physicMaterial index 27d8092..b37d49a 100644 --- a/Assets/DefaultPhysicsMat.physicMaterial +++ b/Assets/DefaultPhysicsMat.physicMaterial @@ -10,5 +10,5 @@ PhysicMaterial: dynamicFriction: 0 staticFriction: 0 bounciness: 0 - frictionCombine: 0 - bounceCombine: 0 + frictionCombine: 1 + bounceCombine: 1 diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index ddf515a..725e55d 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -615,6 +615,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: ae4c295274f6a0645b04723c79481e73, type: 3} m_Name: m_EditorClassIdentifier: + m_State: 0 m_MoveSpeed: 7 m_GroundDrag: 5 m_AirDrag: 4 @@ -629,7 +630,10 @@ MonoBehaviour: m_SlideScaler: 0.6 m_SlideDrag: 3 m_SlideSpeed: 10 - m_JumpForce: 10 + m_JumpForce: 20 + m_WallRunSpeed: 10 + m_WallCheckDistance: 1.5 + m_DistanceOfFloorToWallRide: 0.1 m_JumpKey: 32 m_SlideKey: 99 m_Body: {fileID: 1174770952} diff --git a/Assets/Scripts/Player/PlayerMovement.cs b/Assets/Scripts/Player/PlayerMovement.cs index c6ec790..710dffb 100644 --- a/Assets/Scripts/Player/PlayerMovement.cs +++ b/Assets/Scripts/Player/PlayerMovement.cs @@ -1,8 +1,18 @@ using UnityEngine; using UnityEngine.UI; +public enum PlayerState +{ + RUNNING, + SLIDING, + WALL_RUNNING +} + public class PlayerMovement : MonoBehaviour { + [Header("VIEWABLE ONLY")] + [SerializeField] PlayerState m_State = PlayerState.RUNNING; + [Header("General Settings")] [SerializeField] float m_MoveSpeed; [SerializeField] float m_GroundDrag; @@ -22,6 +32,11 @@ public class PlayerMovement : MonoBehaviour [Header("Jump Settings")] [SerializeField] float m_JumpForce; + [Header("Wall Run Settings")] + [SerializeField] float m_WallRunSpeed; + [SerializeField] float m_WallCheckDistance; + [SerializeField] float m_DistanceOfFloorToWallRide; + [Header("KeyBinds")] [SerializeField] KeyCode m_JumpKey; [SerializeField] KeyCode m_SlideKey; @@ -37,20 +52,32 @@ public class PlayerMovement : MonoBehaviour // Current direction the user has inputted Vector2 m_Input; - // The direction to move the player - Vector3 m_MoveDir; - - // Player state tracker - bool m_Grounded = false; - bool m_OnSlope = false; - bool m_Sliding = false; - + // Key state trackers bool m_JumpKeyPressed = false; bool m_SlidingKeyPressed = false; + // The direction to move the player + Vector3 m_MoveDir; + + // Player state trackers + bool m_Grounded = false; + bool m_OnSlope = false; + + // Trackers for the walls + bool m_HitLhsWall = false; + bool m_HitRhsWall = false; + + // Tracks if the distance of the ground is big enough + bool m_IsFarEnoughOffGroundToWallRide = false; + + // Timer for slide boost duration left int m_TicksOfSlideBoostLeft = 0; + // Raycast hit objects + RaycastHit m_GroundHit; RaycastHit m_SlopeHit; + RaycastHit m_LhsWall; + RaycastHit m_RhsWall; // Start is called before the first frame update private void Start() @@ -76,22 +103,20 @@ public class PlayerMovement : MonoBehaviour // Applies drag to the player private void ApplyDrag() { - // Only applies ground drag if the player is on the floor - if (m_Grounded) + switch (m_State) { - m_Body.drag = m_GroundDrag; - } + case PlayerState.SLIDING: + m_Body.drag = m_SlideDrag; + break; - // Applies sliding drag if sliding <- Very useful comment - if (m_Sliding) - { - m_Body.drag = m_SlideDrag; - } + default: + // Applies different drag depending on if the player is on the ground or not + if (m_Grounded) + { m_Body.drag = m_GroundDrag; } + else + { m_Body.drag = m_AirDrag; } - // Else it applies the air drag to the player - else - { - m_Body.drag = m_AirDrag; + break; } } @@ -99,9 +124,16 @@ public class PlayerMovement : MonoBehaviour private void Update() { // Performs raycasts to see what the player is standing on - m_Grounded = Physics.Raycast(transform.position, Vector3.down, m_PlayerHeight * 0.5f + 0.3f, m_GroundMask); + m_Grounded = Physics.Raycast(transform.position, Vector3.down, out m_GroundHit, m_PlayerHeight * 0.5f + 0.3f, m_GroundMask); m_OnSlope = Physics.Raycast(transform.position, Vector3.down, out m_SlopeHit, m_PlayerHeight * 0.5f + 0.3f, m_SlopeMask); + // Checks for walls either side of the player + m_HitLhsWall = Physics.Raycast(transform.position, m_Orientation.right, out m_LhsWall, m_WallCheckDistance, m_GroundMask); + m_HitRhsWall = Physics.Raycast(transform.position, -m_Orientation.right, out m_RhsWall, m_WallCheckDistance, m_GroundMask); + + // Checks the player is far enough of the ground to start wall running + m_IsFarEnoughOffGroundToWallRide = m_GroundHit.distance > m_DistanceOfFloorToWallRide; + // Updates the state of the user input UpdateInput(); @@ -112,24 +144,6 @@ public class PlayerMovement : MonoBehaviour m_SpeedDisplay.text = "Speed: " + m_Body.velocity.magnitude.ToString("0.00"); } - // Updates basic movement and player jumping - private void UpdatePlayerPosition() - { - // Sliding has its own movement code so the force being applied here is not needed - if (m_Sliding == false) - { - // Adds the force to the rigid body - m_Body.AddForce(m_MoveDir.normalized * m_MoveSpeed * m_Body.mass * 10.0f, ForceMode.Force); - } - - // Jumps if the jump key has been pressed - if (m_JumpKeyPressed) - { - // The jump function stops jumping if not grounded so no check is needed here - Jump(); - } - } - // Handles the logic for starting to slide private void StartSlide() { @@ -156,37 +170,8 @@ public class PlayerMovement : MonoBehaviour // Function to manage the sliding of the player private void UpdateSlidingState() { - // Works out wether the player's velocity is high enough to slide - Vector3 vel = m_Body.velocity; - bool canSlide = - !( - Mathf.Abs(vel.x) < m_SlideRequiredSpeed && - Mathf.Abs(vel.z) < m_SlideRequiredSpeed - ) || true; - - // Checks wether the key state is valid for starting a slide - if (m_SlidingKeyPressed == true && m_Sliding == false) - { - // Checks player is moving in a direction - if (canSlide) - { - m_Sliding = true; // Updates the sliding state - - StartSlide(); - } - } - - // Checks wether the player has stopped a slide or - // the player sliding if they are moving too slow - else if ((m_SlidingKeyPressed == false && m_Sliding == true) || (canSlide == false && m_Sliding == true)) - { - m_Sliding = false; // Updates the sliding state - - StopSlide(); - } - // Correctly applies force on slopes - if (m_Sliding && m_OnSlope) + if (m_OnSlope) { Vector3 slopeDir = m_SlopeHit.normal; slopeDir.y = 0.0f - slopeDir.y; @@ -198,6 +183,25 @@ public class PlayerMovement : MonoBehaviour { m_Body.AddForce(m_MoveDir.normalized * m_SlideSpeed * m_Body.mass * 10, ForceMode.Force); } + + //m_Body.AddForce(Vector3.down * m_Body.mass * 5.0f, ForceMode.Impulse); + } + + private void UpdateWallRunState() + { + // Calculates the foward direction of the wall + Vector3 normal = m_HitRhsWall ? m_RhsWall.normal : m_LhsWall.normal; + Vector3 foward = Vector3.Cross(normal, transform.up); + + // Flips the foward direction if facing the other direction + if ((m_Orientation.forward - foward).magnitude > (m_Orientation.forward - -foward).magnitude) + { foward = -foward; } + + // Applies the wall running force to the player + m_Body.AddForce(foward * m_WallRunSpeed * m_Body.mass * 10.0f, ForceMode.Force); + + // Removes any vertical velocity the player may have + //m_Body.velocity = new Vector3(m_Body.velocity.x, 0.0f, m_Body.velocity.z); } // Function to make the player jump @@ -213,30 +217,110 @@ public class PlayerMovement : MonoBehaviour } } + private void UpdatePlayerState() + { + // Stores previous state + PlayerState previous = m_State; + + // Works out wether the player's velocity is high enough to slide + Vector3 vel = m_Body.velocity; + bool canSlide = + !( + Mathf.Abs(vel.x) < m_SlideRequiredSpeed && + Mathf.Abs(vel.z) < m_SlideRequiredSpeed + ); + + // Checks if the player is in the wall running state + if (m_HitLhsWall || m_HitRhsWall) + { m_State = PlayerState.WALL_RUNNING; } + + // Checks if the player is in the wall riding state + else if (m_SlidingKeyPressed && (canSlide || m_OnSlope) && m_Grounded) + { m_State = PlayerState.SLIDING; } + + // Defaults to ruuning + else { m_State = PlayerState.RUNNING; } + + // Exits early if the state has not changed + if (previous == m_State) + { return; } + + // Calls exit function of old state + switch (previous) + { + case PlayerState.SLIDING: + StopSlide(); + break; + + default: + break; + } + + // Calls entry function of new state + switch (m_State) + { + case PlayerState.SLIDING: + StartSlide(); + break; + + default: + break; + } + } + // Fixed Update is called once per physics update private void FixedUpdate() { + // Works out the new state of the player + UpdatePlayerState(); + // Calculates the movement direction m_MoveDir = (m_Orientation.forward * m_Input.y) + (m_Orientation.right * m_Input.x); - // Does additional calculations on the movement direction if on a slope + // if (m_OnSlope) { + // Calculates better move direction for sliding m_MoveDir = Vector3.ProjectOnPlane(m_MoveDir, m_SlopeHit.normal).normalized; - - m_Body.useGravity = false; // Disables gravity on slopes } - else + // Runs correct update function depending on player state + switch (m_State) { - m_Body.useGravity = true; // Renables gravity to stop errors + case PlayerState.RUNNING: + // Adds the force to the rigid body + m_Body.AddForce(m_MoveDir.normalized * m_MoveSpeed * m_Body.mass * 10.0f, ForceMode.Force); + + // Stops player sliding slopes when they don't want to + if (m_OnSlope) + { m_Body.useGravity = false; } + + // Non-Slope running requires gravity on + else + { m_Body.useGravity = true; } + + break; + + case PlayerState.SLIDING: + m_Body.useGravity = false; // Disables gravity on slopes + + // Calls correct update function + UpdateSlidingState(); + break; + + case PlayerState.WALL_RUNNING: + // Calls correct update function + UpdateWallRunState(); + m_Body.useGravity = false; // Disables gravity on walls to stop the player sliding off them + break; } - // Updates the player sliding state - UpdateSlidingState(); - - // Updates the position of the player - UpdatePlayerPosition(); + // Calls the Jump function if the user has pressed jump + // No grounded checks needed as Jump() function does that internally + if (m_JumpKeyPressed) + { + Jump(); + } // Updates the counter for slide boost updates left m_TicksOfSlideBoostLeft = (int)Mathf.Clamp(m_TicksOfSlideBoostLeft - 1, 0, Mathf.Infinity);