- Smooth Locomotion - The translation of a player based off a movement vector from a controller that is rotated by the current rotation of the head.
- Snap Turning - The instantaneous (or near-instantaneous, depending on implementation) rotation of a player by a specified number of degrees.
- Smooth Turning - The rotation of a player by a constant amount based on the intensity of X-axis input from a controller.
I'm currently working on creating a physical VR character controller for use in WebXR experiences, starting with A-Frame. The aim right now is simply to make it so that the player is unable to smooth locomote through a static physics body. I'm currently using aframe-physics-system, which implements ammo.js, the JavaScript port of Bullet via Emscripten, but I believe the concepts should still be general enough that this shouldn't be an issue. By default, aframe-physics-system creates a btDiscreteDynamicsWorld with iterations set to 10, maxSubSteps set to 4, and a fixedTimeStep of .01667. Here is the documentation on the Ammo driver for further information.
The Issue
Up to now, I've been unable to find a good solution for handling collision. My initial thought was to simply have a dynamic body for the player and impart forces on it to move and rotate. While this did give me the collision I was looking for, it introduced problems with my snap turning and smooth turning implementations. Before introducing physics, I was manually setting the position and rotation of the player, but it seems this gets overridden when trying to do it on a dynamic body, even if I do it through the setOrigin and setRotation functions on the world transform.
My second thought was to make the body kinematic, so I can use the code I've already written for the non-physics character controller to move and rotate, but now I have to handle collisions manually. Right now, the closest thing I have to a solution is to check how many bodies the player is currently colliding with, and reversing their input by 3 times as much until they're no longer colliding. This technically works, but obviously introduces insane amounts of jittering as the player goes in and out of an object, so it's far from ideal. I thought instead I could maybe do a raycast from the player position towards their direction of movement, and halt any movement where the difference between the raycast hit and the player position is below a certain point. This works very inconsistently, and the player can often still move right through the static body. I believe this is a consequence for the difference between the tickrate of the program and the actual physics system, but I'm at a loss for how to potentially solve it.
Raycast Collision Check Code:
Code: Select all
//This code makes use of three.js for handling player entity position in the A-Frame scene.
//direction is an array with the desired movement vector
//this.world is a reference to the btDiscreteDynamicsWorld
let pos = this.player.object3D.position;
let bRayFromWorld = new Ammo.btVector3(pos.x, pos.y + 0.5, pos.z); //y is raised to be roughly center mass, as cube I'm colliding with is floating
let bRayToWorld = new Ammo.btVector3(pos.x + (direction[0] * 1.25), pos.y + (direction[1] * 1.25), pos.z + (direction[2] * 1.25));
let bRayResultCallback = new Ammo.ClosestRayResultCallback(bRayFromWorld, bRayToWorld);
this.world.rayTest(bRayFromWorld, bRayToWorld, bRayResultCallback);
let hitPos = new THREE.Vector3(bRayResultCallback.get_m_hitPointWorld().x(), bRayResultCallback.get_m_hitPointWorld().y(), bRayResultCallback.get_m_hitPointWorld().z());
this.distance = pos.distanceTo(hitPos);
That's about where I'm at currently. If anyone has any suggestions, or needs further examples of code, let me know.