Prevent physics drift


(Dr. Binary) #1

Hey, I’ve stumbled onto a weird problem here.

I am trying to create a “local physics” environment in order to handle moving platforms/vehicles. The end goal is to be able to have two or more space ships moving in a scene while players navigate aboard the vessels as if they each have their own “no inertia + artificial gravity” field. I seem to have almost solved the problem mathematically, but on implementation the player drifts ever so slowly across the surface. I believe the problem is related to when physics is being updated relative to when the script is run, but I could be wrong. The logic is as follows:

#use relative position to calculate the tangential velocity at the player's location
relPos = self.object.worldPosition - self.platform.worldPosition
tanVel = self.platform.localAngularVelocity.cross(relPos)
#apply tangential velocity, as well as any linear velocity
self.object.worldLinearVelocity = tanvel + self.platform.worldLinearVelocity
#account for rotation of the platform
self.object.angularVelocity = self.platform.localAngularVelocity

I’ve tried everything I can think of to accomplish this and preserve the physics of the player object. Any ideas guys?


(- Click for resources) #2

You want to navigate a moving object?


obj.linearvelocity = moving object velocity
obj.linearvelocity += walkspeed

obj.orientation = moving object orientation + offset (half the height of player)

Should that not be enough?


(AeroLynx) #3

This sounds InterestIng, but I can’t fully unterstand the issue just by your explanation. A minimal *.Blend would help as a start. Can you prepare one?


(Dr. Binary) #4

Not quite. Put an object running this code on a platform that can rotate on all three axis and you will find this is only accurate at the origin. The speed of an object far from the origin of a rotating object is different from the speed of another object close to the origin, hence the tangential velocity calculation.

@AeroLynx sure, I’ll put one up during my lunch break.


(AeroLynx) #6

Just a thought. This might be related to Coriolis forces/conservation of impulse, but I was never sure if Bullet considers these. Does the tangential drift appear when you move closer or further away from A rotating object?


(Dr. Binary) #7

I haven’t yet tried implementing player motion yet, but by placing the player object manually I’ve found distance does not seem to effect the drift rate.

I just double checked it and this occurs even if the player object origin is at the same location as the vehicle object origin.


(- Click for resources) #8

ok then i guess it’s just the velocity and position updating to late.

by grabbing worldposition or orientation etc, you grab it from the next frame, if you want to grab it directly from the current frame you need to use .copy(). so obj.worldposition = ship.worldPosition.copy() same for the others.

Try that and see if that fixes it.


(Dr. Binary) #9

Oh, that is not a bad idea at all. I didn’t know it did it that way.

I gave that a shot and it didn’t seem to make an observable difference. I still think it is related to updating to old data relative to what the ship is currently doing.


(- Click for resources) #10

so you did add it to all? like this:

#use relative position to calculate the tangential velocity at the player's location
relPos = self.object.worldPosition.copy() - self.platform.worldPosition.copy()
tanVel = self.platform.localAngularVelocity.copy().cross(relPos)
#apply tangential velocity, as well as any linear velocity
self.object.worldLinearVelocity = tanvel + self.platform.worldLinearVelocity.copy()
#account for rotation of the platform
self.object.angularVelocity = self.platform.localAngularVelocity.copy()

if so, then indeed a demo file would be handy


(Dr. Binary) #11

Yeah, that looks exactly like it. I’ll definitely get one up with a few examples on it.


(Dr. Binary) #12

Ok guys, here is a basic demonstration. Blue cubes do not call .copy, Green cubes do.

tan_drift.blend (608.1 KB)

Forgot to mention: this is on the latest UPBGE build.


(Daedalus_MDW) #13

damping affects linear velocity.

manually apply damping if not on ground.
owner.applyForce(-owner.worldLinearVelocity*0.2, False)


(AeroLynx) #14

Disclaimer: No solution yet.

Okay, I had a look at it and did some things to encircle the Problem and keep it as simple as possible. Used UPBGE 0.2.4., downloaded just minutes ago.

  1. Simplified the code (hopefully without adding bugs)
  2. Printed the angularvelocity in the console
  3. Got rid of the copies in the scene

