jacobi1
jacobi1

Reputation: 23

How to move a glTF model in the direction it is facing in A-frame?

I have a gltf model of my robot loaded in A-frame scene version 1.0.4. So far I can turn the robot about its x,y, z axes, but when I try to move it forward it still moves in its initial direction rather than the direction it's facing. I want to be able to control its movements with my keyboard without using any external libraries. I believe I have to use Quaternions, but I haven't figured out how to use them yet. Here is my A-frame code so far.

<a-scene id="myScene">
  <a-entity environment="preset:forest;"></a-entity>

 <a-entity gltf-model="#humanoid" id="robot" position="0 20 0" rotation="0 0          0" 
   scale="0.0001 0.0001 0.0001" static-body>
 </a-entity>
</a-scene>

And here is my script

const robot = document.getElementById('robot');

const update = () => {
   if (keys.forward) {
       let {x,y,z} = robot.object3D.position;
       let ry = robot.object3D.rotation.y;
       z += Math.cos(ry * Math.PI/180)/12;
       x += Math.sin(ry * Math.PI/180)/12;
       robot.object3D.position.set(x, y, z); 
}
   else if (keys.backwards) {
       let {x, y, z} = robot.object3D.position;
       let ry = robot.object3D.rotation.y;
       z -= Math.cos(ry * Math.PI/180)/10;
       x -= Math.sin(ry * Math.PI/180)/10;
       robot.object3D.position.set(x, y, z);   
}
 if (keys.turnLeft) {
       let {x, y, z} = robot.getAttribute('rotation');
       y += 0.25;
       robot.setAttribute('rotation',{x,y,z});
}
    else if (keys.turnRight) {
        let {x, y, z} = robot.getAttribute('rotation');
        y -= 0.25;
        robot.setAttribute('rotation',{x,y,z});
}
}

Upvotes: 0

Views: 1158

Answers (3)

user1039663
user1039663

Reputation: 1335

You can use Quaternions and many other ways, but Object3D has some usesful and more simple alternatives to move object in local coordinates:

I think the most simple one is to use https://threejs.org/docs/#api/en/core/Object3D.translateZ

robot.object3D.translateZ(amountToMoveForward);

Also https://threejs.org/docs/#api/en/core/Object3D.localToWorld can be used to transform vectors.

Upvotes: 1

Piotr Adam Milewski
Piotr Adam Milewski

Reputation: 14655

let ry = robot.object3D.rotation.y;
z += Math.cos(ry * Math.PI/180)/12;

Although a-frame uses degrees for rotation, threejs uses radians, so there is no need to convert them again:

const robot = document.querySelector("a-box")
document.body.addEventListener("keydown", (evt) => {
   if (evt.key === 's') {
       let {x,y,z} = robot.object3D.position;
       let ry = robot.object3D.rotation.y;
       z += Math.cos(ry)/12;
       x += Math.sin(ry)/12;
       robot.object3D.position.set(x, y, z); 
    } else if (evt.key === 'w') {
       let {x, y, z} = robot.object3D.position;
       let ry = robot.object3D.rotation.y;
       console.log(ry)
       z -= Math.cos(ry)/10;
       x -= Math.sin(ry)/10;
       robot.object3D.position.set(x, y, z);   
     }
     if (evt.key === 'a') {
       let {x, y, z} = robot.getAttribute('rotation');
       y += 1;
       robot.setAttribute('rotation',{x,y,z});
     } else if (evt.key === 'd') {
       let {x, y, z} = robot.getAttribute('rotation');
       y -= 1;
       robot.setAttribute('rotation',{x: x,y: y,z: z});
     }
})
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<a-scene>
  <a-box position="-1 0.5 -3" rotation="0 0 0" color="#4CC3D9">
    <a-sphere radius="0.5" position="0 0 -1"></a-sphere>
  </a-box>
  <a-entity camera position="0 1.6 0"></a-entity>
</a-scene>


Same with getWorldDirection:

const robot = document.querySelector("a-box")
const direction = new THREE.Vector3();
document.body.addEventListener("keydown", (evt) => {
   if (evt.key === 's') {
     // get robot direction
     robot.object3D.getWorldDirection(direction);
     // add a "speed" value
     direction.multiplyScalar(0.1)
     // add the new vector to the actual position
     robot.object3D.position.add(direction)
    } else if (evt.key === 'w') {
     robot.object3D.getWorldDirection(direction);
     direction.multiplyScalar(-0.1)
     robot.object3D.position.add(direction) 
    }
    if (evt.key === 'a') {
     let {x, y, z} = robot.getAttribute('rotation');
     y=y+1
     robot.setAttribute('rotation',{x,y,z});
    } else if (evt.key === 'd') {
     let {x, y, z} = robot.getAttribute('rotation');
     y=y-1
     robot.setAttribute('rotation',{x: x,y: y,z: z});
    }
})
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<a-scene>
  <a-box position="-1 0.5 -3" rotation="0 0 0" color="#4CC3D9">
    <a-sphere radius="0.5" position="0 0 -1"></a-sphere>
  </a-box>
  <a-entity camera position="0 1.6 0"></a-entity>
</a-scene>

Upvotes: 1

Diarmid Mackenzie
Diarmid Mackenzie

Reputation: 826

Yes, using quatenions is a good way to solve this.

You can simply apply the robot's current quaternion to a vector3 representing the movement you want to make in the robt's original rotation, and apply that to the robot's position.

Something like this:

fowardVector = new Vector3(0, 0, -0.1)
rotatedForwardVector = new Vector3();

rotatedForwardVector.copy(forwardVector);
rotatedForwardVector.applyQuaternion(robot.object3D.quaternion);
robot.object3D.position.add(rotatedForwardVector);

Note that you'll want to only create the Vector3s once, and then re-use them to avoid unecessary garbage collection, rather than creating them for each movement.

E.g using a closure as explained here

Upvotes: 1

Related Questions