Hello Three.js Community,
I’m encountering a peculiar issue in my 3D game environment where my character behaves unexpectedly when crossing over the South Pole of a spherical world. The game works perfectly at the North Pole and elsewhere, but near the South Pole, the character’s movement and gravity calculations seem to fail, and the character is locked in a sort of orbital movement around the south pole, and while I can widen or shrink the orbit, I cannot get out of it. My goal is to get character movement as close to how we move on earth as possible.
Additional libraries used: Ammo.js (not relevant to issue, when I removed it same things occurred.)
Problem Description:
The issue occurs specifically when the character approaches the South Pole.
The character’s gravity and velocity directions become locked in orbit around the South Pole, despite the up vector and right vector remaining as they should(see code).
This does not occur at the North Pole or other parts of the sphere.
Code Snippet:
javascript(no ammo.js, only 3js):
const sphereRadius = 50; // Half of the diameter
const characterSpeed = .1;
if (character) {
// Earth-like gravity settings
const sphereCenter = new THREE.Vector3(0, sphereCenterY, 0);
// Apply the rotation from mouse input (only if there's movement)
if (mouse.movementX !== 0) {
const mouseDeltaX = mouse.movementX;
const mouseSensitivity = 0.002;
const mouseRotationAngle = -mouseDeltaX * mouseSensitivity;
// Rotate around the global Y-axis
const yAxis = new THREE.Vector3(0, 1, 0);
const mouseRotation = new THREE.Quaternion().setFromAxisAngle(yAxis, mouseRotationAngle);
// Accumulate mouse rotation
accumulatedMouseRotation.multiply(mouseRotation);
}
// Reset mouse movement
mouse.movementX = 0;
// Calculate the up direction based on the sphere's surface
const characterToCenter = character.position.clone().sub(sphereCenter);
const distanceFromCenter = characterToCenter.length();
const upDirection = characterToCenter.normalize();
// Adjust character position to be on the sphere's surface
if (distanceFromCenter !== sphereRadius) {
const correctionFactor = sphereRadius / distanceFromCenter;
character.position.multiplyScalar(correctionFactor);
}
// Creating a quaternion to align the character's 'up' direction with upDirection
const alignWithUpDirection = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), upDirection);
// Combine alignment with 'up' direction with accumulated mouse rotation (if any)
const finalOrientation = alignWithUpDirection.clone().multiply(accumulatedMouseRotation);
character.quaternion.copy(finalOrientation);
// Forward direction - perpendicular to upDirection and character's quaternion
const forwardVector = new THREE.Vector3(0, 0, 1).applyQuaternion(character.quaternion).projectOnPlane(upDirection).normalize();
// Right direction - perpendicular to both upDirection and forwardVector
const rightVector = new THREE.Vector3(1, 0, 0).applyQuaternion(character.quaternion).projectOnPlane(upDirection).normalize();
character.forwardVector = forwardVector;
character.rightVector = rightVector;
character.upVector = upDirection;
// Calculate movement velocity based on character's orientation
let velocityDirection = new THREE.Vector3(0, 0, 0);
if (keys.w) {
velocityDirection.add(forwardVector);
}
if (keys.s) {
velocityDirection.sub(forwardVector);
}
if (keys.a) {
velocityDirection.add(rightVector);
}
if (keys.d) {
velocityDirection.sub(rightVector);
}
velocityDirection.projectOnPlane(upDirection);
// Normalize to get direction and multiply by speed to get velocity
velocityDirection.normalize().multiplyScalar(characterSpeed);
// Update the character's position
character.position.add(velocityDirection);
// Ensure the character remains on the sphere's surface after movement
const newCharacterToCenter = character.position.clone().sub(sphereCenter);
const newDistanceFromCenter = newCharacterToCenter.length();
if (newDistanceFromCenter !== sphereRadius) {
const newCorrectionFactor = sphereRadius / newDistanceFromCenter;
character.position.multiplyScalar(newCorrectionFactor);
}
Attempts to Resolve:
I’ve tried adjusting the sphere’s position to avoid alignment issues with the Y-axis.
I’ve double-checked quaternion operations and coordinate transformations.
I’ve Tried many different movement methods, but they all see to fail and get into a lock at the bottom of the sphere.
Questions:
Has anyone experienced similar issues with character movement near the poles in a spherical environment?
Could this be related to the right-handed coordinate system of Three.js, and if so, how can I adjust my calculations to accommodate this?
Any suggestions on how to debug or resolve this issue would be greatly appreciated, as I’ve been trying to figure this out for a week now and have seen no solution online for 3js.