I am moving a rigidbody (the character) on the surface of a sphere, like what is described in this former post viewtopic.php?p=34525#p34525
I am facing the following issue that I am having hard time to solve:
After moving the character, it's starts spinning really slightly around it's Y axis.
As a side note, I am using Ammo.js which is a javascript/webassembly port of Bullet 2.82, but this has no impact on the problem.
To "stick" the player (a dynamic rigidbody with a cylinder shape) to the sphere (a static rigid body with a sphere shape), here is the code, called on each game loop iteration:
Code: Select all
_applyAttraction() {
let sphereOrigin = this.body.getWorldTransform().getOrigin();
let characterOrigin = this._characterEntity.body.getWorldTransform().getOrigin();
// compute and apply sphere gravity to character body
let sphereAttractionForce = new ammo.btVector3(
characterOrigin.x() - sphereOrigin.x(),
characterOrigin.y() - sphereOrigin.y(),
characterOrigin.z() - sphereOrigin.z()
);
sphereAttractionForce.normalize();
sphereAttractionForce.op_mul(gravity);
this._characterEntity.body.applyForce(sphereAttractionForce, ammo.btVector3(0, 0, 0));
// align character up with sphere origin
const gravityDirection = bt2ThreeVec3(sphereAttractionForce).normalize().multiplyScalar(-1);
// extract up axis of character transform basis
let characterUp = new THREE.Vector3();
this._characterEntity.graphic.matrixWorld.extractBasis(
new THREE.Vector3(),
characterUp,
new THREE.Vector3()
);
// apply rotation to align up with gravity vector
let verticalAlignmentRotation = new Quaternion()
.setFromUnitVectors(characterUp, gravityDirection)
.multiply(bt2ThreeQuat(this._characterEntity.body.getWorldTransform().getRotation()));
this._characterEntity.body.getWorldTransform().setRotation(three2BtQuat(verticalAlignmentRotation));
}
Code: Select all
let scalingFactor = 0.02;
let moveX = inputController.getMoveX();
let moveZ = inputController.getMoveZ();
if (moveX === 0 && moveZ === 0) return;
let movement = new THREE.Vector3(moveX, 0, moveZ).normalize();
movement.multiplyScalar(scalingFactor);
let wantedWorldPos = this.graphic.localToWorld(movement);
this.body
.getWorldTransform()
.setOrigin(new ammo.btVector3(wantedWorldPos.x, wantedWorldPos.y, wantedWorldPos.z));
1) Get the direction from the player to the sphere
2) Apply a force using that vector:
Code: Select all
this._characterEntity.body.applyForce(sphereAttractionForce, ammo.btVector3(0, 0, 0));
3) a. normalize and "flip" gravity vector direction
3) b. extract character up vector and compute the quaternion which give the rotation to align this up vector with the gravity vector
3) c. compute the new player orientation by applying quaternion from 3.b to it's current orientation
4) Apply movement (from keyboard inputs), in details:
4) a. get a vector resulting from the requested movement alongside x and z axis
4) b. convert this vector from player local basis to world basis
Code: Select all
this.graphic.localToWorld(movement);
The project may be seen here https://ai-gallery.vercel.app/ with the spinning issue I am describing.