Shifting an item to a specific location using three.js

I'm attempting to fire bullets or projectiles utilizing 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 observe, spheres are discharged from the cube onto the plane when the mouse is clicked.

I've developed the getMouse3D() function to ascertain the position on the plane where the user clicked, and upon spawning a sphere, I save it in sphere.userData.target. However, I am uncertain how to adjust the sphere's position each frame so that it advances towards the target location (see the TODO note). In essence, by the time a sphere reaches the plane, it should intersect with sphere.userData.target (where the user clicked).

How can this be accomplished?

Answer №1

If you have the coordinates of a cube and its target final position, then it's simple to determine the direction by calculating it along with the speed:

sphere.position.addScaledVector(sphere.userData.direction, sphere.userData.speed * delta);
if (sphere.position.z <= plane.position.z) sphere.userData.speed = 0; // Stop when we reach the specified plane

I've modified your code using THREE.Raycaster() to easily obtain the target position without needing the getMouse3D() and getMouse2D() functions.

let renderer, camera, scene, light, plane, cube, spheres, raycaster = new THREE.Raycaster(), mouse = new THREE.Vector2(), intersects, clock = new THREE.Clock(), delta = 0;

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 => {
    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
    raycaster.setFromCamera(mouse, camera);
    intersects = raycaster.intersectObjects([plane]);
    if (intersects.length == 0) return;
    shoot(intersects[0].point);
  }, false);
}

function animate() {
  delta = clock.getDelta();
  spheres.forEach(sphere => {
    sphere.position.addScaledVector(sphere.userData.direction, sphere.userData.speed * delta);
    if (sphere.position.z <= plane.position.z) sphere.userData.speed = 0; // stop, when we reached the plane
  });

  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.5);
  sphere.position.copy(cube.position);
  sphere.userData.direction = target.sub(cube.position).normalize();
  sphere.userData.speed = 20;
  scene.add(sphere);
  spheres.push(sphere);
}
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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Hide the div element once the timer runs out

Struggling to make a timer disappear when it reaches 00:00, every attempt results in the div being hidden immediately. Check out the code snippet I've been working with: $(document).ready(function(e) { var $worked = $("#worked"); function upd ...

Guidelines for incorporating Context API in Material UI

Currently, I am encountering a TypeScript error while attempting to pass a property using the Context API within my components. Specifically, the error message reads: "Property 'value' does not exist on type 'String'" To provide conte ...

Can you please explain the concept of straightforward UI routes to me?

I recently discovered that ui-routes are more powerful and advantageous than ngRoutes. In an effort to learn more about the ui-routing feature, I started studying from http://www.ng-newsletter.com/posts/angular-ui-router.html. However, I found it challengi ...

Using Jquery to increase input values in multiples on keyup event

Is there a way to ensure that an input element only accepts numbers in increments of 50? While we know we can use the step="50" attribute, is it possible to achieve this using the keyup event instead? I came across this code that restricts users from inp ...

Various JSON Tag values occurring more than once

Upon receiving a JSON Object in HTML, I am passing it to a JavaScript function defined below. The issue arises when the same folderName appears repeatedly on the HTML page. JSON Object: { "folderName": "arjunram-test", "objects": [ { ...

Combining Jquery with Objects and Strings

Having a bit of trouble with a small issue that I can't seem to figure out. Let me explain... So, I'm creating an input like this... var input = $('<input/>'); When I append it like this, everything works fine: $(this).append( ...

What is the procedure for configuring custom request headers in Video.js?

Encountering this issue, I created a simple example using guidance from this documentation: <!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <link href="https://vjs.zencdn.net/7.4.1/video-js.css" re ...

The date picker feature of Jquery Mobile is not appearing on the popup field

I implemented the jtsage jquery mobile date picker, and I am facing an issue where the date picker appears behind the popup instead of in front of it when the text inside the popup is clicked. Here is a snippet of my code: <div data-role="content"> ...

The error "Unable to create an instance of mssql.Schema" indicates that the

Seeking assistance from experienced ReactJS developers to address the issue outlined below. The code provided is based on a tutorial I was following. Despite numerous attempts, I have been unable to resolve the issue. Here is the code snippet from User.js ...

Prevent Xdebug from processing multiple requests

One recurring issue I encounter in my app is calling an API endpoint every 60 seconds. However, when I attempt to debug using Xdebug, stepping through the controller associated with that endpoint often takes longer than a minute. This results in another re ...

Tips for breaking apart elements of a JavaScript array when a specific delimiter is present in an object property

I am facing a challenge with an array of objects where some properties contain commas. My goal is to split these properties into new objects and recursively copy the rest of the properties into new array elements. For example, consider this original array ...

`initMap` does not exist as a function

Hey everyone, I need some help: I recently integrated the Google Maps API into my AngularJs project and encountered an error in the console: Uncaught message: "initMap is not a function" name: "InvalidValueError" This occurs when the Google Map API is ...

Firebase web authentication is most effective upon the second attempt

I am currently working on a website that interacts with Google's firebase to read and write data. The website has both anonymous and email authentication enabled. Users can view the data anonymously, but in order to edit or write new data, they must s ...

Display the changing value at the beginning of the drop-down menu

I'm currently working on an AngularJS forEach loop where I need to display a dropdown list with dynamic values. The goal is to have the value stored in $scope.showCurrentProgram as the first selected element in the dropdown list when the page loads, a ...

Converting SVG to PNG image directly in the browser (compatible with all browsers, including Internet Explorer)

I am currently working with Highcharts and I have a need to convert all the charts displayed on the webpage into images so that I can send them to my server for merging with a master export Excel file. The Excel file already contains some tables and pivot ...

What is the best method for retrieving the selected itemsPerPage in Vuetify's data-table in version 2.0

Recently, I've been struggling to retrieve the selected items-per-page on a Vuetify data-table due to some recent changes. I came across this helpful example: How to set initial 'rows per page' value in Vuetify DataTable component? Here is th ...

The React.js .map function encountered an error while trying to map the results from Firebase

As a newcomer to the realm of React and Firebase, I find myself struggling with arrays and objects. It seems like the way my data is formatted or typed does not play well with the .map method. Despite scouring Stack Overflow for answers, none of the soluti ...

Using Javascript to dynamically add and remove elements on click

Whenever I click on a link, I am able to generate input, label, and text. However, I want to be able to delete these elements with the next click event on the same link: I've tried updating my code like this: var addAnswer = (function() { var la ...

What is the best way to upload an image through PHP using $_POST after using JavaScript to resize it?

I am currently working on developing a webpage that allows users to upload images from their iPhone, Android, and desktop devices. The goal is to save these pictures as thumbnails in the ./userupload directory and store the link to them in a MySQL database ...

What is the best way to position my logo on top of the stunning space background?

Click here to check out the code on CodePen Please take a look at my code on codepen.io. I am new to Stack Overflow so please be patient with me if I make any mistakes. ;-; I currently have my logo (https://i.stack.imgur.com/W0nWc.png) included in the co ...