Limiting rigidbody velocity
Asked Answered
D

8

0

When working with non-kinematic rigidbodies (especially bouncy ones), there can be situations where collisions with static colliders and other rigidbodies result in large increases in velocity. This can lead to undesirable effects, such as objects suddenly "teleporting" to random parts of the screen or breaking through static collider scenery.

What's the best way to limit the velocity of a non-kinematic rigidbody?

Defense answered 15/11, 2023 at 11:2 Comment(1)

Could someone take a look at my post and let me know if this thread is really one and the same issue? You only mention collisions with known objects in this thread... in my scenario I have no collisions with known objects except, if any, the terrain itself... and also do see in my post that it happens while vehicle is at complete idle. http://answers.unity3d.com/questions/20456/terrain-unexpected-collisions-loopidy-loops

Spacing
C
0

I can think of two methods of doing this, one of them is recommended against by UT though.

First, check the magnitude of your rigidbody.velocity][1].

If it's under the speed limit, take no action. If it's over the speed limit you can do one of two things:

  • The 'proper' way would be to apply a force in the opposite direction of the rigidbody's velocity. The amount of force should be proportional to the extent to which the rigidbody is exceeding its speed limit.

  • The naughty, non-recommended way would be to take the current velocity vector, and if its magnitude exceeds the speed limit, normalize the vector3, multiply it by the speed limit, and directly reassign the velocity vector.

I'm not entirely sure whether this second method comes recommended against just because of "non realism", or whether there could be more serious simulation-breaking consequences.

Callicrates answered 12/9, 2023 at 19:46 Comment(5)

Thanks, Duck. My current approach is to gradually increase drag as the velocity approaches the limit, and then use the second approach (normalize/multiply) to clamp it. I've noticed that the velocity can exceed the "speed limit" (instantaneously, in some cases) in FixedUpdate, OnCollisionEnter, or OnCollisionExit, so I'm checking it all three places. But this feels a bit heavy-handed and overly complex, not to mention sub-optimal from a performance perspective. Also, as you mention, I'm concerned I might be unintentionally creating some undesirable side-effects.

Defense

