I have a rounded ground and there is a ball on it. trying to move the ball inside the ground with using smartphone orientation. no problem with that part. the ball is moving and make the ball stop at the edge by using three.js intersectsSphere
. but couldn’t manage the move the ball again if the orientation points opposite direction. here is the working sample.
https://codesandbox.io/s/jovial-mirzakhani-f50p2l?file=/src/App.vue
sample code below;
export const LIGHT = () => { const light = new THREE.SpotLight(0xffffff, 1); light.position.set(100, 1, 0); light.castShadow = true; light.position.set(0, 0, 35); return light; }; export const BALL = () => { const geometry = new THREE.SphereGeometry(3, 10, 10); const material = new THREE.MeshLambertMaterial({ color: "#f1c40f" }); const mesh = new THREE.Mesh(geometry, material); mesh.castShadow = true; mesh.receiveShadow = false; return mesh; }; export const BALL_BOUNDING = (sphere) => { return new THREE.Sphere(sphere.position, 3); }; export const GROUND = () => { const geometry = new THREE.CircleGeometry(21, 21, 0); const material = new THREE.MeshPhongMaterial({ color: "#bdc3c7" // transparent: true, // opacity: 0, }); const mesh = new THREE.Mesh(geometry, material); mesh.castShadow = true; mesh.position.z = 1; return mesh; }; export const GROUND_BOUNDING = (ground) => { const geometry = new THREE.SphereGeometry(13, 8, 8); const material = new THREE.MeshLambertMaterial({ color: "#FFF" }); const mesh = new THREE.Mesh(geometry, material); // expanding bounding XYZ return new THREE.Sphere(mesh.position, 18); }; const ww = window.innerWidth; const wh = window.innerHeight; const renderer = new THREE.WebGLRenderer({ canvas: canvas.value, alpha: true }); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(35, ww / wh, 0.1, 1000); const light = _light() const ball = BALL() const ball_bounding = BALL_BOUNDING(ball) const ground = GROUND() const ground_bounding = GROUND_BOUNDING(ground) renderer.shadowMap.enabled = true; renderer.setSize(window.innerWidth, window.innerHeight); camera.position.z = 120; scene.add(ball) scene.add(ground) scene.add(light) function animate() { requestAnimationFrame(animate) render() } function render() { const x = posX.value / 20 const y = posY.value / 20 if (ball.geometry.boundingSphere !== null) { ball.geometry.computeBoundingSphere() ball_bounding.copy(ball.geometry.boundingSphere).applyMatrix4(ball.matrixWorld) } if (ground_bounding.intersectsSphere(ball_bounding)) { ball.position.x += x; ball.position.y -= y; } renderer.render(scene, camera) } animate()
Advertisement
Answer
Think about the very moment when the ball goes out of intersection, which is the moment when ground_bounding.intersectsSphere(ball_bounding)
returns true
for the last time, what happens exactly? Let’s take some time to ponder about it:
ground_bounding.intersectsSphere(ball_bounding)
returnstrue
- so you feel comfortable to move the ball
ball.position.x += x
- next round
animate()
,ground_bounding.intersectsSphere(ball_bounding)
returnsfalse
- at this point, the ball is already moved (in the previous round) to a position where
ground_bounding.intersectsSphere(ball_bounding)
always returnsfalse
, we’re stuck in the state forever.
Mistake is made at step 2, the ball SHOULDN’T be moved even though step 1 says true
. Because if moved, next round it will definitely say false
, and got stuck there forever.
Solution is to make a dummy clone of ball_bounding
, then move the dummy first, and do intersection test against the dummy. If dummy is still within intersection after moved, that means it’s also safe to move the ball.
const dummy = ball_bounding.clone(); dummy.center.x += x; dummy.center.y -= y; if (ground_bounding.intersectsSphere(dummy)) { ball.position.x += x; ball.position.y -= y; }