1
\$\begingroup\$

I have a Rigidbody character controller (Unity, C#) which I'm trying to make work seamlessly when walking on sloped ground. I've got it working fine walking up and down a slope by changing the forward direction to always be parallel to the ground (using the surface normal). But a strange thing is happening when walking sideways on that same slope (and not up and down it)...the character slows to a crawl. Anyone have any idea why this might happen, or how to solve it?

private void Walk()
{
    isMoving = false;

    if (!inChatwithNpc && !ledgeChecker.isGrabbingLedge)
    {

        isMoving = true;

        playerRigidbody.AddForce(moveDirection * moveSpeed /** multiplier*/ * Time.deltaTime);
        

        if (playerOnGround)
        {
            playerRigidbody.velocity = Vector3.Lerp(playerRigidbody.velocity, moveDirection * playerRigidbody.velocity.magnitude, Time.deltaTime / 0.5f);

            if (!isRunning)
            {
                playerRigidbody.velocity = Vector3.ClampMagnitude(moveDirection * playerRigidbody.velocity.magnitude, 12f);
            }

            if (isRunning)
            {
                playerRigidbody.velocity = Vector3.ClampMagnitude(moveDirection * playerRigidbody.velocity.magnitude, 18f);
            }
        }

    }

    if(!isMoving && playerOnGround)
    {
        playerRigidbody.velocity = Vector3.Lerp(playerRigidbody.velocity, Vector3.zero, 0.2f);
    }
}
private void CalculateForward()
{
    if (!playerOnGround)
    {
        forward = transform.forward;
        return;
    }

    forward = Vector3.Cross(transform.right, touch.normal);
\$\endgroup\$
8
  • \$\begingroup\$ Welcome. My experience with AddForce VS set velocity: usually, the more pleasant movement for the main character doesn't have to be physically accurate. Players love accurate physics when falling, maybe when jumping, but hate them when walking. Try replacing AddForce with setting velocity directly. Walking has to be visually pleasant, and get you where you want to go without being a pain. Except for a certain very expensive walking simulator out there, in which walking is supposed to get in the way of the player. \$\endgroup\$ – Hatoru Hansou Oct 24 '20 at 4:24
  • \$\begingroup\$ Thanks for the comment and the welcome Hatoru :) One of the main reasons I have addforce instead of velocity is because I also have a grappling/swinging and wallwalking system in the game and while the grappling rope is attached the player should be able to run along the ground or wall and seemlessly transition into swinging with the grapple (ie attaching the grappling hook to an elevated point in front of a ledge and then running off it). This all feels really good right now but I believe it wouldn't if I set the velocity instead. Continued... \$\endgroup\$ – Relativ9 Oct 25 '20 at 9:02
  • \$\begingroup\$ Continued... I could of course make two different locomotion systems for when grounded and when not, one using set velocity and the other using addforce, but I was hoping to avoid that. For now I've kind of jerry-rigged a solution which sets the local right vector to be perpendicular to the ground normal "slopeRight", and then changes the movement direction to either use local forwarad and local right or the same but crossed with the surface normal if the angles are too extreme. This isn't perfect and still causes some slowdown or skipping if going diagonally down the slope, continued... \$\endgroup\$ – Relativ9 Oct 25 '20 at 9:10
  • \$\begingroup\$ Continued...But it's a lot smoother. If I'm unable to find a better solution (or fine tune this one) I might have to make the switch to setting velocity like you say :) Here's the "fix" I incorperated for anyone having similar problems (there is surprisingly little documentation/toturials about Rigidbody character controllers). Next comment: \$\endgroup\$ – Relativ9 Oct 25 '20 at 9:15
  • \$\begingroup\$ code: moveDirectionSlope = -right * horizontal + forward * vertical; moveDirectionFlat = transform.right * horizontal + transform.forward * vertical; if (groundAngleRight >= 105 || groundAngleRight <= 75) { moveDirection = Vector3.Lerp(moveDirection, moveDirectionFlat, 0.5f); playerOnSlope = false; } else if (playerOnGround || grapplingGun.isGrappling) { moveDirection = Vector3.Lerp(moveDirection, moveDirectionSlope, 0.5f); playerOnSlope = true; } \$\endgroup\$ – Relativ9 Oct 25 '20 at 9:15
1
\$\begingroup\$

Hatoru Hansou adviced that I set velocity instead of addforce, but for various reasons involving my other locomotion requirements (wallwalking and grappeling/swinging) this is something I want to avoid because I want to use the same forces and controls during swinging and wallwalking as I do while walking (just altering speeds and gravity multipliers). So instead what I eventually did was make a imperfect solution that's good enough for now but not ideal:

I noticed that the slowdown only happened when I used my custom forward direction for the movement direction, this was based on my local forward direction crossed with the surface normal of the ground I was walking on. Meaning that my forward direction was always paralell with the slope angle. So I made a if statement that said that if my local right (which I also made paralell with the ground normal) was above or below a certain perpendicular angle (it being 90 on perfectly flat ground) I would revert the forward and right directions to the regular local directions, uncrossed with the ground normal (see code below), I also put a lerp on it to smooth the transition a little bit. This solved the problem somewhat, but when you walk up a slop at extreme diagonal angles there is still a bit of slowdown, and similarly when you walk down there is some skipping (due to forward now pointing out from the slope instead of down along it).

If anyone has a better way to solve this problem I'd love to hear it!

moveDirectionSlope = -right * horizontal + forward * vertical;
moveDirectionFlat = transform.right * horizontal + transform.forward * vertical;          if (groundAngleRight >= 105 || groundAngleRight <= 75)         
{    moveDirection = Vector3.Lerp(moveDirection, moveDirectionFlat, 0.5f);
     playerOnSlope = false;      
} else if (playerOnGround || grapplingGun.isGrappling)         
{    moveDirection = Vector3.Lerp(moveDirection, moveDirectionSlope, 0.5f);
     playerOnSlope = true;  
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.