Hello!
Edit: Better example in comment below.
I have a 3rd person controller I’ve created. On a flat even plane it works wonderfully. Slopes however is where the problem occurs. Relevant code below.
private Vector3 GetMoveDirection()
{
Vector3 slopedMoveDirection = GetSlopedMoveDirection();
Vector3 dirOne = Vector3.ProjectOnPlane(_mainCamTransform.forward, Vector3.up).normalized * slopedMoveDirection.z;
Vector3 dirTwo = Vector3.ProjectOnPlane(_mainCamTransform.right, Vector3.up).normalized * slopedMoveDirection.x;
Vector3 dirThree = _transform.up * slopedMoveDirection.y;
Vector3 finalDirection = dirOne + dirTwo + dirThree;
return finalDirection;
}
private Vector3 GetSlopedMoveDirection()
{
Vector3 playerMovement = _playerInput.MoveInput;
if (_isGrounded)
{
playerMovement = new(_playerInput.MoveInput.x, 0f, _playerInput.MoveInput.y);
Vector3 localGroundCheckHitNormal = _transform.InverseTransformDirection(_groundCheckHit.normal);
float groundSlopeAngle = GetSlopeAngle(localGroundCheckHitNormal);
if (groundSlopeAngle != 0f)
{
Quaternion slopeAngleRotation = Quaternion.FromToRotation(_transform.up, localGroundCheckHitNormal);
playerMovement = slopeAngleRotation * playerMovement;
}
}
return playerMovement;
}
This is the code that works fine on the flat plane. I can (mostly) go up the slope the direction the camera is facing smoothly. I’m pretty sure it’s happening due to multiplying the move direction based on the cameras transform. So, I’m pretty sure I just need to incorporate the actual player transform and camera transform at the same time but I’m not sure how to go about it.
Does anyone have any suggestions?
What exactly is the problem? Jitter? Not moving?
One thing to improve might be a minor tweak to both lines 6 and 7 above:
instead of using the camera transforms “straight up,”:
- copy them to a temporary Vector3
- set the .y to zero (this makes them “flat in the world”)
- use the result to project on plane
This should make your movement inputs flat, but not sure if that’s the issue you’re seeing.
It’s a little bit of jitteriness and not really moving, but it only happens depending on which way the camera is facing. In the gif below, look at the Y value in the vector (which is finalDirection printed out). It will stay positive (like it needs to be to go up the slope) when the camera is facing the slope, but it will only turn negative (to go down the slope) when the camera is facing ‘down’ the slope.

I need the camera transform to know which direction to move, but (I think) I need the player transform to properly move up/down the slope, I’m just not sure how to incorporate both.
For anyone who ends up here in the future I managed to resolve my issue. Here are the changes I made.
I calculated the move direction first based on the cameras transform and then rotated that vector using the world _groundCheckHit.normal, not the localized one.
public Vector3 GetMoveDirection()
{
Vector3 playerMovement = new (_playerInput.MoveInput.x, 0f, _playerInput.MoveInput.y);
Vector3 dirOne = Vector3.ProjectOnPlane(_mainCamTransform.forward, Vector3.up).normalized * playerMovement.z;
Vector3 dirTwo = Vector3.ProjectOnPlane(_mainCamTransform.right, Vector3.up).normalized * playerMovement.x;
playerMovement = dirOne + dirTwo;
if (_isGrounded)
{
Vector3 localGroundCheckHitNormal = _transform.InverseTransformDirection(_groundCheckHit.normal);
float groundSlopeAngle = GetSlopeAngle(localGroundCheckHitNormal);
if (groundSlopeAngle != 0f)
{
Quaternion slopeAngleRotation = Quaternion.FromToRotation(_transform.up, _groundCheckHit.normal);
playerMovement = slopeAngleRotation * playerMovement;
}
}
return playerMovement;
}