Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A-frame move forward in camera direction

I'm trying to make a component in A-frame that will move the player/camera in the direction the camera is facing. It should not move anywhere on the y plane, only in the x/y plane. The current setup in the dom is:

<a-entity>
    <a-camera></a-camera>
</a-entity>

I want to move the entity element position to x units in the current camera direction but it should not move anything on the y-plane. I have tried various solutions but they seem to introduce some weird shift in the camera.

like image 927
Kristofer Avatar asked Dec 13 '22 18:12

Kristofer


1 Answers

You could handle this in multiple ways.

0) Vectors

Simple and available in most 3D engines:

  • Grab the camera's forward vector / world direction
  • multiply it by the distance you want to move
  • add it to your position vector

The above steps within an a-frame component could look like this:

<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
  // define a custom component
  AFRAME.registerComponent("foo", {
    init: function() {
      // grab the camera
      var player = document.querySelector("a-camera")
      // create a direction vector
      var direction = new THREE.Vector3();

      window.addEventListener("keydown", (e) => {
        if (e.code === "KeyR") {
          // get the cameras world direction
          this.el.sceneEl.camera.getWorldDirection(direction);
          // multiply the direction by a "speed" factor
          direction.multiplyScalar(0.1)
          // get the current position
          var pos = player.getAttribute("position")
          // add the direction vector
          pos.add(direction)
          // set the new position
          player.setAttribute("position", pos);
          // !!! NOTE - it would be more efficient to do the
          // position change on the players THREE.Object:
          // `player.object3D.position.add(direction)`
          // but it would break "getAttribute("position")
        }
      })
    }
  })
</script>
<a-scene>
  <a-box position="-1 0.5 -3" rotation="0 45 0" foo color="#4CC3D9"></a-box>
  <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
  <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
  <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
  <a-sky color="#ECECEC"></a-sky>
  <a-camera></a-camera>
</a-scene>

However you may want to do this a bit more "math-ish" way:

1) 2D - Polar Coordinates

Mapping angles to 2D space is a job for the polar coordinate system !

We want to calculate x/y coordinates based on the camera rotation. The coordinate conversion looks like this:

x = r * cos(a)
y = r * sin(a)

where "r" is the step, the "a" is the angle.


Theory is boring, so lets get to practice:
var angle = player.getAttribute("rotation") // grab angle
var x = 1 * Math.cos(angle.y * Math.PI / 180) // calculate shifts
var y = 1 * Math.sin(angle.y * Math.PI / 180)
var pos = player.getAttribute("position") // grab position
pos.x -= y; // update position
pos.z -= x;
player.setAttribute("position", pos);

It's quite simple - get the angle, calculate the shift, and set the new position.

We can modify the earlier example:

<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
  AFRAME.registerComponent("foo", {
    init: function() {
      var player = document.querySelector("a-camera")
      window.addEventListener("keydown", (e) => {
        if (e.code === "KeyR") {
          var angle = player.getAttribute("rotation")
          var x = 0.1 * Math.cos(angle.y * Math.PI / 180)
          var y = 0.1 * Math.sin(angle.y * Math.PI / 180)
          var pos = player.getAttribute("position")
          pos.x -= y;
          pos.z -= x;
          player.setAttribute("position", pos);
        }
      })
    }
  })
</script>
<a-scene>
  <a-box position="-1 0.5 -3" rotation="0 45 0" foo color="#4CC3D9"></a-box>
  <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
  <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
  <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
  <a-sky color="#ECECEC"></a-sky>
  <a-camera></a-camera>
</a-scene>

2) 3D - Spherical coordinates

After all, this is a 3D space we're talking about.
The concept is the same - converting the camera angles into the x/y/z coordinates. The nifty trick here are the conversions from the spherical coordinate system.Three dimensions make it a bit more difficult, but the most confusing thing - the axis are different in the spherical system, than in the clipspace(used by a-frame):

enter image description here

Keeping that in mind the calculations should look like in this snippet:

<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
  // same component as 2D - just different calculations
  AFRAME.registerComponent("foo", {
    init: function() {
      var player = document.querySelector("a-camera")
      window.addEventListener("keydown", (e) => {
        if (e.code === "KeyR") {
          // get the player rotation
          var angle = player.getAttribute("rotation")
          // calculate the angles
          // the camera's theta == 0 is actually 90' in the clipspace
          let theta = (angle.x * Math.PI / 180) + Math.PI / 2 
          let fi = angle.y * Math.PI / 180
          let r = 0.1
          // calculate the position shifts
          let z = Math.sin(theta) * Math.cos(fi) * r
          let x = Math.sin(theta) * Math.sin(fi) * r
          let y = Math.cos(theta) * r

          // update the position
          var pos = player.getAttribute("position")
          pos.x -= x;
          pos.y -= y;
          pos.z -= z;
          player.setAttribute("position", pos);
        }
      })
    }
  })
</script>
<a-scene>
  <a-box position="-1 0.5 -3" rotation="0 45 0" foo color="#4CC3D9"></a-box>
  <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
  <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
  <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
  <a-sky color="#ECECEC"></a-sky>
  <a-camera></a-camera>
</a-scene>
like image 76
Piotr Adam Milewski Avatar answered Dec 17 '22 23:12

Piotr Adam Milewski