I'm currently working on a FPS "Speedrun" game & I want to add "Sliding" to the players movement. I already added "Crouching", but currently I'm stuck at getting the sliding done. (I'm using the Character Controller) I've tried many things, but nothing really worked. Heres the current PlayerMovement script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UIElements;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
private Rigidbody rb;
public Transform cameraTransform;
public HeadBobController headBobController;
[Header("Movement")]
private float speedMultiplier = 1f;
public float speed = 12f;
public float sprintSpeed = 20f;
public float slopeSpeed = 0.01f;
public float gravity = -9.81f;
public float jumpHeight = 3f;
public float sensitivity = 2f;
public float cameraRotationLimit = 85f;
[Header("Countermovement")]
public float counterMovement = 0.175f;
private float threshold = 0.01f;
public float maxSlopeAngle = 35f;
[Header("Sliding / Crouch")]
public float slideSpeed;
float originalHeight;
public float reducedHeight;
float targetHeight = 2f;
public float timeToCrouch = 0.25f;
float timeElapsed = 0;
bool canStopCrouching;
bool wantStopCrouch;
bool isRunning = false;
public bool isCrouching = false;
bool canUseHeadbob = true;
bool WillSlideOnSlopes = true;
bool IsSliding
{
get
{
if (controller.isGrounded && Physics.Raycast(transform.position, Vector3.down, out RaycastHit slopeHit, 3f))
{
hitPointNormal = slopeHit.normal;
return Vector3.Angle(hitPointNormal, Vector3.up) > controller.slopeLimit;
} else
{
return false;
}
}
}
[Header("Headbob")]
[SerializeField] private float walkBobSpeed = 14f;
[SerializeField] private float walkBobAmount = 0.05f;
[SerializeField] private float sprintBobSpeed = 18f;
[SerializeField] private float sprintBobAmount = 0.11f;
[SerializeField] private float crouchBobSpeed = 8f;
[SerializeField] private float crouchBobAmount = 0.025f;
private float defaultYPos = 0;
private float timer;
private Vector3 hitPointNormal;
[Header("Ground Check")]
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
Vector3 velocity;
public bool isGrounded;
[Header("Camera")]
float cameraRotationX = 0f;
float cameraRotationY = 0f;
public Camera camera;
public Transform orientation;
[Header("Other")]
public GameObject speedlines;
public ParticleSystem jumpParticle;
void Start()
{
rb = GetComponent<Rigidbody>();
controller = GetComponent<CharacterController>();
originalHeight = controller.height;
defaultYPos = camera.transform.localPosition.y;
//Cursor.lockState = CursorLockMode.Locked;
//Cursor.visible = false;
}
public void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
move = Vector3.ClampMagnitude(move, 1f);
if (Input.GetKey(KeyCode.LeftShift) && !isCrouching)
{
controller.Move(move * sprintSpeed * Time.deltaTime);
isRunning = true;
headBobController.amplitude = 0.1f;
headBobController.frequency = 30f;
} else
{
controller.Move(move * speed * speedMultiplier * Time.deltaTime);
isRunning = false;
headBobController.amplitude = 0.0426f;
headBobController.frequency = 19.1f;
}
if (Physics.Raycast(camera.transform.position, Vector3.up, 1f))
{
canStopCrouching = false;
} else
{
canStopCrouching = true;
}
if (canStopCrouching && wantStopCrouch && !Physics.Raycast(camera.transform.position, Vector3.up, 1f))
{
wantStopCrouch = false;
StopCrouch();
}
if (Input.GetKeyDown(KeyCode.LeftControl) && isGrounded)
{
//controller.height = Mathf.Lerp(reducedHeight, originalHeight, timeElapsed);
StartCrouch();
} else if(Input.GetKeyUp(KeyCode.LeftControl) && isGrounded && !Physics.Raycast(camera.transform.position, Vector3.up, 1f))
{
//controller.height = Mathf.Lerp(originalHeight, targetHeight, timeElapsed);
StopCrouch();
} else if(Input.GetKeyUp(KeyCode.LeftControl) && isGrounded && Physics.Raycast(camera.transform.position, Vector3.up, 1f))
{
wantStopCrouch = true;
}
if (Input.GetKey(KeyCode.LeftControl) && isRunning && isGrounded)
{
//Sliding
}
if (canUseHeadbob && isGrounded)
{
if (Mathf.Abs(move.x) > 0.1f || Mathf.Abs(move.z) > 0.1f)
{
timer += Time.deltaTime * (isCrouching ? crouchBobSpeed : isRunning ? sprintBobSpeed : walkBobSpeed);
camera.transform.localPosition = new Vector3(camera.transform.localPosition.x, defaultYPos + Mathf.Sin(timer) * (isRunning ? sprintBobAmount : walkBobAmount), camera.transform.localPosition.z);
}
}
if(WillSlideOnSlopes && IsSliding)
{
move += new Vector3(hitPointNormal.x, -hitPointNormal.y, hitPointNormal.z) * slopeSpeed;
}
if (isRunning)
{
speedlines.active = true;
} else {
speedlines.active = false;
}
controller.Move(move * speed * speedMultiplier * Time.deltaTime);
if (Input.GetButtonDown("Jump") && isGrounded)
{
controller.Move(-transform.forward * 0.1f);
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
jumpParticle.Play();
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
// Rotate the player based on mouse input
float mouseX = Input.GetAxis("Mouse X") * sensitivity;
float mouseY = Input.GetAxis("Mouse Y") * sensitivity;
cameraRotationY += mouseX;
cameraRotationX -= mouseY;
cameraRotationX = Mathf.Clamp(cameraRotationX, -cameraRotationLimit, cameraRotationLimit);
cameraTransform.localEulerAngles = new Vector3(cameraRotationX, cameraRotationY, 0f);
transform.eulerAngles = new Vector3(0f, cameraRotationY, 0f);
}
private void StartCrouch()
{
controller.height = reducedHeight;
speedMultiplier = 0.5f;
isCrouching = true;
}
private void StopCrouch()
{
controller.height = originalHeight;
speedMultiplier = 1f;
isCrouching = false;
}
}
It would be very nice if anyone could try to explain me how to implement it into this code using the Character Controller! :)
I've tried multiple Youtube Tutorial solutions & a lot in Online Forums, nothing really worked for me for some reason. Often the system wouldn't even work (nothing happened). I want the player to have the crouching ability (which is implemented) & to slide when isRunning & when the player isGrounded
Related
The main character from the Unity 3D tutorials that I followed, I believe not from the site, are producing a wobbly left foot. I did not put a foot IK because the issue is not placement. It seems like an origin point thing, but not sure so I turned here for help.
I tried to have the full range of motion of the Mecanim character but was unable to produce foot bones on the rig that seem to work even though I just imported the model and code.
Third Person Character code :
using System;
using System.Collections;
using UnityEngine;
namespace UnityStandardAssets.Characters.ThirdPerson
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Animator))]
public class ThirdPersonCharacter : MonoBehaviour
{
[SerializeField] float m_MovingTurnSpeed = 360;
[SerializeField] float m_StationaryTurnSpeed = 180;
[SerializeField] float m_JumpPower = 12f;
[Range(1f, 4f)][SerializeField] float m_GravityMultiplier = 2f;
[SerializeField] float m_RunCycleLegOffset = 0.2f;
[SerializeField] float m_MoveSpeedMultiplier = 1f;
[SerializeField] float m_AnimSpeedMultiplier = 1f;
[SerializeField] float m_GroundCheckDistance = 0.1f;
Rigidbody m_Rigidbody;
Animator m_Animator;
bool m_IsGrounded;
float m_OrigGroundCheckDistance;
const float k_Half = 0.5f;
float m_TurnAmount;
float m_ForwardAmount;
Vector3 m_GroundNormal;
float m_CapsuleHeight;
Vector3 m_CapsuleCenter;
CapsuleCollider m_Capsule;
bool m_Crouching;
void Start()
{
m_Animator = GetComponent<Animator>();
m_Rigidbody = GetComponent<Rigidbody>();
m_Capsule = GetComponent<CapsuleCollider>();
m_CapsuleHeight = m_Capsule.height;
m_CapsuleCenter = m_Capsule.center;
m_Rigidbody.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY | RigidbodyConstraints.FreezeRotationZ;
m_OrigGroundCheckDistance = m_GroundCheckDistance;
}
public void Move(Vector3 move, bool crouch, bool jump)
{
if (move.magnitude > 1f) move.Normalize();
move = transform.InverseTransformDirection(move);
CheckGroundStatus();
move = Vector3.ProjectOnPlane(move, m_GroundNormal);
m_TurnAmount = Mathf.Atan2(move.x, move.z);
m_ForwardAmount = move.z;
ApplyExtraTurnRotation();
if (m_IsGrounded)
{
HandleGroundedMovement(crouch, jump);
}
else
{
HandleAirborneMovement();
}
ScaleCapsuleForCrouching(crouch);
PreventStandingInLowHeadroom();
UpdateAnimator(move);
}
void ScaleCapsuleForCrouching(bool crouch)
{
if (m_IsGrounded && crouch)
{
if (m_Crouching) return;
m_Capsule.height = m_Capsule.height / 2f;
m_Capsule.center = m_Capsule.center / 2f;
m_Crouching = true;
}
else
{
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up);
float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half;
if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength, Physics.AllLayers, QueryTriggerInteraction.Ignore))
{
m_Crouching = true;
return;
}
m_Capsule.height = m_CapsuleHeight;
m_Capsule.center = m_CapsuleCenter;
m_Crouching = false;
}
}
void PreventStandingInLowHeadroom()
{
if (!m_Crouching)
{
Ray crouchRay = new Ray(m_Rigidbody.position + Vector3.up * m_Capsule.radius * k_Half, Vector3.up);
float crouchRayLength = m_CapsuleHeight - m_Capsule.radius * k_Half;
if (Physics.SphereCast(crouchRay, m_Capsule.radius * k_Half, crouchRayLength, Physics.AllLayers, QueryTriggerInteraction.Ignore))
{
m_Crouching = true;
}
}
}
void UpdateAnimator(Vector3 move)
{
m_Animator.SetFloat("Forward", m_ForwardAmount, 0.1f, Time.deltaTime);
m_Animator.SetFloat("Turn", m_TurnAmount, 0.1f, Time.deltaTime);
m_Animator.SetBool("Crouch", m_Crouching);
m_Animator.SetBool("OnGround", m_IsGrounded);
if (!m_IsGrounded)
{
m_Animator.SetFloat("Jump", m_Rigidbody.velocity.y);
}
float runCycle =
Mathf.Repeat(
m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime + m_RunCycleLegOffset, 1);
float jumpLeg = (runCycle < k_Half ? 1 : -1) * m_ForwardAmount;
if (m_IsGrounded)
{
m_Animator.SetFloat("JumpLeg", jumpLeg);
}
if (m_IsGrounded && move.magnitude > 0)
{
m_Animator.speed = m_AnimSpeedMultiplier;
}
else
{
m_Animator.speed = 1;
}
}
void HandleAirborneMovement()
{
Vector3 extraGravityForce = (Physics.gravity * m_GravityMultiplier) - Physics.gravity;
m_Rigidbody.AddForce(extraGravityForce);
m_GroundCheckDistance = m_Rigidbody.velocity.y < 0 ? m_OrigGroundCheckDistance : 0.01f;
}
void HandleGroundedMovement(bool crouch, bool jump)
{
if (jump && !crouch && m_Animator.GetCurrentAnimatorStateInfo(0).IsName("Grounded"))
{
m_Rigidbody.velocity = new Vector3(m_Rigidbody.velocity.x, m_JumpPower, m_Rigidbody.velocity.z);
m_IsGrounded = false;
m_Animator.applyRootMotion = false;
m_GroundCheckDistance = 0.1f;
}
}
void ApplyExtraTurnRotation()
{
float turnSpeed = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, m_ForwardAmount);
transform.Rotate(0, m_TurnAmount * turnSpeed * Time.deltaTime, 0);
}
public void OnAnimatorMove()
{
if (m_IsGrounded && Time.deltaTime > 0)
{
Vector3 v = (m_Animator.deltaPosition * m_MoveSpeedMultiplier) / Time.deltaTime;
v.y = m_Rigidbody.velocity.y;
m_Rigidbody.velocity = v;
}
}
void CheckGroundStatus()
{
RaycastHit hitInfo;
Debug.DrawLine(transform.position + (Vector3.up * 0.1f), transform.position + (Vector3.up * 0.1f) + (Vector3.down * m_GroundCheckDistance));
if (Physics.Raycast(transform.position + (Vector3.up * 0.1f), Vector3.down, out hitInfo, m_GroundCheckDistance))
{
m_GroundNormal = hitInfo.normal;
m_IsGrounded = true;
m_Animator.applyRootMotion = true;
}
else
{
m_IsGrounded = false;
m_GroundNormal = Vector3.up;
m_Animator.applyRootMotion = false;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
[Header("Movement")]
private float moveSpeed;
public float walkSpeed;
public float sprintSpeed;
public bool sliding;
public bool sprinting;
public float groundDrag;
[Header("Jumping")]
public float jumpForce;
public float jumpCooldown;
public float airMultiplier;
bool readyToJump;
[Header("Crouching")]
public float crouchSpeed;
public float crouchYScale;
private float startYScale;
[Header("Keybinds")]
public KeyCode jumpKey = KeyCode.Space;
public KeyCode sprintKey = KeyCode.LeftShift;
public KeyCode crouchKey = KeyCode.LeftControl;
[Header("Ground Check")]
public float playerHeight;
public LayerMask whatIsGround;
bool grounded;
[Header("Slope Handling")]
public float maxSlopeAngle;
private RaycastHit slopeHit;
private bool exitingSlope;
[Header("Sliding")]
public float maxSlideTime;
public float slideForce;
private float slideTimer;
public float slideSpeed;
public float slideYScale;
[Header("References")]
public Transform orientation;
private Rigidbody rb;
[Header("Input")]
private float horizontalInput;
private float verticalInput;
Vector3 moveDirection;
public MovementState state;
public enum MovementState
{
walking,
sprinting,
crouching,
sliding,
air
}
private void Start()
{
rb = GetComponent<Rigidbody>();
rb.freezeRotation = true;
readyToJump = true;
startYScale = transform.localScale.y;
}
private void Update()
{
// ground check
grounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.2f, whatIsGround);
MyInput();
SpeedControl();
StateHandler();
if (Input.GetKeyUp(sprintKey) || Input.GetKeyUp(KeyCode.W))
sprinting = false;
// handle drag
if (grounded)
rb.drag = groundDrag;
else
rb.drag = 0;
horizontalInput = Input.GetAxisRaw("Horizontal");
verticalInput = Input.GetAxisRaw("Vertical");
if (Input.GetKeyDown(crouchKey) && sprinting)
StartSlide();
if (Input.GetKeyUp(crouchKey) && sliding)
StopSlide();
}
private void FixedUpdate()
{
MovePlayer();
if (sliding)
SlidingMovement();
}
private void MyInput()
{
horizontalInput = Input.GetAxisRaw("Horizontal");
verticalInput = Input.GetAxisRaw("Vertical");
// when to jump
if (Input.GetKey(jumpKey) && readyToJump && grounded)
{
readyToJump = false;
Jump();
Invoke(nameof(ResetJump), jumpCooldown);
}
// start crouch
if (Input.GetKeyDown(crouchKey))
{
transform.localScale = new Vector3(transform.localScale.x, crouchYScale, transform.localScale.z);
rb.AddForce(Vector3.down * 5f, ForceMode.Impulse);
}
if (Input.GetKeyDown(crouchKey) && sprinting)
{
transform.localScale = new Vector3(transform.localScale.x, slideYScale, transform.localScale.z);
rb.AddForce(Vector3.down * 5f, ForceMode.Impulse);
sliding = true;
moveSpeed = slideSpeed;
}
// stop crouch
if (Input.GetKeyUp(crouchKey))
{
transform.localScale = new Vector3(transform.localScale.x, startYScale, transform.localScale.z);
sliding = false;
}
}
private void StateHandler()
{
// Mode - Crouching
if (Input.GetKey(crouchKey))
{
state = MovementState.crouching;
moveSpeed = crouchSpeed;
}
// Mode - Sliding
if(Input.GetKey(crouchKey) && sprinting)
{
state = MovementState.sliding;
moveSpeed = slideSpeed;
}
// Mode - Sprinting
else if (grounded && Input.GetKey(sprintKey) && Input.GetKey(KeyCode.W))
{
state = MovementState.sprinting;
moveSpeed = sprintSpeed;
sprinting = true;
}
// Mode - Walking
else if (grounded)
{
state = MovementState.walking;
moveSpeed = walkSpeed;
}
// Mode - Air
else
{
state = MovementState.air;
}
}
private void MovePlayer()
{
// calculate movement direction
moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput;
// on slope
if (OnSlope() && !exitingSlope)
{
rb.AddForce(GetSlopeMoveDirection() * moveSpeed * 20f, ForceMode.Force);
if (rb.velocity.y > 0)
rb.AddForce(Vector3.down * 80f, ForceMode.Force);
}
// on ground
else if (grounded)
rb.AddForce(moveDirection.normalized * moveSpeed * 10f, ForceMode.Force);
// in air
else if (!grounded)
rb.AddForce(moveDirection.normalized * moveSpeed * 10f * airMultiplier, ForceMode.Force);
// turn gravity off while on slope
rb.useGravity = !OnSlope();
}
private void SpeedControl()
{
// limiting speed on slope
if (OnSlope() && !exitingSlope)
{
if (rb.velocity.magnitude > moveSpeed)
rb.velocity = rb.velocity.normalized * moveSpeed;
}
// limiting speed on ground or in air
else
{
Vector3 flatVel = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
// limit velocity if needed
if (flatVel.magnitude > moveSpeed)
{
Vector3 limitedVel = flatVel.normalized * moveSpeed;
rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z);
}
}
}
private void Jump()
{
exitingSlope = true;
// reset y velocity
rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
rb.AddForce(transform.up * jumpForce, ForceMode.Impulse);
}
private void ResetJump()
{
readyToJump = true;
exitingSlope = false;
}
private bool OnSlope()
{
if (Physics.Raycast(transform.position, Vector3.down, out slopeHit, playerHeight * 0.5f + 0.3f))
{
float angle = Vector3.Angle(Vector3.up, slopeHit.normal);
return angle < maxSlopeAngle && angle != 0;
}
return false;
}
private Vector3 GetSlopeMoveDirection()
{
return Vector3.ProjectOnPlane(moveDirection, slopeHit.normal).normalized;
}
private void StartSlide()
{
sliding = true;
transform.localScale = new Vector3(transform.localScale.x, slideYScale, transform.localScale.z);
rb.AddForce(Vector3.down * 5f, ForceMode.Impulse);
slideTimer = maxSlideTime;
}
private void SlidingMovement()
{
Vector3 inputDirection = orientation.forward;
rb.AddForce(inputDirection.normalized * slideForce, ForceMode.Force);
slideTimer -= Time.deltaTime;
if (slideTimer <= 0)
StopSlide();
}
private void StopSlide()
{
sliding = false;
transform.localScale = new Vector3(transform.localScale.x, startYScale, transform.localScale.z);
}
}
I have been trying for a very long time to fix this, although I'm not the best at coding. I decided to come onto stack overflow for some help. Thank you to everyone who tries to solves this problem :D. As the titles states everything here works, excluding the crouching, when I press ctrl my moveSpeed does not change to my crouchSpeed...
I have coded the unity movement for my character. I made it so they can wall run bit it seems that wall running is somehow making my character walk weird. After wall running (if I wall run with the wall on my right) it will apply a force 90 degrees when I'm walking on the ground. The unknown force changes when each button is pressed (is W then apply both the W force and a force pushing it to the right). And the same happens with running on the left wall but it is at a -90 degree angle. Please help I've been stuck on this for a whole week. Thanks
This is the wall running code
using System.Collections.Generic;
using UnityEngine;
public class MoveChar : MonoBehaviour
{
[Header("Movement")]
float moveSpeed;
[SerializeField] float walkSpeed;
[SerializeField] float sprintSpeed;
[SerializeField] float slideSpeed;
[SerializeField] float wallRunSpeed;
float desiredMoveSpeed;
float lastDesiredMoveSpeed;
[SerializeField] float groundDrag;
[Header("Jumping")]
[SerializeField] float jumpForce;
[SerializeField] float jumpCoolDown;
[SerializeField] float airMultiplier;
bool readyToJump;
[HideInInspector] public bool doubleJump;
[Header("Crouching")]
[SerializeField] float crouchSpeed;
[SerializeField] float crouchYScale;
float crouchYStart;
bool exitingJump;
[Header("Slope Check")]
[SerializeField] float maxSlope;
RaycastHit slopeHit;
[Header("Ground Check")]
[SerializeField] float playerHeight;
[SerializeField] LayerMask whatIsGround;
bool grounded;
[Header("Keybinds")]
KeyCode jumpKey = KeyCode.Space;
KeyCode walkKey = KeyCode.LeftShift;
KeyCode crouchKey = KeyCode.C;
[SerializeField] Transform orientation;
[SerializeField] Transform camRotate;
float horizontalInput;
float verticalInput;
Vector3 moveDirection;
Rigidbody rb;
MovementState state;
enum MovementState
{
walking,
sprinting,
crouching,
sliding,
wallRunning,
air
}
[HideInInspector] public bool sliding;
[HideInInspector] public bool wallRunning;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
ResetJump();
crouchYStart = transform.localScale.y;
}
// Update is called once per frame
void Update()
{
grounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.2f, whatIsGround);
MyInput();
SpeedControl();
StateHandler();
if (grounded)
{
rb.drag = groundDrag;
}
else
{
rb.drag = 0;
}
}
private void FixedUpdate()
{
MovePlayer();
}
void MyInput()
{
horizontalInput = Input.GetAxisRaw("Horizontal");
verticalInput = Input.GetAxisRaw("Vertical");
if (Input.GetKeyDown(jumpKey) && readyToJump && (grounded || doubleJump))
{
readyToJump = false;
Jump();
Invoke(nameof(ResetJump), jumpCoolDown);
}
if (grounded && !Input.GetKey(jumpKey))
{
doubleJump = false;
}
if (Input.GetKeyDown(crouchKey))
{
transform.localScale = new Vector3(transform.localScale.x, crouchYScale, transform.localScale.z);
rb.AddForce(Vector3.down * 5f, ForceMode.Impulse);
}
if (Input.GetKeyUp(crouchKey))
{
transform.localScale = new Vector3(transform.localScale.x, crouchYStart, transform.localScale.z);
}
}
void StateHandler()
{
if (wallRunning)
{
state = MovementState.wallRunning;
desiredMoveSpeed = wallRunSpeed;
doubleJump = false;
}
if (sliding)
{
state = MovementState.sliding;
if (OnSlope() && rb.velocity.y < 0.1f)
{
desiredMoveSpeed = slideSpeed;
}
else
{
desiredMoveSpeed = sprintSpeed;
}
}
else if (Input.GetKey(crouchKey))
{
state = MovementState.crouching;
desiredMoveSpeed = crouchSpeed;
}
else if (grounded && Input.GetKey(walkKey))
{
state = MovementState.walking;
desiredMoveSpeed = walkSpeed;
}
else if (grounded)
{
state = MovementState.sprinting;
desiredMoveSpeed = sprintSpeed;
}
else
{
state = MovementState.air;
}
if (Mathf.Abs(desiredMoveSpeed - lastDesiredMoveSpeed) > 4f && moveSpeed != 0)
{
StopAllCoroutines();
StartCoroutine(SmoothlyLerpMoveSpeed());
}
else
{
moveSpeed = desiredMoveSpeed;
}
lastDesiredMoveSpeed = desiredMoveSpeed;
}
IEnumerator SmoothlyLerpMoveSpeed()
{
float time = 0;
float difference = Mathf.Abs(desiredMoveSpeed - moveSpeed);
float startValue = moveSpeed;
while (time < difference)
{
moveSpeed = Mathf.Lerp(startValue, desiredMoveSpeed, time / difference);
time += Time.deltaTime;
yield return null;
}
moveSpeed = desiredMoveSpeed;
}
void MovePlayer()
{
moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput;
if (OnSlope() && !exitingJump)
{
rb.AddForce(GetSlopeMoveDirection(moveDirection) * moveSpeed * 20f, ForceMode.Force);
if (rb.velocity.y > 0)
{
rb.AddForce(Vector3.down * 80f, ForceMode.Force);
}
}
else if (grounded)
{
rb.AddForce(moveDirection.normalized * moveSpeed * 10f, ForceMode.Force);
}
else if (!grounded)
{
rb.AddForce(moveDirection.normalized * moveSpeed * 10f * airMultiplier, ForceMode.Force);
}
if (!wallRunning)
{
rb.useGravity = !OnSlope();
}
}
void SpeedControl()
{
if (OnSlope() && !exitingJump)
{
if (rb.velocity.magnitude > moveSpeed)
{
rb.velocity = rb.velocity.normalized * moveSpeed;
}
}
else
{
Vector3 flatVel = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
if (flatVel.magnitude > moveSpeed)
{
Vector3 limitVel = flatVel.normalized * moveSpeed;
rb.velocity = new Vector3(limitVel.x, rb.velocity.y, limitVel.z);
}
}
}
void Jump()
{
exitingJump = true;
rb.velocity = new Vector3(rb.velocity.x, 0, rb.velocity.z);
rb.AddForce(transform.up * jumpForce, ForceMode.Impulse);
doubleJump = !doubleJump;
}
void ResetJump()
{
readyToJump = true;
exitingJump = false;
}
public bool OnSlope()
{
if (Physics.Raycast(transform.position, Vector3.down, out slopeHit, playerHeight * 0.5f + 0.3f))
{
float angle = Vector3.Angle(Vector3.up, slopeHit.normal);
return angle < maxSlope && angle != 0;
}
return false;
}
public Vector3 GetSlopeMoveDirection(Vector3 direction)
{
return Vector3.ProjectOnPlane(direction, slopeHit.normal).normalized;
}
}
This is the wall running code
using System.Collections.Generic;
using UnityEngine;
public class WallRunning : MonoBehaviour
{
[Header("Wallrunning")]
[SerializeField] LayerMask whatIsRunAble;
[SerializeField] LayerMask whatIsGround;
[SerializeField] float wallRunForce;
[SerializeField] float wallRunUpForce;
[SerializeField] float wallRunSideForce;
[SerializeField] float wallClimbSpeed;
[Header("Input")]
KeyCode jumpKey = KeyCode.Space;
float horizontalInput;
float verticalInput;
[Header("Detection")]
[SerializeField] float wallCheckDistance;
[SerializeField] float minJumpHeight;
RaycastHit leftWallHit;
RaycastHit rightWallHit;
bool wallLeft;
bool wallRight;
[Header("Exiting")]
[SerializeField] float exitWallTime;
bool exitingWall;
float exitWallTimer;
[Header("Gravity")]
[SerializeField] bool useGravity;
[SerializeField] float gravityCounterForce;
[Header("References")]
[SerializeField] Transform orientation;
MoveChar mc;
Rigidbody rb;
[SerializeField] Transform cam;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
mc = GetComponent<MoveChar>();
}
// Update is called once per frame
void Update()
{
CheckForWall();
StateMachine();
}
private void FixedUpdate()
{
if (mc.wallRunning)
{
WallRunMove();
}
}
void CheckForWall()
{
wallRight = Physics.Raycast(transform.position, orientation.right, out rightWallHit, wallCheckDistance, whatIsRunAble);
wallLeft = Physics.Raycast(transform.position, -orientation.right, out leftWallHit, wallCheckDistance, whatIsRunAble);
}
bool AboveGround()
{
return !Physics.Raycast(transform.position, Vector3.down, minJumpHeight, whatIsGround);
}
void StateMachine()
{
horizontalInput = Input.GetAxisRaw("Horizontal");
verticalInput = Input.GetAxisRaw("Vertical");
if ((wallLeft || wallRight) && verticalInput > 0 && AboveGround() && !exitingWall)
{
if (Input.GetKeyDown(jumpKey))
{
WallJump();
}
}
if ((wallLeft || wallRight) && verticalInput > 0 && AboveGround() && !exitingWall)
{
if (!mc.wallRunning)
{
StartWallRun();
}
}
else if (exitingWall)
{
if (mc.wallRunning)
{
StopWallRun();
}
if (exitWallTimer > 0)
{
exitWallTimer -= Time.deltaTime;
}
if (exitWallTimer <= 0)
{
exitingWall = false;
}
}
else
{
if (mc.wallRunning)
{
StopWallRun();
}
}
}
void StartWallRun()
{
mc.wallRunning = true;
rb.velocity = new Vector3(rb.velocity.x, 0, rb.velocity.z);
if (wallRight)
{
cam.localRotation = Quaternion.Euler(0, 0, 10f);
}
if (wallLeft)
{
cam.localRotation = Quaternion.Euler(0, 0, -10f);
}
}
void WallRunMove()
{
rb.useGravity = useGravity;
Vector3 wallNormal = wallRight ? rightWallHit.normal : leftWallHit.normal;
Vector3 wallForward = Vector3.Cross(wallNormal, transform.up.normalized);
if ((orientation.forward - wallForward).magnitude > (orientation.forward - -wallForward).magnitude)
{
wallForward = -wallForward;
}
rb.AddForce(wallForward * wallRunForce, ForceMode.Force);
/*if (!(wallLeft && horizontalInput > 0) && !(wallRight && horizontalInput < 0))
{
rb.AddForce(-wallNormal * 100, ForceMode.Force);
}
if (useGravity)
{
rb.AddForce(transform.up * gravityCounterForce, ForceMode.Force);
}*/
}
void StopWallRun()
{
mc.wallRunning = false;
cam.localRotation = Quaternion.Euler(0, 0, 0);
}
void WallJump()
{
exitingWall = true;
exitWallTimer = exitWallTime;
Vector3 wallNormal = wallRight ? rightWallHit.normal : leftWallHit.normal;
Vector3 forceToApply = transform.up * wallRunUpForce + wallNormal * wallRunSideForce;
rb.velocity = new Vector3(rb.velocity.x, 0, rb.velocity.z);
rb.AddForce(forceToApply, ForceMode.Impulse);
mc.doubleJump = !mc.doubleJump;
}
}
Thank you soooooooo much for any help
So, I made a basic movement script with wallrunning and wanted to add dashing to it, at first I made parameters for the character to dash and testing it with debug.log worked as intended, but the actual dash command, which was transform.translate(Vector3.forward), didn't work for some reason.
This is the code:
public class PlayerMovement : MonoBehaviour
{
public Transform player;
public CharacterController controller;
public float speed = 12f;
public float baseSpeed;
Vector3 velocity;
public float gravity = -9.81f;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
public Transform wallCheckL;
public Transform wallCheckR;
public float wallDistanceL = -0.4f;
public float wallDistanceR = 0.4f;
public LayerMask wallMask;
public bool touchWallL;
public bool touchWallR;
public float SlideTime = 10f;
public float Ynormal;
public bool isGrounded;
public bool Sprinting;
public bool Crouching;
public bool Sliding;
public bool canDash;
// Start is called before the first frame update
void Start()
{
baseSpeed = speed;
canDash = true;
}
// Update is called once per frame
void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
touchWallL = Physics.CheckSphere(wallCheckL.position, wallDistanceL, wallMask);
touchWallR = Physics.CheckSphere(wallCheckR.position, wallDistanceR, wallMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
if ((isGrounded == true) && Input.GetKeyDown("space"))
{
velocity.y = 10f;
}
if (Input.GetKeyDown(KeyCode.LeftShift))
{
speed = baseSpeed * 1.5f;
Sprinting = true;
}
if (Input.GetKeyUp(KeyCode.LeftShift))
{
speed = baseSpeed;
Sprinting = false;
}
if (Input.GetKeyDown(KeyCode.LeftControl))
{
Crouching = true;
}
if (Input.GetKeyUp(KeyCode.LeftControl))
{
Crouching = false;
}
if (Crouching)
{
transform.localScale = new Vector3(1f, 0.5f, 1f);
} else if (Crouching == false)
{
transform.localScale = new Vector3(1f, 1f, 1f);
}
if (Sprinting && Crouching)
{
Sliding = true;
} else if (Sprinting == false && Crouching == false)
{
Sliding = false;
}
if (Sliding)
{
speed = baseSpeed * 2;
transform.localScale = new Vector3(1f, 0.25f, 1f);
}
Ynormal = transform.localEulerAngles.y;
if (touchWallL)
{
gravity = -4.4f;
transform.localRotation = Quaternion.Euler(0, Ynormal, -20f);
isGrounded = true;
} else if (touchWallR)
{
gravity = -4.4f;
transform.localRotation = Quaternion.Euler(0f, Ynormal, 20f);
isGrounded = true;
}
else
{
gravity = -9.81f;
transform.localRotation = Quaternion.Euler(0f, Ynormal, 0f);
}
if (touchWallR && Input.GetKeyDown(KeyCode.Space))
{
velocity.y = 10f;
transform.position += Vector3.left * Time.deltaTime * 100;
}
if (Input.GetKeyDown(KeyCode.E) && canDash)
{
Dash();
}
}
void Dash()
{
StartCoroutine(dashTimer());
player.transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
IEnumerator dashTimer()
{
canDash = false;
yield return new WaitForSeconds(2f);
canDash = true;
}
}
The requirements themselves work, I did some testing, but the dash itself didn't. I tried controller.move, transform.position += transform.position, even making a game object empty called dash distance and trying to teleport my character there, but none of it worked, all of it resulted in my character just not doing anything when I tried to dash.
This is due that Transform.Translate actually needs to be updated each frame.
Try refactoring a bit like this:
// add another control flag
private bool isDashing = false;
void Update()
{
//...
if (Input.GetKeyDown(KeyCode.E) && canDash)
{
StartCoroutine(dashTimer());
}
if (isDashing)
{
Dash();
}
}
void Dash()
{
player.transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
IEnumerator dashTimer()
{
canDash = false;
isDashing = true;
yield return new WaitForSeconds(2f);
canDash = true;
isDashing = false;
}
Solved - I ended up removing playerController.SimpleMove and having playerController.Move control all the player's movement.
So I have been trying to make my character wall jump between to walls. I've been able to make it work however, after each successful wall jump the height of the next jump decreases. This gets to the point where it starts wall jumping downwards. I have no idea why it does this. Before each time pressing the space bar, I am resetting the movement Vector3's to zero and then reapplying the correct values to the jump. I have even gone through the console to look at the vertical changes and neither moveDirection and moveAmount get a large enough value change to make this happen.
Because I am just trying this out, below is all the code that is affecting the player.
public float rotateSpeed = 3.0f;
public float walkSpeed = 5.0f;
public float runSpeed = 15.0f;
public float jumpSpeed = 10.0f;
public float acceleration = .05f;
public bool running = false;
public bool jumping = false;
public bool falling = false;
public bool onWall = false;
public bool wallJumping = false;
public bool stay = false;
private CharacterController playerController;
private Animator playerAnimator;
private float speed;
private Vector3 moveAmount;
private float animationSpeed;
private float currentSpeed;
private float targetSpeed;
private float moveSpeed;
float gravity = 10f;
private Vector3 moveDirection = Vector3.zero;
// Use this for initialization
void Start () {
playerController = GetComponent<CharacterController>();
playerAnimator = GetComponent<Animator>();
}
// Update is called once per frame
void Update () {
transform.Rotate(0, Input.GetAxis("Horizontal") * rotateSpeed, 0);
if (onWall)
{
playerAnimator.SetBool("Wall Holding", true);
jumping = false;
}
else
{
playerAnimator.SetBool("Wall Holding", false);
}
if (stay)
{
moveDirection = Vector3.zero;
moveAmount = Vector3.zero;
}
#region Player Controls
if (Input.GetButton("Vertical") && !wallJumping)
{
moveSpeed = (Input.GetKey(KeyCode.LeftShift)) ? runSpeed : walkSpeed;
moveAmount = transform.TransformDirection(Vector3.forward);
}
else
{
moveSpeed = 0;
moveAmount = Vector3.zero;
}
if (playerController.isGrounded){
moveDirection = Vector3.zero;
jumping = false;
wallJumping = false;
onWall = false;
stay = false;
}
else
{
if (!stay)
onWall = false;
}
if (Input.GetKeyDown(KeyCode.Space))
{
if (playerController.isGrounded)
{
moveDirection.y = jumpSpeed;
jumping = true;
}
else if (onWall)
{
moveDirection = Vector3.zero;
moveAmount = Vector3.zero;
wallJumping = true;
moveDirection += transform.forward * jumpSpeed * 2f;
moveDirection += transform.up * jumpSpeed * 2f;
stay = false;
}
}
if (Input.GetKeyDown(KeyCode.F))
{
if (playerAnimator.GetBool("fight"))
{
playerAnimator.SetBool("fight", false);
}
else
{
playerAnimator.SetBool("fight", true);
}
}
#endregion Player Controls
moveDirection.y -= gravity * Time.deltaTime;
Debug.Log(moveDirection.x);
targetSpeed = Input.GetAxis("Vertical") * moveSpeed;
currentSpeed = SpeedFactor(targetSpeed, currentSpeed, acceleration);
if (currentSpeed > walkSpeed)
running = true;
else
running = false;
playerAnimator.SetFloat("movespeed", currentSpeed);
if (!stay)
{
playerController.SimpleMove(currentSpeed * moveAmount);
playerController.Move(moveDirection * Time.deltaTime);
}
}
private float SpeedFactor (float targetSpeed, float currentSpeed, float dilation) {
if ( currentSpeed < targetSpeed) {
currentSpeed += dilation;
if ( currentSpeed > targetSpeed) {
currentSpeed = targetSpeed;
}
}
else if ( currentSpeed > targetSpeed) {
currentSpeed -= dilation;
if ( currentSpeed < targetSpeed) {
currentSpeed = targetSpeed;
}
}
return currentSpeed;
}
void OnControllerColliderHit (ControllerColliderHit hit) {
if (hit.gameObject.tag == "JumpingWall" && !playerController.isGrounded && !onWall && (jumping || wallJumping))
{
onWall = true;
transform.Rotate(0,180,0);
stay = true;
}
}
The simplest explanation is that transform.forward and/or transform.up vectors begin to diverge from your expectations over time. This would explain how it even seems to be jumping "backwards" eventually when you apply the lines below. Are you sure these vectors are oriented as you expect after the jumper collides with a wall (prior to the next jump)?
moveDirection += transform.forward * jumpSpeed * 2f;
moveDirection += transform.up * jumpSpeed * 2f;