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
  4. Dismiss Notice

Question Unable to set max velocity for Rigidbody and code examples that claim to work online do nothing.

Discussion in 'Scripting' started by archentitykalim, Jul 24, 2023.

  1. archentitykalim

    archentitykalim

    Joined:
    May 15, 2019
    Posts:
    6
    I am trying to set the max velocity to a tank with the script below. It doesn't matter if I place the velocity clamping code in Update or FixedUpdate, I've tried various code snippets from forums in each, nothing clamps the speed. Here is my code that controls the tank below. The tank is designed to move forward and backward by using forces and rotate left and right by using transform.Rotate. Someone please help me implement this the proper way.

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class PlayerMovement : MonoBehaviour
    7. {
    8.     public float speed = 85.0f;
    9.     public float turnSpeed = 30.0f;
    10.     public float maxSpeed = 0.01f;
    11.     public float currentVelocityMagnitude;
    12.  
    13.     private float horizontalInput;
    14.     private float verticalInput;
    15.     private Rigidbody tankRigidbody;
    16.  
    17.     // Start is called before the first frame update
    18.     void Start()
    19.     {
    20.         // Lower the player tank's center of mass so that the vehicle won't flip during movement.
    21.         GetComponent<Rigidbody>().centerOfMass += new Vector3(0, -1f, 0);
    22.  
    23.         // Get the tank's rigid body component to avoid unecessarily calling the "GetComponent<Rigidbody>()" function later.
    24.         tankRigidbody = GetComponent<Rigidbody>();
    25.     }
    26.  
    27.     // Update is called once per frame
    28.     void Update()
    29.     {
    30.         // Gather input from axes in Input Manager
    31.         horizontalInput = Input.GetAxis("Horizontal");
    32.         verticalInput = Input.GetAxis("Vertical");
    33.  
    34.         // Turn the tank left or right based on horizontal input
    35.         transform.Rotate(Vector3.up * Time.deltaTime * turnSpeed * horizontalInput);
    36.     }
    37.  
    38.     void FixedUpdate()
    39.     {
    40.         // Record current velocity for TESTING.
    41.         currentVelocityMagnitude = tankRigidbody.velocity.magnitude;
    42.  
    43.         // Move the tank forward and backward based on vertical input
    44.         tankRigidbody.AddRelativeForce(0, 0, speed * verticalInput, ForceMode.VelocityChange);
    45.  
    46.         // Ensure player tank's max velocity doesn't exceed the defined max velocity.
    47.         if (tankRigidbody.velocity.magnitude > maxSpeed)
    48.         {
    49.             tankRigidbody.velocity = tankRigidbody.velocity.normalized * maxSpeed;
    50.         }
    51.     }
    52. }
    53.  
     
  2. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    549
    You can let the physics engine dictate the max speed by using drag and friction. The laws of physics dictates the maximum speed of things. There is no hidden intelligent force watching over a car to make sure its speed doesn't go over a value in God's database.

    But if you really want to dictate the max speed then you can do something like this:

    Code (CSharp):
    1.     void FixedUpdate()
    2.     {
    3.         Vector3 moveDir=tankRigidbody.velocity.normalized;
    4.         Vector3 pushDir=new Vector3(Input.GetAxisRaw("Horizontal"),0,Input.GetAxisRaw("Vertical")).normalized;
    5.         // Push the tank if its moving below max speed or if pushed in the opposite direction of travel (we still want to be able to slow down if moving too quickly (maybe going downhill))
    6.         if (tankRigidbody.velocity.magnitude<maxSpeed || (Vector3.Dot(moveDir,pushDir)<0))
    7.             tankRigidbody.AddRelativeForce(0, 0, speed * verticalInput, ForceMode.VelocityChange);
    8.     }
     
  3. Elhimp

    Elhimp

    Joined:
    Jan 6, 2013
    Posts:
    71
    Code (CSharp):
    1. Vector3 thrust = Quaternion.Inverse(body.rotation) * -body.localVelocity;
    2.  
    3. // "m" is how powerful force impulse is
    4. // where 4.0 making velocity change sharp and lower values make movement "floaty"
    5. float delta = (desiredVelocity + thrust.z) * m;
    6.  
    7. // set drag to zero or increase this value to overcome it
    8. thrust.z = Mathf.Clamp(delta, deceleration, acceleration);
    9.  
    10. body.AddRelativeForce(thrust, ForceMode.Acceleration);
     
    Last edited: Jul 25, 2023
  4. archentitykalim

    archentitykalim

    Joined:
    May 15, 2019
    Posts:
    6
    For testing purposes I'm sure I can ignore this part:

    Vector3 moveDir=tankRigidbody.velocity.normalized;
    Vector3 pushDir=new Vector3(Input.GetAxisRaw("Horizontal"),0,Input.GetAxisRaw("Vertical")).normalized;

    and this part:

    || (Vector3.Dot(moveDir,pushDir)<0)

    as the tank will never be pushed in the opposite direction of travel since I have no hills or ramps in the scene. With this in mind I tried the following portion of your posted code:

    if (tankRigidbody.velocity.magnitude < maxSpeed)
    {
    // Move the tank forward and backward based on vertical input
    tankRigidbody.AddRelativeForce(0, 0, speed * verticalInput, ForceMode.VelocityChange);
    }

    This still doesn't achieve the desired effect and instead makes the tank rapidly jump to a velocity above the maxSpeed and back to the maxSpeed while increasing the current speed variable in the Inspector window during testing. Did you have this issue when you tested your code?
     
  5. archentitykalim

    archentitykalim

    Joined:
    May 15, 2019
    Posts:
    6
    This solution is to complex for me as I am still a beginner at Unity and C#.
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,917
    I mean your add force method is using
    ForceMode.VelocityChange
    which immediately jumps to a certain velocity, so I'm not sure why you're surprised at your current behaviour.

    If you want a gradual chance you want to use the other modes.
     
    Ryiah and Yoreki like this.
  7. ijmmai

    ijmmai

    Joined:
    Jun 9, 2023
    Posts:
    188
    You could add a threshold. I am no expert on this, but if the speed is below the max speed, the next force could easily put it over that max speed.
     
  8. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    549
    You may need to lower the speed variable in the inspector if it's making the tank move too quickly. But I suspect the real issue is that you've done a typo and your script is changing the speed variable for some reason. The code I posted doesn't change the speed variable.
     
    Last edited: Jul 26, 2023
  9. archentitykalim

    archentitykalim

    Joined:
    May 15, 2019
    Posts:
    6
    Even the following code in Update:

    Code (CSharp):
    1.         // For TESTING
    2.         if (speed < 500)
    3.         {
    4.             speed += 10;
    5.         }
    and the following code in FixedUpdate:

    Code (CSharp):
    1. tankRigidbody.AddRelativeForce(0, 0, speed * verticalInput, ForceMode.VelocityChange);
    2. tankRigidbody.velocity = Vector3.ClampMagnitude(tankRigidbody.velocity, maxSpeed);
    still fail to limit the tank to the maxSpeed value even though each of these code snippets are the last line of code in both Update and FixedUpdate. Can you please test this with the following starting values for speed and maxSpeed?:

    Code (CSharp):
    1. [SerializeField] float speed = 10.0f;
    2. [SerializeField] float maxSpeed = 5.0f;
    3.  
    I'm trying to understand why the maxSpeed is ignored even though the very last thing done on each FixedUpdate loop is literally supposed to clamp the rigidbody's vector to the maxSpeed value.
     
  10. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    549
    Strange.. I've not had any problems clamping the velocity of a rigidbody

    You may be struggling with a weird quirk of Unity in that if you make an initialized variable visible to the inspector by using public or [SerializeField] then later you decide to change the initialized value in script, Unity won't register the new value until you reset the script in the Inspector. So perhaps you're changing maxSpeed in the script and not in the Inspector?
     
  11. archentitykalim

    archentitykalim

    Joined:
    May 15, 2019
    Posts:
    6
    I don't even have to change maxSpeed in the inspector or the script during testing and I still run into this same problem where maxSpeed gets ignored. I only change the current speed during testing and changing it within the inspector during play mode or with code as mentioned above still doesn't do anything different. Did the same above code work for you?
     
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,546
    Do you know that when you add a force, the effects such as changing the linear or angular velocity don't happen until the simulation runs? Adding a force or lots of forces are summed then simulated later. Adding a force then immediately clamping the velocity is pointless. You'll be clamping the velocity from the previous simulation step. This is clearly documented in the docs here.

    If you look at the docs for Rigidbody you'll also find Rigidbody.maxLinearVelocity although it is possible to exceed this max temporarily until the next simulation step using this but that's also documented.

    If you absolutely must change the velocity there and then, you can directly add a value to the Rigidbody.velocity
     
    archentitykalim and Ryiah like this.
  13. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    549
    Yes.

    Try this:
    Code (CSharp):
    1.         tankRigidbody.AddRelativeForce(0, 0, speed * verticalInput, ForceMode.VelocityChange);
    2.         Debug.Log(maxSpeed);
    3.         tankRigidbody.velocity = Vector3.ClampMagnitude(tankRigidbody.velocity, maxSpeed);

    Now you'll be able to see if maxSpeed is actually set to what you think it should be set to.


    .
     
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,546
    As long as the OP understands that this'll output the same velocity value for before and after:
    Code (CSharp):
    1. Debug.Log($"Before: {tankRigidbody.velocity}");
    2. tankRigidbody.AddRelativeForce(0, 0, speed * verticalInput, ForceMode.VelocityChange);
    3. Debug.Log($"After: {tankRigidbody.velocity}");
     
    Kurt-Dekker and spiney199 like this.
  15. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    549
    .
    After reading MelvMay's messages I did some more tests and he's right. If you add too much force to a rigidbody then clamping its velocity becomes unreliable.

    Personally I wouldn't try to restrict a rigidbody's velocity directly but if you really feel its necessary then try being less forceful when moving your objects around. Try using ForceMode.Impulse.
     
  16. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,546
    It's not about too much. If you're referring to the maxVelocity property, if you read the docs you'll see it says the clamping happens before the simulation step so post simulation, it can move beyond that, until the next simulation step.

    All I see above is confusion on what things do and when it happens. :)
     
  17. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    549
    Oooh! I didn't even know there was a maxLinearVelocity. Unity 2021 user here!. :)
     
    MelvMay likes this.