Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Setting a maximum XZ plane velocity of a rigid body

Discussion in 'Physics' started by helgso, Jul 13, 2015.

  1. helgso

    helgso

    Joined:
    May 3, 2015
    Posts:
    6
    Hey all.

    I'm learning how to make a FPS controller. However, I was never able to force the player to a top speed in Unity 5. Let's attach this script to a 3D capsule and call it our player. I have also attached a zero friction Physic Material to our player so he never slows down by friction. We want to limit the player's top speed on the XZ axis to 1 while the ASDW keys are being pressed. The following code should cap the speed exactly to 1 (walkSpeed) but it doesn't. The speed exceeds 1 (actually goes up to 6.6 on my computer until it stops increasing) while I keep pressing ASDW. If I stop pressing ASDW the player it slows down to 1, the desired top speed, and stays there until I press ASDW again.

    Why does the speed exceed the walkSpeed limit while pressing ASDW? The if (xzVelocity.sqrMagnitude > walkSpeed*walkSpeed) should never let it exceed WalkSpeed which equals 1.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class PlayerMovementScript : MonoBehaviour {
    5.  
    6.     public float walkAccel = 10000f;
    7.     public float walkSpeed = 1f;
    8.  
    9.     Rigidbody playerBody;
    10.  
    11.     void Start () {
    12.         playerBody = GetComponent<Rigidbody> ();
    13.     }
    14.  
    15.     void Update() {
    16.         playerBody.AddRelativeForce (Vector3.forward * Input.GetAxis ("Vertical")
    17.                                      * walkAccel * Time.deltaTime);
    18.         playerBody.AddRelativeForce (Vector3.right * Input.GetAxis ("Horizontal")
    19.                                     * walkAccel * Time.deltaTime);
    20.  
    21.         Vector2 xzVelocity = new Vector2 (playerBody.velocity.x, playerBody.velocity.z);
    22.  
    23.         if (xzVelocity.sqrMagnitude > walkSpeed*walkSpeed) {
    24.             float yVelocity = playerBody.velocity.y;
    25.             xzVelocity = xzVelocity.normalized*walkSpeed;
    26.             playerBody.velocity = new Vector3(xzVelocity.x, yVelocity, xzVelocity.y);
    27.         }
    28.     }
    29. }
     
  2. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    First, put your code in FixedUpdate. Any physics stuff goes there.

    Would this work?
    Code (CSharp):
    1.     void FixedUpdate()
    2.     {
    3.         Vector3 targetVelocity = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")).normalized * walkSpeed;
    4.         Vector3 clampedVelocity = Vector3.ClampMagnitude(targetVelocity - transform.InverseTransformDirection(playerBody.velocity, maxWalkSpeed));
    5.         clampedVelocity.y = 0f;
    6.         playerBody.AddRelativeForce(clampedVelocity);
    7.     }
    8.  
    set walkSpeed to something like 50, and max walk speed to something like 2.
    The reason walkSpeed is higher than walk speed, is because we are using the default addforce, which uses ForceMode.Force, which is similar to multiplying the value by Time.deltaTime (or Time.fixedDeltaTime). If we used ForceMode.VelocityChange, then the walkSpeed would be something like .05f
     
    helgso likes this.
  3. helgso

    helgso

    Joined:
    May 3, 2015
    Posts:
    6
    Your code works great. The only problem is I'd like the player to accelerate and deaccelerate much faster than it does with this code (while keeping the same top speed) . How would I do that?
     
  4. helgso

    helgso

    Joined:
    May 3, 2015
    Posts:
    6
    I used Forcemode.VelocityChange and got this to work as i wanted. Thanks a lot
     
    HiddenMonk likes this.