Entity.getPosition is sometimes wrong after teleport
When reading the entity's position after a teleport, the position is sometimes the new and correct one, and sometimes the old one from before the teleport.
Project to reproduce: https://playcanvas.com/editor/scene/2276474
How do I reproduce the bug? I refreshed multiple times, but don't see any issue in the console.
Well, all I can say is to refresh more, I tried to debug it for a couple of hours already, and have absolutely no idea why this is happening
The source of the bad position data is coming from the _updateDynamic method of RigidBodyComponent, this part in particular:
const motionState = body.getMotionState();
if (motionState) {
const entity = this.entity;
motionState.getWorldTransform(_ammoTransform);
const p = _ammoTransform.getOrigin();
const q = _ammoTransform.getRotation();
The question is why is the old position stored in the motion state
~~I see it. This is probably due to #7769.~~ The issue I mentioned is not valid, so there must be something else in play here.
I've looked into it a bit. The issue is that when you teleport a body, its motion state is not updated, unless it is a kinematic body.
https://github.com/playcanvas/engine/blob/7b4f9c0c08a3a22e9c9197a300ab75ca613ed24f/src/framework/components/rigid-body/component.js#L1059-L1064
You need to wait for the physics to step, which happens after your script update. If the new position is needed right after teleport, you can either use the new position directly (since you are teleporting to it, instead of reading entity position), or you can read it in postUpdate method, after the physics have stepped.
Thanks for the investigation @LeXXik , that is great. Is there anything that can be done to improve this, like copying the teleporting position to motion state? Or is it better to leave it as is.
In my framework with Jolt, I do update the motion state of a body after a teleport, unless it is a static one. Works great. We could change the guard here to avoid a static body, instead of only accepting kinematic one. Its a safe operation.
Edit: We'd need to check when the entity transform would get that new position from the motion state, though.
You need to wait for the physics to step, which happens after your script update. If the new position is needed right after teleport, you can either use the new position directly (since you are teleporting to it, instead of reading entity position), or you can read it in
postUpdatemethod, after the physics have stepped.
In such a situation, fixedUpdate would help, which makes the physics behavior predictable in the update method.
... fixedUpdate() <- here make teleport physicsFixedUpdate() ... update() <- here get position after teleport