Page 1 of 1

Physics - Limit velocity

Posted: Thu Dec 10, 2015 5:50 pm
by Ulydev
Hello everyone,

I've been playing around with Box2D and I'm quite surprised there's no simple way to limit an object's velocity.

A workaround I found is to check the current velocity of the object and if it's too high, just apply a counter-force to the desired value. But this is very brutal and looks somewhat a bit jittery.

Is there a better way to do that?
Thanks in advance. ;)

Re: Physics - Limit velocity

Posted: Thu Dec 10, 2015 6:19 pm
by FullyLucid
You should be able to just set it's linear velocity if it's higher than you want. Other than that, there's a settings file in Box2D itself that has a maximum linear velocity for all solid bodies, not individually.

Code: Select all

local xvel, yvel = body:getLinearVelocity()
body:setLinearVelocity( math.min(xvel, max_xvel), math.min(yvel, max_yvel) )
You could also play with body:setLinearDamping() since you're getting oscillation you don't want, but I think applying an inverse impulse and adjusting for oscillation is plenty slower than changing the linear velocity by hand.

Re: Physics - Limit velocity

Posted: Mon Dec 14, 2015 11:40 am
by Ulydev
FullyLucid wrote:-snip-
Hey there, thanks for your answer! Unfortunately, that still is very buggy and looks really jittery, especially on small frame drops. Is there a less brutal way of doing it?

Re: Physics - Limit velocity

Posted: Mon Dec 14, 2015 4:11 pm
by ArchAngel075
I would look at it by taking collisions with the limited body and after resolving check velocity and alter/decrease etc.
This should catch that instant it speeds up or slows down on collide and instead 'lock' the velocity after collisions.
Similarly any 'addForce/addVelocity' calls can be wrapped to prevent it from exceeding the limit?

Re: Physics - Limit velocity

Posted: Fri Dec 08, 2017 10:11 am
by Marty
FullyLucid wrote: Thu Dec 10, 2015 6:19 pm You should be able to just set it's linear velocity if it's higher than you want. Other than that, there's a settings file in Box2D itself that has a maximum linear velocity for all solid bodies, not individually.

Code: Select all

local xvel, yvel = body:getLinearVelocity()
body:setLinearVelocity( math.min(xvel, max_xvel), math.min(yvel, max_yvel) )
You could also play with body:setLinearDamping() since you're getting oscillation you don't want, but I think applying an inverse impulse and adjusting for oscillation is plenty slower than changing the linear velocity by hand.
Sorry for reviving this old thread, but I've a problem with this code. I want to limit the velocity of my object that got it through applyLinearImpulse:

Code: Select all

if love.mouse.isDown(1) and canShot
 	canShot = false
 	local clickVector = Vector(love.mouse:getX(), love.mouse:getY())
	local bulletVector = Vector(body:getX(), body:getY())
	local distVector = (clickVector - bulletVector)
	local dirVector = distVector:normalized()
	body:applyLinearImpulse(dirVector.x * power, dirVector.y * power) --power is defined somewhere else
end
local velX, velY = body:getLinearVelocity()
body:setLinearVelocity(math.min(velX, maxSpeed), math.min(velY, maxSpeed)) --maxSpeed is defined somewhere else
As you can see I already try to give a fixed speed with my power variable. canShot will be true when my object collides with the ground again. My object is bouncy for the looks, tho. This means the user can profit from the force that gets applied when it bounces back to get even more force, jumping higher and higher. I don't want that.

In result applyLinearImpulse + existing force from bouncing back is stronger than my maxSpeed, eventually, and for some reason it does not get limited with the code above. However, once my impulse is over and the object reaches it highest point and falls down again, it gets limited, and thus slowly falls down.

What is the correct way to limit the speed of my object when the velocity is resulting from applyLinearImpulse?

Re: Physics - Limit velocity

Posted: Fri Dec 08, 2017 3:46 pm
by ivan
I recommend:

Code: Select all

--- Clamp length
-- @param v vector
-- @param d maximum length
-- @return initial length of the vector
function clamp(v, d)
  local x, y = v.x, v.y
  local d2 = math.sqrt(x*x + y*y)
  if d2 > d then
    v.x = x/d2*d
    v.y = y/d2*d
  end
  return d2
end
What is the correct way to limit the speed of my object when the velocity is resulting from applyLinearImpulse?
If you want to change the velocity using impulses then:
changeInVelocity = finalVelocity - initialVelocity
impulse = mass*changeInVelocity
Apply the impulse only once to achieve the DESIRED velocity.
The "desired" velocity should remain constant unless one of the following is involved:
1.gravity 2.collisions 3.linear damping 4.joints.
If you want to maintain a constant velocity over time, then use ApplyForce.
setLinearVelocity is not recommended for dynamic bodies, because it overrides the effects of "collision responses" and doesn't look realistic.

Re: Physics - Limit velocity

Posted: Fri Dec 08, 2017 11:18 pm
by Marty
ivan wrote: Fri Dec 08, 2017 3:46 pm If you want to change the velocity using impulses then:
changeInVelocity = finalVelocity - initialVelocity
impulse = mass*changeInVelocity
Apply the impulse only once to achieve the DESIRED velocity.
The "desired" velocity should remain constant unless one of the following is involved:
1.gravity 2.collisions 3.linear damping 4.joints.
If you want to maintain a constant velocity over time, then use ApplyForce.
setLinearVelocity is not recommended for dynamic bodies, because it overrides the effects of "collision responses" and doesn't look realistic.
Thank you for your reply, this helped a lot, although it took me a while to get my head around this. Indeed, it makes more sense to calculate the correct vector in before-hand, instead of correcting the vector with additional forces afterwards.

