I am using the a-frame components for a WebVR application. The camera has look-controls with a cursor to let the user pick an object. And when the event is fired I wish to change the camera position. This is what I have done. A camera is defined in the scene.
<a-entity id="cam" camera="userHeight: 1.6" look-controls>
<a-entity cursor="fuse: true; fuseTimeout: 500" position="0 0 -1" geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03" material="color: black; shader: flat">
</a-entity>
</a-entity>
I have a box with a listener as follows
<a-box box-listener color="tomato" position="1 0 3" depth="1" height="1" width="1"></a-box>
When the user moves the cursor to the box and holds for half a second, the event gets fired. The event script is as follows:
AFRAME.registerComponent('box-listener', {
init: function() {
this.el.addEventListener('click', function(evt) {
var cam = document.getElementById("cam");
var campos = cam.getAttribute("position");
campos.x = 1; // some random change.
var camrot = cam.getAttribute("rotation");
camrot.x = 0;
camrot.y = 0;
camrot.z = 0; // setting it back to looking straight ahead
var look = cam.getAttribute("look-controls");
look.enabled = false;
cam.setAttribute('look-controls',look);
cam.setAttribute("position", campos);
cam.setAttribute("rotation", camrot);
look.enabled = true;
cam.setAttribute('look-controls',look);
});
}
});
By putting logs I find that the values are getting updated in the camera element however nothing changes on the screen.
where am I going wrong? Does the camera element need to be refreshed?
Thanks,
Raj
Related
I wanna replicate what this guy does.
Basically, you go back and forth from one corner of your room to another and rotate the scene when you reach the guardian fence.
https://repl.it/#tetegithub/aframe#index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://aframe.io/releases/1.1.0/aframe.min.js">
</script>
</head>
<body>
<a-scene>
<a-box id="box" position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
<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-entity id="rig" rotation="0 0 0">
<a-entity id="camera" camera look-controls></a-entity>
<!-- *** -->
<a-entity id="leftHand"
hand-tracking-controls="hand: left;modelColor:#E9967A;"
onpinchstarted="rig.object3D.rotation.x +=Math.Pi;"></a-entity>
<!-- *** -->
<a-entity id="rightHand" hand-tracking-controls="hand: right;modelColor:#E9967A"></a-entity>
</a-entity>
</a-scene>
</body>
</html>
I've added "onpinchstarted" event in the left hand's tag in the hope that when I pinch with my left hand the camera rig will rotate. And it doesn't.I think I have to somehow work with the event listeners and handlers but all the docs I read look like they are written for the robots. Any advice appreciated.
Does the rotation work if you trigger it outside of the event listener?
I see you're referring to "rig" in onpinchstarted, does "rig" exist as a variable in that scope?
One solution would be to start with a helper function that does the rotation, then run it in the console to confirm it works. Then, attach it to the scene via a javascript instead of html (doesn't have to be a component, but it might be easier to reuse).
The docs are unclear if onpinchstarted would work vs pinchstarted https://aframe.io/docs/1.1.0/components/hand-tracking-controls.html#events_pinchended
Well, I came up with a sort of a solution by dissecting the example project.
rig.js
AFRAME.registerComponent('rig', {
init: function () {
this.bindMethod();
this.el.sceneEl.addEventListener('pinchstarted', this.onPinchStarted);
},
//I still don't get what this thing does.
bindMethod: function () {
this.onPinchStarted = this.onPinchStarted.bind(this);
},
onPinchStarted: function () {
this.el.setAttribute('rotation', {y: this.el.getAttribute('rotation').y + 30});
},
});
index.html
<script src="rig.js"></script>
<a-entity rig>
<a-entity camera look-controls position="0 1 0"></a-entity>
<a-entity hand-tracking-controls="hand: left; modelColor:#E9967A;"></a-entity>
<a-entity hand-tracking-controls="hand: right; modelColor:#E9967A;"></a-entity>
</a-entity>
Now I want the rig to yaw the same amount the camera has rotated while I was holding the pinch with my left hand.
And I made it work after a while thanks to this person.
index.html
<script src="rotator.js"></script>
<a-entity id="rig">
<a-camera></a-camera>
<a-entity
oculus-touch-controls="hand: left;"
hand-tracking-controls="hand: left; modelColor:#E9967A;"
rotator="rig: #rig"
></a-entity>
<a-entity
oculus-touch-controls="hand: right;"
hand-tracking-controls="hand: right; modelColor:#E9967A;"
rotator="rig: #rig"
></a-entity>
</a-entity>
As web XR hand tracking in the Oculus Browser where I test it is still experimental and unstable I added touch controllers' grip buttons.
rotator.js
/* global AFRAME, THREE */
AFRAME.registerComponent("rotator", {
schema: {
rig: { type: "selector" },
},
init: function() {
this.bindMethods();
this.el.addEventListener("pinchstarted", this.onPinchStarted);
this.el.addEventListener("pinchended", this.onPinchEnded);
this.el.addEventListener("gripdown", this.onPinchStarted);
this.el.addEventListener("gripup", this.onPinchEnded);
this.rig = this.data.rig;
this.box = this.data.box;
this.box2 = this.data.box2;
this.camera = this.el.sceneEl.camera.el;
this.axisY = new THREE.Vector3(0, 1, 0);
},
bindMethods: function() {
this.onPinchStarted = this.onPinchStarted.bind(this);
this.onPinchEnded = this.onPinchEnded.bind(this);
},
onPinchStarted: function() {
this.trigger = 1;
this.oldCameraAngle = this.camera.getAttribute("rotation").y;
},
tick: function() {
if (this.trigger == 1) {
var angleDifference = THREE.Math.degToRad(
this.oldCameraAngle - this.camera.getAttribute("rotation").y );
this.oldCameraAngle = this.camera.getAttribute("rotation").y;
var cameraPosition = new THREE.Vector3();
cameraPosition.setFromMatrixPosition(this.camera.object3D.matrixWorld);
this.rig.object3D.position.add( cameraPosition.negate() );
this.rig.object3D.position.applyAxisAngle( this.axisY, angleDifference );
this.rig.object3D.position.add( cameraPosition.negate() );
this.rig.object3D.rotateOnAxis( this.axisY, angleDifference );
}
},
onPinchEnded: function() {
this.trigger = 0;
}
});
GitHub link and the version published on my website.
The A-Frame camera docs say to create a HUD like this:
<a-entity camera look-controls>
<a-entity geometry="primitive: plane; height: 0.2; width: 0.2" position="0 0 -1"
material="color: gray; opacity: 0.5"></a-entity>
</a-entity>
However, this causes the HUD element to be clipped by in-game objects. Normally HUDs are rendered on a separate layer above all the other game objects. Is there a simple way to fix this?
You have to:
Set renderer.sortObjects = true;
Set a the render order of the entity this.el.object3D.renderOrder = 100;
Disable the material depth test material.depthTest = false;
I created a simple overlay component that easily applies the above to any entity.
AFRAME.registerComponent("overlay", {
dependencies: ['material'],
init: function () {
this.el.sceneEl.renderer.sortObjects = true;
this.el.object3D.renderOrder = 100;
this.el.components.material.material.depthTest = false;
}
});
<a-entity camera look-controls wasd-controls position="0 1.6 0">
<a-plane id="hud" overlay rotation="0 0 0" position="0 0 -2" width="1" height="1" color="purple" shadow></a-plane>
</a-entity>
Example on glitch
I just started developing with a-frame, please excuse if the answer is obvious.
In my project I would like to get the position of a-sky where the user is looking at. Therefor I implemented a raycaster within the camera, which works fine so far.
HTML
<a-camera listener>
<a-entity raycaster="far: 1000" position="0 -0.9 0" rotation="0 0 0"></a-entity>
</a-camera>
<a-sky follow-intersection
id="sky"
src="#skybox-image">
</a-sky>
TS
AFRAME.registerComponent("follow-intersection", {
init: function() {
this.el.addEventListener("raycaster-intersected", evt => {
this.intersectingRaycaster = evt.detail.el.components.raycaster;
});
this.el.addEventListener("raycaster-intersected-cleared", () => {
this.intersectingRaycaster = null;
});
},
tick: function(t) {
if (!this.intersectingRaycaster) {
return;
}
const intersection = this.intersectingRaycaster.getIntersection(this.el);
if (intersection) {
let point = intersection.uv;
console.log(point.x, point.y);
}
So far this works fine, the problem is that after I set the cursor in the scene (which is needed for the project)
<a-scene
cursor="rayOrigin: listener"
>
I always get the intersections with the cursor, which are not wanted.
How can I only get the intersections of the camera? Thank you!
I don't think the cursor in a-scene is needed. You could just do:
<a-camera listener>
<a-entity cursor raycaster="far: 1000" position="0 -0.9 0" rotation="0 0 0"></a-entity>
</a-camera>
I want to use fernandojsg's teleport controls on my A-Frame project, but the way I want to use them is with shake.js, one shake to make the teleport line appear and another one to actually teleport where you selected.
I've seen the documentations and came across the startEvents and endEvent properties, and I want to map them into the shake event... for me it sounds like I have to create a custom component to do this, but I wanted to seek help first, to see if this is possible without doing it.
So far I've made this (glitch.com/ link) but it doesn't work so far (please note that I've got some other libraries there that make use of shake, mousedown, and similar events... my plan is to activate or deactivate them depending on how the user wants to move)
<a-entity id="player" wasd-controls tap-to-walk>
<a-camera id="eyes" position="-.5 1.5 0" camera="" look-controls="" mouse-cursor="">
<a-entity id="cursor" cursor="fuse: false;"
position="0 0 -1"
geometry="primitive: ring; radiusInner: 0.015; radiusOuter: 0.019"
material="color: white; shader: flat"
raycaster="far: 5; interval: 1000; objects: .clickable">
</a-entity>
<a-entity id="texto" text="value:Hola;align:center" position="0 -.3 -0.5"></a-entity>
<a-plane position="0 .7 -1" material="transparent: true; opacity: 0.5; color: #ffec04; shader:flat;" scale="1 0.2 1"></a-plane>
</a-camera>
<a-entity
teleport-controls="cameraRig: #player; teleport-origins: #eyes; startEvents:['shake','mousedown']"> </a-entity>
<a-entity id="step" sound="src: #step1"></a-entity>
</a-entity>
Thanks...
Yes you will need a some JS or a custom component to get shake.js and teleport controls to work together for two reasons:
shake.js emits its events outside the A-Frame scene on window where teleport-controls is not listening
shake.js only emits one event type, and you need to differentiate for startEvents and endEvents
But it doesn't need to be very complicated:
AFRAME.registerComponent('shake-listener', {
init: function () {
var targetEl = this.el
var count = 0
// you could also initialize the shake instance here
window.addEventListener('shake', function () {
if (++count % 2) {
targetEl.emit('shakestart')
} else {
targetEl.emit('shakeend')
}
})
}
})
Then add shake-listener to the same entity as teleport-controls="startEvents: shakestart; endEvents: shakeend"
For an Hackaton, we have to use movement with keyboard and camera management.
I didn't find on the doc, the possibility to use both.
To solve the problem (for the demo only) I catch the key event myself like this:
<!--html-->
<a-entity camera id="camera" look-controls position="0 1.764 0">
<a-entity id="cursor" cursor="fuse: false; fuseTimeout: 500" position="0 0 -1"
geometry="primitive: ring; radiusInner: 0.01; radiusOuter: 0.02;" material="color: #CCC; shader: flat;">
</a-entity>
</a-entity>
//javascript
priv.bindEvent = function(evt) {
var el = document.getElementById('camera').getAttribute('position');
var element = document.getElementById('camera');
switch(evt.key) {
case "z":
el.z -= 0.2;
element.setAttribute('position', el.x+" "+el.y+" "+el.z);
break;
case "q":
el.x -= 0.2;
element.setAttribute('position', el.x+" "+el.y+" "+el.z);
break;
case "d":
el.x += 0.2;
element.setAttribute('position', el.x+" "+el.y+" "+el.z);
break;
case "s":
el.z += 0.2;
element.setAttribute('position', el.x+" "+el.y+" "+el.z);
break;
default:
break;
}
};
We are discovering this awesome library and I ask if there are on other way to use this feature.
thanks in advance
The default way to do this is to use look-controls as you are already doing, along with wasd-controls for the keyboard.
Example:
<a-entity camera look-controls wasd-controls></a-entity>
You can also examine the source code for wasd-controls if you need to fork it and make changes for a different layout.