Some observations can be made then:

  1. The platform was not turning around its origin - you had local velocity AND rotation applied. As I was not sure if this messes up something, I got rid of local velocity and shifted the Mesh off-center, so it turns similar just with an rotation applied
  2. Platform scale was not 1 on all axis. I learned that this should be 1 as it might mess up Bullet physics, so I re-scaled.
  3. What caught my eye was that the numbers of angular velocity change slightly - look at the terminal while it runs. These slight changes sum up when integrated and might be the cause of your observed drift.
  4. I also wondered, that I put 5° per tick/timestep in the Motion actuator, and the print showed 5.236. What if the physics engine assumes 60 FPS, but there is actual variation in the execution time, so that you end up with 58 FPS!?

I assume, that you have come across a funny time-discrepancy/numerical integration behavior which might be impossible to encounter. Can’t you just parent them together?

Here is the *.blend
tan_drift_v2.blend (521.7 KB)


(Dr. Binary) #15

@Daedalus_MDW
That’s a good point I’d forgotten about this. If I set all object damping to 0 for now wouldn’t it supposedly work correctly? I’m not applying any forces, only setting velocities.

@AeroLynx
Wow, that is super helpful. I had fixed a few of these in my actual project and missed them while I cranked out the example. Sorry. It’ll take me a bit to dissect and analyze everything here. I am worried that I’m running up against an engine constraint here.

I am trying to avoid parenting objects, because then I loose the physics engine for the children. I’m hoping to avoid having to calculate all collisions and movements manually. But it is a possibility as a last resort.


(AeroLynx) #16

Short follow up:

  1. I messed up the units, forget about point 4. above. 5 Degree per frame at 60 FPS sum up to 300 in one second, which is less than a full turn / 2 PI (roughly 6.28) and is the observed 5.blabla number.
  2. Changing Damping does not seem to change anything on my side, which makes sense, as there are actually no forces, but Velocities applied directly.

As I live in another time zone than you, I’ll call it a day :slight_smile:


(Dr. Binary) #17

I appreciate the effort, this still gives me a couple more places to poke at.


(Dr. Binary) #18

Eureka! With your help I believe I’ve got it. With both objects as Dynamic bodies, the following gives at least the basic necessary behavior:

    def start(self, args):
        scene = bge.logic.getCurrentScene()
        
        self.platform = scene.objects['platformOne']    
        self.box      = scene.objects['noCopyOne']
        
        self.platform.gravity = Vector((0,0,0))
        self.box.gravity = Vector((0,0,0))

        self.relPos = self.box.worldPosition - self.platform.worldPosition
        
        
    def update(self):
        self.platform.worldAngularVelocity = Vector((-0.0,0.0,5.0))
        self.box.angularVelocity = self.platform.localAngularVelocity
        vel = self.platform.getVelocity(self.relPos)
        self.box.setLinearVelocity(vel, True)

Here’s what I found:

  • Using the logic bricks gave us the inconsistent velocity, setting the velocity at the start of the update ensures we have a constant value for this test.
  • Setting both to dynamic bodies and disabling gravity ensures the Vectors are all calculated the same way. this was also needed for some of the function calls.
  • the relative position should only be updated when the player moves locally, not when the ship does. Setting the relPos variable each call meant that the ship had moved but the player had not yet, resulting in the slow drift. I’m fairly confident this is the main issue.
  • object.getVelocity([point]) is nifty, it calculates the tangential velocity for us so the code looks much neater.

Thanks again everyone who offered suggestions and asked questions. I deem this bug solved!

…Almost. Rats.


(BluePrintRandom) #19

I rolled my own physics before to do this it’s not that hard.

store local to the last object the player landed on and work from there

offset = lastObject.worldOrientation.inverted() * ( player.worldPosition - lastObject.worldPosition)

to get what to store to use next frame then

own.worldPosition = offset + (own.worldOrientation*own[‘localVelocity’])

to set where you are next frame.

(in this video I am using blender physics engine, however I know know I can totally roll my own)


(Dr. Binary) #20

Turns out this is way closer but there is a new bug, only obvious if you set the rotation very high and set a camera parented to watch the child object. Now it seems to oscilate position in an ellipse around a small area. From far away everything looks fine, but up close something weird is going on. -_-

@BluePrintRandom
I’ve seen that video before, and I would be curious to see the code itself. I can try to modify mine to use functions like you describe here to see if it will work.


(BluePrintRandom) #21

let me see if I can dig it up from my old pc, if not I will make it from scratch.