What’s the best way to smoothly counter the inertia of an object being pushed by applyForce? I have been using a system that applies a counter force in the opposite axis but it results in the object vibrating or jittering as the force is applied (since the object is travelling at high speed and the counter needs to be big to be effective).
I am using force as it is better for what I need; I have a flying vehicle object that travels at high speed and force provides a great sense of inertia for this, its just trying to balance these forces which is tricky (and I haven’t had any collision issues).
I have played with linearVelocity- this solves the oscillation but it has no inertia for the ship which I would have to add via script.
Alright, sounds like force may be the way to go for you. You could try applying some kind of friction force over several frames to slow the vehicle to a stop rather than trying to stop it in one frame.
Recently I was trying something similar (negating motion with and opposite force) and it wasn’t going so well. However in my situation it was for controlling a character, so I didn’t need accurate physics. I ended up using the character physics type to handle collision, and then scripting all the movement behavior myself. You could try something like this but it would probably be a lot of work. The character collision type is available in recent SVN builds of Blender.
This is a simple solution, just set the linear velocity to 0. This will stop all linear movement on the object.
def stop(cont):
obj = cont.owner
force = -obj.localLinearVelocity
force *= obj.mass
force *= bge.logic.getAverageFrameRate()
obj.applyForce(force, True)
This second method actually calculates the needed force to stop the object. Again it will stop all linear movement. It can also be adjusted to allow for a friction be only applying a fraction of the force per frame (or all of it if it is small enough).
Both of these methods aren’t friendly to gravity. To deal with that you will need to ignore any z axis velocity. For the first method this means getting the velocity, clearing out the x and y components, and then setting the linear velocity. For the second method that means setting the z component to 0.
Thanks for those examples, they are very informative!
This is my slightly changed version:
from bge import logic
cont = logic.getCurrentController()
obj = cont.owner
sce = logic.getCurrentScene()
pointVel = obj.getLinearVelocity(True)
def A():
if pointVel[0] > 20:
force = -pointVel[0]
force *= obj.mass
force *= logic.getAverageFrameRate()
#print(force.magnitude)
obj.applyForce(force, True)
A couple of slightly beginner questions:
1: You have three force statements one after the other; does this mean in code terms each step leads on from the last and are not overwritten? (So, each step in your second example the force value is set, multiplied by the objects mass and then multiplied by the average framerate value?)
2: You mention for your second example setting the Z component to 0 (or any axis). Now, in my example above, I have set this by only applying the calculation to the x component (-pointVel[0]). Is this right? If not, where do I do this?
But again, thanks! I was aware of how to calculate the stopping force of an object but a bit clueless as to apply it in Python. And your first example will prove useful too.
If you manipulate one axis of a vector, it means the vector gets distorted. Finally it points at a different direction.
This might be what you want to achieve, maybe not. In some cases you get side effects you might not want or not expect.
Example: walking on a grid. If you go one step vertical or horizontal you always have the same speed. If you do a step diagonal, your speed is much faster as you do a step horizontal and vertical at the same time. This means a character walking on the grid needs to walk faster on diagonals (animation issue).
A counter force is a vector that is exactly the opposite direction of the original force. You can also think of it as a negative vector (force * -1). If the vector does not match the direction of the original you can quickly add forces you do not want to get (see diagonal grid movement).
Btw. you can cancel gravity by constantly applying a counter force to gravity (0,0,9.8). The same rules belong to this counter force.
Those three lines with regards to force could be written a couple of different ways.
All on one line:
force = -pointVel[0] * obj.mass * logic.getAverageFrameRate()
Written out without the shortcut assignment operators:
force = -pointVel[0]
force = force * obj.mass
force = force * logic.getAverageFrameRate()
As you can see from these other examples the answer to your first question is force is modified at each step, and then overwritten with the new value.
For the most part your code should work for one axis, with one slight problem. applyForce() requires a vector, but your force value is a scalar (single number). Are you not getting an error from that? To get the force you want as a vector you can do something like this:
force = mathutils.Vector((force, 0, 0))
If you do that, don’t forget to import mathutils first. In case you aren’t aware of this, Python does not care that I just changed data types. Force changed from a single float value to a mathutils.Vector. If you find this confusing you can assign the vector to a differently named variable, such as force_vec.
Kupoman > cheers for that too! For some reason my .blend is not throwing up any errors (which is odd). I shoe horned your original code into my current project and it works- unfortunately I still get judders, which leads me to think its an issue with how many times the counter force is triggered rather than the force itself. How I sort that is up for experimentation, I may have to eventually go to linear forces. But its all a learning curve;)
But thanks for going through that, I learnt quite a lot!