In the process of coding a roll ability for my game, I came across an issue where once I press the roll button, the rigidbody speeds up to about half of the rollSpeed I have set. Then once the roll ends, the rigidbody very quickly speeds up to the full rollSpeed, then gradually slowing down back to my runSpeed.
Any advice on how to sort this out, and make it so that the rigidbody immediately reaches rollSpeed when I press the roll button? My player movement and rolling scripts are below, along with a video showing what I’m experiencing.
7fb6ne
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public Transform orientation;
public MovementState state;
public enum MovementState
{
running,
rolling,
air
}
public bool rolling;
[Header("Movement")]
private float moveSpeed;
public float runSpeed;
public float rollSpeed;
public float rollSpeedChangeFactor;
public float maxYSpeed;
public float groundDrag;
[Header("Keybinds")]
public KeyCode jumpKey;
[Header("Ground Check")]
public float playerHeight;
public LayerMask whatIsGround;
public bool grounded;
[Header("Slope Handling")]
public float maxSlopeAngle;
private RaycastHit slopeHit;
private bool exitingSlope;
[Header("Jumping")]
public float airMultiplier;
bool readyToJump;
public float jumpHeight = 2f;
public bool isJumping;
public float gasTime;
public float gasTimeCounter;
float horizontalInput;
float verticalInput;
Vector3 moveDirection;
Rigidbody rb;
private void Start()
{
rb = GetComponent<Rigidbody>();
rb.freezeRotation = true;
readyToJump = true;
}
private void Update()
{
//ground check
grounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.2f, whatIsGround);
MyInput();
StateHandler();
SpeedControl();
if (state == MovementState.running)
{
rb.drag = groundDrag;
}
else
rb.drag = 0;
}
private void FixedUpdate()
{
MovePlayer();
//handle ground drag (sliding on ground)
if (state == MovementState.running)
rb.drag = groundDrag;
else
rb.drag = 0;
}
private void MyInput()
{
horizontalInput = Input.GetAxisRaw("Horizontal");
verticalInput = Input.GetAxisRaw("Vertical");
Jump();
}
private float desiredMoveSpeed;
private float lastDesiredMoveSpeed;
private MovementState lastState;
private bool keepMomentum;
private void StateHandler()
{
//Mode - Rolling
if (rolling)
{
state = MovementState.rolling;
desiredMoveSpeed = rollSpeed;
speedChangeFactor = rollSpeedChangeFactor;
}
//Mode - running
else if (grounded)
{
state = MovementState.running;
desiredMoveSpeed = runSpeed;
}
//Mode - Air
else
{
state = MovementState.air;
desiredMoveSpeed = runSpeed;
}
bool desiredMoveSpeedHasChanged = desiredMoveSpeed != lastDesiredMoveSpeed;
if (lastState == MovementState.rolling) keepMomentum = true;
if (desiredMoveSpeedHasChanged)
{
if (keepMomentum)
{
StopAllCoroutines();
StartCoroutine(SmoothlyLerpMoveSpeed());
}
else
{
StopAllCoroutines();
moveSpeed = desiredMoveSpeed;
}
}
lastDesiredMoveSpeed = desiredMoveSpeed;
lastState = state;
}
private float speedChangeFactor;
private IEnumerator SmoothlyLerpMoveSpeed()
{
//smoothly Lerp movementSpeed to desired value
float time = 0;
float difference = Mathf.Abs(desiredMoveSpeed - moveSpeed);
float startValue = moveSpeed;
float boostFactor = speedChangeFactor;
while (time < difference)
{
moveSpeed = Mathf.Lerp(startValue, desiredMoveSpeed, time / difference);
time += Time.deltaTime * boostFactor;
yield return null;
}
moveSpeed = desiredMoveSpeed;
speedChangeFactor = 1f;
keepMomentum = false;
}
private void MovePlayer()
{
if (state == MovementState.rolling) return;
//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);
}
}
//Limiting the Y (vertical) Velocity
if (maxYSpeed != 0 && rb.velocity.y > maxYSpeed)
rb.velocity = new Vector3(rb.velocity.x, maxYSpeed, rb.velocity.z);
}
private void Jump()
{
//Initialize jump
if (Input.GetKeyDown(jumpKey) && readyToJump && grounded)
{
isJumping = true;
readyToJump = false;
exitingSlope = true;
//Reset Y Velocity
rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
gasTimeCounter = gasTime;
rb.velocity = new Vector3(rb.velocity.x, Mathf.Sqrt(jumpHeight * -2f * Physics.gravity.y), rb.velocity.z);
readyToJump = false;
}
//Continue jump if held down
if (Input.GetKey(jumpKey) && isJumping)
{
if (gasTimeCounter > 0)
{
rb.velocity = new Vector3(rb.velocity.x, Mathf.Sqrt(jumpHeight * -2f * Physics.gravity.y), rb.velocity.z);
gasTimeCounter -= Time.deltaTime;
}
else
{
isJumping = false;
exitingSlope = false;
readyToJump = true;
}
}
if (Input.GetKeyUp(jumpKey))
{
isJumping = false;
exitingSlope = false;
readyToJump = true;
}
}
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;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rolling : MonoBehaviour
{
[Header("References")]
public Transform orientation;
public Transform playerCam;
private Rigidbody rb;
private PlayerMovement pm;
[Header("Rolling")]
public float rollForce;
public float rollUpwardForce;
public float maxRollYSpeed;
public float rollDuration;
[Header("Camera Effects")]
public ThirdPersonCam cam;
public float rollFov;
//NEW-ish stuff, could be used for bursting
[Header("Settings")]
public bool useCameraForward = true;
public bool allowAllDirections = true;
public bool disableGravity = false;
public bool resetVel = true;
[Header("Cooldown")]
public float rollCd;
private float rollCdTimer;
[Header("Input")]
public KeyCode rollKey = KeyCode.Q;
private void Start()
{
rb = GetComponent<Rigidbody>();
pm = GetComponent<PlayerMovement>();
}
private void Update()
{
if (Input.GetKeyDown(rollKey))
{
Roll();
}
if (rollCdTimer > 0)
rollCdTimer -= Time.deltaTime;
}
private void Roll()
{
if (rollCdTimer > 0) return;
else rollCdTimer = rollCd;
pm.rolling = true;
pm.maxYSpeed = maxRollYSpeed;
cam.DoFov(rollFov);
//NEW Stuff - could be used for bursting (from this line to "rb.useGravity = false;" line (line 68) with exception of "forceToApply" line (line 65))
Transform forwardT;
if (useCameraForward)
forwardT = playerCam;
else
forwardT = orientation;
Vector3 direction = GetDirection(forwardT);
Vector3 forceToApply = direction * rollForce + orientation.up * rollUpwardForce; //NEW EQ has direction * rollForce... old EQ has orientation.forward * rollForce...
if (disableGravity)
rb.useGravity = false;
delayedForceToApply = forceToApply;
Invoke(nameof(DelayedRollForce), 0.025f);
Invoke(nameof(ResetRoll), rollDuration);
}
private Vector3 delayedForceToApply;
private void DelayedRollForce()
{
//NEW Stuff - could be used for bursting
if (resetVel)
rb.velocity = Vector3.zero;
rb.AddForce(delayedForceToApply, ForceMode.Impulse);
}
private void ResetRoll()
{
pm.rolling = false;
pm.maxYSpeed = 0;
cam.DoFov(85f);
//NEW Stuff - could be used for bursting
if (disableGravity)
rb.useGravity = true;
}
//NEW Stuff - could be used for bursting
private Vector3 GetDirection(Transform forwardT)
{
float horizontalInput = Input.GetAxisRaw("Horizontal");
float verticalInput = Input.GetAxisRaw("Vertical");
Vector3 direction = new Vector3();
//Allows bursting in any direction depending on what keys you're pressing
if (allowAllDirections)
direction = forwardT.forward * verticalInput + forwardT.right * horizontalInput;
//Only allows bursting in the direction the player is facing (with playerObj chosen) or the camera is facing (with orientation chosen)
else
direction = forwardT.forward;
//Bursts forward if no keys are pressed
if (verticalInput == 0 && horizontalInput == 0)
direction = forwardT.forward;
return direction.normalized;
}
}