Player moving on a sphere surface

Post Reply
Eturcim
Posts: 2
Joined: Sat Jan 23, 2021 4:12 am

Player moving on a sphere surface

Post by Eturcim »

Hello there,

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));
  }
  
And to move the character on the sphere (I removed the code dealing with the mouse controlled orientation since it is irrelevant to track down the problem). This code is also called on every game loop iteration:

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));
In plain English here is what I am doing:

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) Align the player Up with the gravity direction, to do so I:
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);
4) c. set the new transform origin with this new vector

The project may be seen here https://ai-gallery.vercel.app/ with the spinning issue I am describing.
Eturcim
Posts: 2
Joined: Sat Jan 23, 2021 4:12 am

Re: Player moving on a sphere surface

Post by Eturcim »

Answering my own question:

The character's rigid body was missing angular damping (which was causing the spinning to occur):
Calling setDamping on the character's rigidbody with value greater than 0 for angularDamping solves the issue.
Post Reply