Physics - Limit velocity

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
Ulydev
Party member
Posts: 445
Joined: Mon Nov 10, 2014 10:46 pm
Location: Paris
Contact:

Physics - Limit velocity

Post 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. ;)
User avatar
FullyLucid
Prole
Posts: 5
Joined: Fri Dec 04, 2015 6:36 pm
Location: Salt Lake City, Utah
Contact:

Re: Physics - Limit velocity

Post 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.
One rogue, one life.
User avatar
Ulydev
Party member
Posts: 445
Joined: Mon Nov 10, 2014 10:46 pm
Location: Paris
Contact:

Re: Physics - Limit velocity

Post 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?
User avatar
ArchAngel075
Party member
Posts: 319
Joined: Mon Jun 24, 2013 5:16 am

Re: Physics - Limit velocity

Post 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?
User avatar
Marty
Citizen
Posts: 89
Joined: Mon Dec 04, 2017 1:47 am
Location: Germany

Re: Physics - Limit velocity

Post 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?
Visual Studio Code TemplateRichLÖVE Mobile (AdMob+UnityAds+PlayGamesServices+GameCenter)Add me on Discord

───▄▀▀▀▄▄▄▄▄▄▄▀▀▀▄───
───█▒▒░░░░░░░░░▒▒█───
────█░░░░░░░░░█────
▄▄──█░░░▀█▀░░░█──▄▄
█░░█▀▄░░░░░░░▄▀█░░█
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Physics - Limit velocity

Post 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.
User avatar
Marty
Citizen
Posts: 89
Joined: Mon Dec 04, 2017 1:47 am
Location: Germany

Re: Physics - Limit velocity

Post 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:
Visual Studio Code TemplateRichLÖVE Mobile (AdMob+UnityAds+PlayGamesServices+GameCenter)Add me on Discord

───▄▀▀▀▄▄▄▄▄▄▄▀▀▀▄───
───█▒▒░░░░░░░░░▒▒█───
────█░░░░░░░░░█────
▄▄──█░░░▀█▀░░░█──▄▄
█░░█▀▄░░░░░░░▄▀█░░█
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Physics - Limit velocity

Post 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.
User avatar
Marty
Citizen
Posts: 89
Joined: Mon Dec 04, 2017 1:47 am
Location: Germany

Re: Physics - Limit velocity

Post 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:
Visual Studio Code TemplateRichLÖVE Mobile (AdMob+UnityAds+PlayGamesServices+GameCenter)Add me on Discord

───▄▀▀▀▄▄▄▄▄▄▄▀▀▀▄───
───█▒▒░░░░░░░░░▒▒█───
────█░░░░░░░░░█────
▄▄──█░░░▀█▀░░░█──▄▄
█░░█▀▄░░░░░░░▄▀█░░█
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 52 guests