While this works (and I don't think directly changing the velocity here should pose a problem really), if you do this on a lot of objects, and in many places, consider checking against the sqrMagnitude of the velocity instead, as that is faster. May not make much of a difference here, but it's always good practice.

Stalker

Wouldnt the Method Vector2.ClampMagnitude(Vector2, float) or the Vector3 equivalent be an interesting alternative? rigidbody.velocity = Vector3.ClampMagnitude(rigidbody.velocity, 10f);

Beaverette

thank you

Riparian

How would you determine the magnitude "speed limit" of a rigidbody? I am having difficulties with a ball that will break through colliders, and I can't determine the speed at which it is doing so. I understand how to figure the magnitude of vectors, so that is not the problem. Simply determining the rigid body's maximum speed (the velocity at which it breaks through the colliders) is the difficulty.

Zitella
D
0

I just posted a couple scripts for limiting velocity, along with an example project, to my blog. Here is the script for Duck's proposed solution.

#pragma strict

// This MonoBehaviour uses hard clamping to limit the velocity of a rigidbody.

// The maximum allowed velocity. The velocity will be clamped to keep 
// it from exceeding this value.
var maxVelocity : float;

// The cached rigidbody reference.
private var rb : Rigidbody;
// A cached copy of the squared max velocity. Used in FixedUpdate.
private var sqrMaxVelocity : float;

// Awake is a built-in unity function that is called called only once during the lifetime of the script instance.
// It is called after all objects are initialized.
// For more info, see:
// http://unity3d.com/support/documentation/ScriptReference/MonoBehaviour.Awake.html
function Awake() {
    rb = rigidbody;
    SetMaxVelocity(maxVelocity);
}

// Sets the max velocity and calculates the squared max velocity for use in FixedUpdate.
// Outside callers who wish to modify the max velocity should use this function. Otherwise,
// the cached squared velocity will not be recalculated.
function SetMaxVelocity(maxVelocity : float){
    this.maxVelocity = maxVelocity;
    sqrMaxVelocity = maxVelocity * maxVelocity;
}

// FixedUpdate is a built-in unity function that is called every fixed framerate frame.
// We use FixedUpdate instead of Update here because the docs recommend doing so when
// dealing with rigidbodies.
// For more info, see:
// http://unity3d.com/support/documentation/ScriptReference/MonoBehaviour.FixedUpdate.html 
function FixedUpdate() {
    var v = rb.velocity;
    // Clamp the velocity, if necessary
    // Use sqrMagnitude instead of magnitude for performance reasons.
    if(v.sqrMagnitude > sqrMaxVelocity){ // Equivalent to: rigidbody.velocity.magnitude > maxVelocity, but faster.
        // Vector3.normalized returns this vector with a magnitude 
        // of 1. This ensures that we're not messing with the 
        // direction of the vector, only its magnitude.
        rb.velocity = v.normalized * maxVelocity;
    }   
}

// Require a Rigidbody component to be attached to the same GameObject.
@script RequireComponent(Rigidbody)

Defense answered 6/6, 2023 at 2:34 Comment(2)

nice blog too btw!

Halinahalite

The logic and calculations are correct.

Scrivings
S
0

here’s how I did it quick & dirty:

// clamp velocity:
Vector3 newVelocity = rigidbody.velocity.normalized;
newVelocity *= m_MaximumVelocity;
rigidbody.velocity = newVelocity;
Sadden answered 14/1, 2013 at 21:59 Comment(1)

This was the best answer for me, only slightly modified (my game is 2d / c#). if(Mathf.Abs(rigidbody.velocity.x) > maxSpeed || Mathf.Abs(rigidbody.velocity.y) > maxSpeed) { // clamp velocity: Vector3 newVelocity = rigidbody.velocity.normalized; newVelocity *= maxSpeed; rigidbody.velocity = newVelocity; }

Clevie
E
0

i found that doing this was pretty simple and worked:

if( rigidbody.velocity.sqrMagnitude > maxVelocity )
{
    //smoothness of the slowdown is controlled by the 0.99f, 
    //0.5f is less smooth, 0.9999f is more smooth
    rigidbody.velocity *= 0.99f;
}
Elderberry answered 25/12, 2023 at 18:55 Comment(3)

wow, nice approach, just this works for me now. thanks, man!

Thornton

OMG... This was simple and works. In Cherub we trust! Thank you for such an elegant solution. 0.99 will still drop rigidbodies like meteorites, but between 0.5 and 0.999 u can definitely find a a sweet spot.

Trophoplasm

this is amazing, worked in 2d like a charm...

Auspex
E
1

The problem with doing the dirty example is defining the velocity of an object is against the physics system. It is like stopping the object from moving and sending it moving again.

Also cherub got us started on doing that every single processing frame, which is damage by your program to the physics engine! You would find yourself a lot better off using rigidbody.drag somehow, though I can’t say that 100% is a good thing.

Here is my “definitive” answer to braking an object to a maximum speed:

float speed = Vector3.Magnitude( rigidbody.velocity );  // test current object speed   
if (speed > maximumSpeed)  
{
    float brakeSpeed = speed - maximumSpeed;  // calculate the speed decrease
    Vector3 normalisedVelocity = rigidbody.velocity.normalized;
    Vector3 brakeVelocity = normalisedVelocity * brakeSpeed;  // make the brake Vector3 value  
    rigidbody.AddForce( -brakeVelocity );  // apply opposing brake force
}

Sorry Ehren, but I find your answer very unreadable on this page!

Envoy answered 15/11, 2023 at 11:4 Comment(2)

Can you please help with translate to vector2 for a 2d game or would this work for a 2d game

Triatomic

the problem is that this approach doesn't take the mass into account.

Gnome
M
0

the best way to keep velocity constant i have found is:

    void FixedUpdate () {
    		if (thisRigid.velocity.magnitude > maxVelocityMagnitude) {
                float x = thisRigid.velocity.magnitude / maxVelocityMagnitude; 
    			thisRigid.velocity /= x;
    		}
    	}

First, see how much has the velocity increased compared to maxVelocity.
Then divide the actual vector velocity by this number to get back instantly to maxVelocity.

Note: the relation between velocity and rigidbody2D.velocity.magnitude is 1/50 (magnit = vel/50).

Madlynmadman answered 5/5, 2015 at 3:30 Comment(0)
B
0

I down-voted the last comment.

Rigidbody travelling at a Vector3 velocity

const float max floatVelocity;
Vector3 vector3Velocity;

Usually you look at the rigidBody in question to see why the velocity’s maximum is set very high. Does it need a limit function, or velocity maximum?

If this limit function is required, use Unity Behaviour as a helper.

if (NormalisedToFloat (vector3Velocity) > maxfloatVelocity)
   rigidbody.drag = dragAmount;
Benedictine answered 15/11, 2023 at 11:6 Comment(0)
E
0

For anyone looking for something simple and that works (thanks ChatGPT):

public float maxSpeed = 40;
private Rigidbody rigidbody;

void Awake()
{
    rigidbody = GetComponent<Rigidbody>();
}

void FixedUpdate() {
    if (rigidbody.velocity.magnitude > maxSpeed)
        rigidbody.velocity = rigidbody.velocity.normalized * maxSpeed;
}
Extroversion answered 15/11, 2023 at 10:29 Comment(1)

This is the second way from the first reply, basically

Culp

© 2022 - 2024 — McMap. All rights reserved.