I'm trying to "shoot" bullets/projectiles using three.js:
let renderer, camera, scene, light, plane, cube, spheres;
initialize();
animate();
function initialize() {
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight);
scene = new THREE.Scene();
light = new THREE.SpotLight("#ffffff");
light.position.y = 700;
scene.add(light);
plane = new THREE.Mesh();
plane.material = new THREE.MeshToonMaterial({ color: "#0000ff" });
plane.geometry = new THREE.PlaneGeometry(60, 30);
plane.position.z = -50;
scene.add(plane);
cube = new THREE.Mesh();
cube.material = new THREE.MeshToonMaterial({ color: "#ff0000", transparent: true, opacity: 0.85 });
cube.geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
cube.position.y = -1;
cube.position.z = -3;
scene.add(cube);
spheres = [];
window.addEventListener("click", event => {
let mouse2d = getMouse2D(event.clientX, event.clientY);
let mouse3d = getMouse3D(mouse2d, plane.position.z);
shoot(mouse3d);
}, false);
}
function animate() {
spheres.forEach(sphere => {
// TODO: Update sphere position based on sphere.userData.target
sphere.position.z -= 1;
});
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
function shoot(target) {
let sphere = new THREE.Mesh();
sphere.material = new THREE.MeshToonMaterial({ color: "#00ff00" });
sphere.geometry = new THREE.SphereGeometry(0.25);
sphere.position.x = cube.position.x;
sphere.position.y = cube.position.y;
sphere.position.z = cube.position.z;
sphere.userData.target = target;
scene.add(sphere);
spheres.push(sphere);
}
function getMouse3D(mouse2d, z) {
let vector = new THREE.Vector3(mouse2d.x, mouse2d.y);
vector.unproject(camera);
let dir = vector.sub(camera.position).normalize();
let distance = (z - camera.position.z) / dir.z;
return camera.position.clone().add(dir.multiplyScalar(distance));
}
function getMouse2D(x, y) {
return new THREE.Vector2(
(x / renderer.domElement.clientWidth) * 2 - 1,
-(y / renderer.domElement.clientHeight) * 2 + 1
);
}
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: 0;
background: #eeeeee;
}
canvas {
display: block;
cursor: crosshair;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.js">
</script>
As you can see, spheres are shot from the cube to the plane on mouse click.
I've written the getMouse3D()
function to determine the position on the plane that the user clicked on, and when a sphere is spawned, I store it in sphere.userData.target
. But I don't know how to update the position of the sphere each frame so that it moves towards it (see the TODO
comment). In other words, by the time a sphere reaches the plane, it should intersect it at sphere.userData.target
(where the user clicked).
How do I do this?