# Character walks slowly when walking down a slope, but walks normally when walking up a slope

I’ve run into this problem while coding a movement system for my game, and I’ve scoured google but nothing has really helped that much. My problem is that when my character walks up a slope, they go at a normal speed, but when walking down a slope their speed is much slower. I’m not sure why, although I think I’ve narrowed it down to the fact that the movement code organically increases the players speed to the target speed, because whenever I take out this chunk of code, this problem doesn’t exist, but the game has a less organic feel to it which I don’t really like.

Here’s a video demonstrating this problem:

And here’s the code for the movement system:

``````    private void Move()
{
// set target speed based on move speed, sprint speed and if sprint is pressed
float targetSpeed = playerStats.currentSpeed;
// if there is no input, set the target speed to 0
if (_input.moveInput == Vector2.zero)
targetSpeed = 0.0f;
// a reference to the players current horizontal velocity
float currentHorizontalSpeed = new Vector3(_controller.velocity.x, 0.0f, _controller.velocity.z).magnitude;
float speedOffset = 0.1f;
float inputMagnitude = _input.analogMovement ? _input.moveInput.magnitude : 1f;
// accelerate or decelerate to target speed
if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)
{
// creates curved result rather than a linear one giving a more organic speed change
_speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate);
// round speed to 3 decimal places
_speed = Mathf.Round(_speed * 1000f) / 1000f;
}
else
{
_speed = targetSpeed;
}
var verticalAxis = new Vector3(cameraTransform.forward.x, 0, cameraTransform.forward.z).normalized;
var horizontalAxis = new Vector3(cameraTransform.right.x, 0, cameraTransform.right.z).normalized;
// normalise input direction
inputDirection = new Vector3(_input.moveInput.x, 0.0f, _input.moveInput.y).normalized;
inputDirection = inputDirection.x * horizontalAxis + inputDirection.z * verticalAxis;
inputDirection.Normalize();
float magnitude = Mathf.Clamp01(inputDirection.magnitude) * _speed;
Quaternion targetRotation = Quaternion.Euler(0, cameraTransform.eulerAngles.y, 0);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, 5 * Time.deltaTime);
Vector3 velocity = inputDirection * magnitude;
velocity.y += _verticalVelocity;
_controller.Move(velocity * Time.deltaTime); //Cinemago?
}
private void JumpAndGravity()
{
isGrounded = _controller.isGrounded;
if (isGrounded)
{
// stop our velocity dropping infinitely when grounded
if (_verticalVelocity < 0.0f)
{
lastGroundedTime = Time.time;
_verticalVelocity = -2f;
}
}
//If you press the space bar, starts a timer
if (_input.jump)
{
lastJumpButtonInput = Time.time;
}
//If the timer - when you last touched the ground is <= to the jump grace period, your step offset is set to its original value
if (Time.time - lastGroundedTime <= jumpGracePeriod)
{
_controller.stepOffset = originalStepOffset;
//If the timer - when you last pressed the jump input is <= to the jump grace period, jump
if(Time.time - lastJumpButtonInput <= jumpGracePeriod)
{
_verticalVelocity = Mathf.Sqrt(playerStats.jumpHeight * -2f * gravity);
lastJumpButtonInput = null;
lastGroundedTime = null;
}
}
//If in the air/jumping, step offset is set to 0 to prevent glitching in walls and other bugs
else
{
_controller.stepOffset = 0;
}
//apply gravity over time if under terminal (multiply by delta time twice to linearly speed up over time)
if (_verticalVelocity < _terminalVelocity)
_verticalVelocity += gravity * Time.deltaTime;
}
{
var ray = new Ray(transform.position, Vector3.down);
if (Physics.Raycast(ray, out RaycastHit hitInfo, 1.5f))
{
var slopeRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
var adjustedVelocity = slopeRotation * velocity;
}
return velocity;
}
``````

And here’s the moveInput script:

``````public class Inputs : MonoBehaviour
{
public void OnMove(InputValue value)
{
MoveInput(value.Get<Vector2>());
}
public void MoveInput(Vector2 newMoveDirection)
{
moveInput = newMoveDirection;
}
}
``````

Alright, so I’ve figured out that the steeper the slope, the slower you go when moving down. I’m not entirely sure how helpful this is, but if anyone can find something out from that, it would help me alot.

I’ve figured out that the character only walks slow when going down a slope because of this line of code

``````if (adjustedVelocity.y < 0)
``````

So if I take this out, they walk slowly going both up and down the slope. Which means they are just generally slow on a slope, which I’ve tried to find a fix for, but I can’t. I have noticed that like stated in my original post, if I take out this chunk of code:

``````if (currentHorizontalSpeed < targetSpeed - speedOffset || currentHorizontalSpeed > targetSpeed + speedOffset)
{
// creates curved result rather than a linear one giving a more organic speed change
_speed = Mathf.Lerp(currentHorizontalSpeed, targetSpeed * inputMagnitude, Time.deltaTime * SpeedChangeRate);
// round speed to 3 decimal places
_speed = Mathf.Round(_speed * 1000f) / 1000f;
}
else
{
_speed = targetSpeed;
}
``````

Then the character walks fine on slopes, but they have a less natural feel to them, which I don’t want for my game. If anyone has a fix for this, that would be greatly appreciated.

Also: I’ve encountered another bug where the character hangs off the edge of objects instead of just falling down. And because they are floating on the edge, they aren’t grounded, so their vertical velocity constantly decreases until they move off the edge, at which point they will just snap to the ground due to the very low vertical velocity.

If someone can help with either of these problems, I would greatly appreciate it! Thanks in advance

I’m under the impression that you’re trying to make a complex system work by removing and adding stuff without understanding the simple parts of the system.

as we discussed in another thread, you’re still using Normalized() on the move vector, which in turn creates this “less natural feel”. You then are trying to mitigate this problem by lerping, which probably creates other problems.

Am I right that you copied the code for the whole movement system, (This is not a bad thing) but you don’t understand every single line?

If so, do yourself a favor and go through a character controller tutorial (Brackey has great ones on YouTube).

As for the hanging on edges problem: this is probably because the ray cast down misses the object your standing on because you moved the center of the character controller over the edge and now it collides normally with the floor but “thinks” the character is in the air.

I’d swap the raycast for a sphere cast with the same radius as the character capsule to make this work

tjmaul, I realise now that I was actually in over my head trying to do something I don’t yet possess the skills to accomplish. I’m just not capable of doing it yet, and asking other people isn’t going to help with that inherent problem. So I reworked the movement to be simpler, and now it works, and I understand it, which is all I could really ask for. Thanks for the help anyways. I’ll try doing easier things first, and work my way up to complex stuff.

1 Like

That’s actually really wise. Good luck with all your endeavors!