First of all, I didn't know that any impulses respecting the mass of my body, but this makes sense. I couldn't see this effect when changing my mass, because I was setting the mass before creating fixtures for my body. After creating the fixtures it changed my mass.

So to sum it up with my own words:
- I get the linear velocity of my body
- I multiply my power with my direction and subtract the current linear velocity from it
- I multiply the result with the mass of the body
- The result is the force that will move my object the same distance without respecting mass and ignoring initial velocity <- this gonna be applied as impulse
- since the initial velocity of my object needs to be multiplied by the mass to get enough counter force on the impulse, I have to multiply the mass to it, always

For the community, here is my solution: An update function code to shoot an object in the direction of my mouse click without respecting mass and ignoring initial velocity. (setMass will be called at the end of the enter function, canShot will be set true on ground collision, power is pre-configured):

Code: Select all

		if love.mouse.isDown(1) and canShot and not releasedShot then
			canShot = false
			releasedShot = true

			local clickVector = Vector(love.mouse:getX(), love.mouse:getY())
			local bulletPosVector = Vector(body:getX(), body:getY())
			local bulletVelVector = Vector(body:getLinearVelocity())
			local distVector = (clickVector - bulletPosVector)
			local dirVector = distVector:normalized()

			body:applyLinearImpulse(body:getMass() * (power * dirVector.x - bulletVelVector.x), body:getMass() * (power * dirVector.y - bulletVelVector.y))
			body:applyAngularImpulse(dirVector.x * power, dirVector.y * power) -- rotation can stay wild!!!

		elseif canShot and releasedShot and not love.mouse.isDown(1) then
			releasedShot = false
		end
(for easier presentation I removed architecture relevant parts, of course I don't work with global variables in this case)

Any suggestions? I'm new to this and I appreciate any feedback to improve myself. :3

ivan wrote: Fri Dec 08, 2017 3:46 pm I recommend:

Code: Select all

--- Clamp length
-- @param v vector
-- @param d maximum length
-- @return initial length of the vector
function clamp(v, d)
  local x, y = v.x, v.y
  local d2 = math.sqrt(x*x + y*y)
  if d2 > d then
    v.x = x/d2*d
    v.y = y/d2*d
  end
  return d2
end
I've to admit, I don't get the exact use for this function. Can you explain further? :roll:

Re: Physics - Limit velocity

Posted: Sat Dec 09, 2017 7:21 am
by ivan
modiX, you're on the right path, but your terminology is slightly off.
Please note that force and power mean very specific things in physics. ;)

Ok, back to your question - you want to limit the velocity of a body.
The easiest way to do that is using linear damping.
If you want to "clamp" the velocity to an exact value without the "easing" effect of damping you can try:

1. Limit the velocity ONLY if it exceeds maxSpeed

Code: Select all

function clamp(x, y, d)
  local d2 = math.sqrt(x*x + y*y)
  if d2 > d then
    x = x/d2*d
    y = y/d2*d
  end
  return x, y, d2
end

--- Example:
local vx, vy = body:getLinearVelocity()
-- limits the velocity ONLY if it exceeds maxSpeed:
vx, vy = clamp(vx, vy, maxSpeed)
body:setLinearVelocity(vx, vy)
2. Overwrite the velocity if it's non-zero

Code: Select all

function normalize(x, y, d)
  d = d or 1
  local d2 = math.sqrt(x*x + y*y)
  if d2 > 0 then
    x = x/d2*d
    y = y/d2*d
  end
  return x, y, d2
end

--- Example:
local vx, vy = body:getLinearVelocity()
vx, vy = normalize(vx, vy, maxSpeed)
body:setLinearVelocity(vx, vy)
3. Using a linear impulse to achieve an exact velocity

Code: Select all

local tx, ty = ? -- target or final velocity
local vx, vy = body:getLinearVelocity() -- initial velocity
local dx, dy = tx - vx, ty - vy -- change in velocity
local ix, iy = dx*mass, dy*mass -- impulse = change in velocity*mass
body:applyLinearImpulse(ix, iy)
in your case the "target velocity" can be calculated as:

Code: Select all

local mx, my = love.mouse.getPosition()
local bx, by = body:getPosition()
local cx, cy = mx - bx, my - by
-- target velocity:
local tx, ty = normalize(cx, cy, targetSpeed)
Again, the linear impulse must be applied once and it won't work precisely if there is gravity,joints or damping involved.

Re: Physics - Limit velocity

Posted: Sat Dec 09, 2017 11:45 pm
by Marty
ivan wrote: Sat Dec 09, 2017 7:21 am modiX, you're on the right path, but your terminology is slightly off.
Please note that force and power mean very specific things in physics. ;)

Ok, back to your question - you want to limit the velocity of a body.
The easiest way to do that is using linear damping.
Hey ivan, you are right, my terminology is slightly off, indeed. My solution will not limit the vector, it will apply a static force in the clicked direction, always. So even when my click is only few pixels away, it will still apply a strong force. But, tbh, I like it this way, I'm satisfied with the result.

Now, I also get your clamp function. It's like the hump's vector:normalized(), but instead of changing the vector to always have a length of 1, it will decrease the vector to the given length d. d2 is my current length (result of Pythagoras) and then you use a simple rule of three to determine the new vector coordinates. This function will be helpful for me in future, I know.

Thank you for this, much love. :awesome: