AFrame - Change controls type using setAttribute - aframe

I would like to remove the twoway-motion component from the entity player, and replace it with the progressive-controls component, using setAttribute.
While the removeAttribute works fine for removing the twoway-motion, the setAttribute does not add the progressive-controls.
<a-entity id="player" networked="template:#avatar-template;showLocalTemplate:false;"
camera spawn-in-circle="radius:3;"
position="0 1.3 0"
wasd-controls
look-controls
twoway-motion="speed: 35">
<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"
id="defaultCursor">
</a-entity>
</a-entity>
<script>
var playerEl = document.getElementById('player');
var cursorEl = document.getElementById('defaultCursor');
playerEl.removeChild(cursorEl);
playerEl.removeAttribute('twoway-motion');
playerEl.setAttribute('progressive-controls');
</script>

the setAttribute(name, value) requires a value.
To add the component, just use setAttribute('progressive-controls', '') to add it with the default schema

Related

Aframe - How to render HUD on top of everything else

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

How do I use checkpoint controls in A-Frame?

I am new to A-Frame and still trying to figure everything out! I'm currently constructing a 3D space and would like to create a guided experience for visitors by providing dots on the floor for them to click and be transported to that position. I found this code online which is perfect but I can't get it to work.
Here is the link to my project on Glitch: https://glitch.com/~museum-exhibit-demo
This is the code for my camera:
<a-entity position="1.8 -1.1 3" rotation="0 90 0" id="pov">
<a-camera universal-controls="movementControls: checkpoint" checkpoint-controls="mode: animate">
<a-entity cursor position="0 0 -1" geometry="primitive: ring; radiusInner: 0.01; radiusOuter: 0.015;" material="color: #CCC; shader: flat;"> </a-entity>
</a-camera>
</a-entity>
And this is the code for the cylinder:
<a-cylinder checkpoint radius="0.1.5" height="0.01" position="-0.164 0.111 2.363" color="#39BB82"></a-cylinder>
Can anyone spot where I'm going wrong?
UPDATE:
I just read the current source of aframe-extra and it seems that nothing is broken! In fact there was a backward-incompatible change in the new versions. Rather than the old syntax of:
universal-controls="movementControls: checkpoint;"
Now this new syntax should be used:
movement-controls="controls: checkpoint;"
But keep in mind that since version 3.2.7, the movement offset is calculated on all 3 XYZ axis and therefore the camera will move to the center of the checkpoint. If you want to preserve the height (y) then simply add the code below above line 83:
targetPosition.y = position.y;
Here is a complete working example:
<html>
<head>
<meta charset="utf-8">
<title>Checkpoint Control with AFrame 1.2.0</title>
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras#v6.1.1/dist/aframe-extras.min.js"></script>
</head>
<body>
<a-scene stats>
<!-- CAMERA -->
<a-entity position="0 0 0" id="pov">
<a-camera camera="active: true; spectator: false;" look-controls="pointerLockEnabled:true" movement-controls="controls: checkpoint;" checkpoint-controls="mode: animate; animateSpeed: 10" wasd-controls="enabled: false;" position="0 1.6 22">
<a-cursor></a-cursor>
</a-camera>
</a-entity>
<!-- CHECKPOINTS -->
<a-cylinder checkpoint radius="0.5" height="0.01" position="0 0 20" color="#FF0000"></a-cylinder>
<a-cylinder checkpoint radius="0.5" height="0.01" position="0 0 16" color="#FF0000"></a-cylinder>
<a-cylinder checkpoint radius="0.5" height="0.01" position="0 0 12" color="#FF0000"></a-cylinder>
<a-cylinder checkpoint radius="0.5" height="0.01" position="0 0 8" color="#FF0000"></a-cylinder>
</a-scene>
</body>
</html>
Information below this line is not valid anymore.
As Piotr already mentioned, the new releases of Aframe-Extra are somehow broken!
Using an older version everything will work again.
Below is a working example with Aframe-extra version 3.2.7.
Once the page is fully loaded, click on the screen to lock the cursor, then point the cursor (tiny ring) at a red circle and click.
I also noted few additional options just in case:
spectator: false (switch between 1st and 3rd person view)
pointerLockEnabled:true (hide the mouse)
mode: animate (the other option is teleport)
animateSpeed: 10 (well... adjusts the animation speed)
wasd-controls="enabled: false;" (otherwise user can move around via WASD/arrow keys)
Code:
<html>
<head>
<meta charset="utf-8">
<title>Checkpoint Control with AFrame 1.2.0</title>
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras#v3.2.7/dist/aframe-extras.min.js"></script>
</head>
<body>
<a-scene stats>
<!-- CAMERA -->
<a-entity position="0 0 0" id="pov">
<a-camera camera="active: true; spectator: false;" look-controls="pointerLockEnabled:true" universal-controls="movementControls: checkpoint;" checkpoint-controls="mode: animate; animateSpeed: 10" wasd-controls="enabled: false;" position="0 1.6 22">
<a-cursor></a-cursor>
</a-camera>
</a-entity>
<!-- CHECKPOINTS -->
<a-cylinder checkpoint radius="0.5" height="0.01" position="0 0 20" color="#FF0000"></a-cylinder>
<a-cylinder checkpoint radius="0.5" height="0.01" position="0 0 16" color="#FF0000"></a-cylinder>
<a-cylinder checkpoint radius="0.5" height="0.01" position="0 0 12" color="#FF0000"></a-cylinder>
<a-cylinder checkpoint radius="0.5" height="0.01" position="0 0 8" color="#FF0000"></a-cylinder>
</a-scene>
</body>
</html>
This won't answer the question, but should solve your problem.
You can substitute the checkpoint-controls with a simple animation system:
you click on a cylinder
you animate the camera from the current position to the cylinder
Which could be implemented like this:
// use a system to keep a global track if we are already moving
AFRAME.registerSystem('goto', {
init: function() {
this.isMoving = false
}
})
// this component will have the actual logic
AFRAME.registerComponent('goto', {
init: function() {
let camRig = document.querySelector('#rig')
// upon click - move the camera
this.el.addEventListener('click', e => {
// check if we are already moving
if (this.system.isMoving) return;
// lock other attempts to move
this.system.isMoving = true
// grab the positions
let targetPos = this.el.getAttribute("position")
let rigPos = camRig.getAttribute("position")
// set the animation attributes.
camRig.setAttribute("animation", {
"from": rigPos,
"to": AFRAME.utils.coordinates.stringify({x: targetPos.x, y: rigPos.y, z: targetPos.z}),
"dur": targetPos.distanceTo(rigPos) * 750
})
camRig.emit('go')
})
// when the animation is finished - update the "shared" variable
camRig.addEventListener('animationcomplete', e=> {
this.system.isMoving = false
})
}
})
with a setup like this:
<!-- Camera with locked movement --/>
<a-entity id="rig" animation="property: position; startEvents: go">
<a-camera look-controls wasd-controls-enabled="false"></a-camera>
<a-entity>
<!-- Cylinder node --/>
<a-cylinder goto></a-cylinder>
You can see it working in this glitch.

In aframe, how do you make the property visible="false" ignore the raycaster?

I have some objects in aframe with visible="false" meaning they aren't visible but they are still interactable even though you can't see them. Is there a solution to this?
Found a few similar cases online but no clear solution.
https://github.com/aframevr/aframe/issues/3551
https://github.com/aframevr/aframe/issues/979
<!-- CURSOR ENTITY -->
<a-entity rotation="0 0 0" position="0 0 2">
<a-entity id="camera" far: 20; camera look-controls rotation="0 0 0" wasd-controls>
<!-- MAIN CURSOR -->
<a-entity raycaster="interval: 200; objects: .clickable" cursor="fuse: true; maxDistance: 500; timeout: 1500;" id="cursor-main" position="0 0 -1.5" geometry="primitive: ring; radiusOuter: 0.04; radiusInner: 0.03; thetaLength: 360; thetaStart: 90;" material="color: #439DC2;">
<a-animation begin="cursor-fusing" attribute="geometry.thetaLength" fill="forwards" from="360" to="0" easing="ease-in"></a-animation>
<a-animation begin="mouseleave" attribute="geometry.thetaLength" fill="backwards" from="0" to="360" dur="0"></a-animation>
</a-entity>
<a-entity id="cursor-loader" position="0 0 -1.51" geometry="primitive: ring; radiusOuter: 0.04; radiusInner: 0.03;" material="color: #2ADD2A;">
</a-entity>
</a-entity>
</a-entity>
<a-entity ui-modal="triggerElement:#selection;" visible="false">
<a-image position="-0.7 -1 1" class="clickable" src="#one" scale="0.7 0.7 0" link="href:location1.html; on: click; visualAspectEnabled: false" src-fit></a-image>
<a-image position="0 -1 1" class="clickable" src="#two" scale="0.7 0.64 0" link="href:location2.html; on: click; visualAspectEnabled: false" src-fit></a-image>
<a-image position="0.7 -1 1" class="clickable" src="#three" scale="0.7 0.7 0" link="href:location3.html; on: click; visualAspectEnabled: false" src-fit></a-image>
<a-image class="clickable" src="#close" id="closing" scale="0.3 0.3 0" position="-0.5 -0.35 1.1"></a-image>
</a-entity>
Ideally if an object is set to visible="false" the user shouldn't be able to interact with it.
As seen in the discussion, if you want to ignore certain objects, you should not rely on visibility. The proper way would be using the objects whitelist:
<a-scene cursor raycaster='objects: .clickable>
The whitelist uses typical CSS selectors, so in the above example the raycaster would only interact with elements which have a clickable class.
You can add / remove it with the visibility:
if (condition) {
el.setAttribute('visible', 'false')
el.classList.remove('clickable')
} else {
el.setAttribute('visible', 'true')
el.classList.add('clickable')
}
You can check it out in this fiddle. Click the sphere to make the box clickable / not clickable.
A hacky workaround could be also setting the scale to 0 0 0. The element won't be visible, and won't be clickable.

Adding 'rayOrigin: mouse' to Aframe cursor component throwing error

I am trying to include a mouse click functionality to VR scene apart from focus cursor. I am using it as below -
<a-entity camera look-controls mouse-cursor>
<a-entity position="0 0 -3" scale="0.2 0.2 0.2" geometry="primitive: ring; radiusOuter: 0.20;radiusInner: 0.10;" material="color: #990000; shader: flat" cursor=" fuse: true; rayOrigin: mouse">
</a-entity>
</a-entity>
Below is the error while using this code. Please note that i am using Afrmae with Angular2
ERROR TypeError: Cannot read property 'count' of undefined
at Mesh.raycast (aframe-master.js:21938)
at intersectObject (aframe-master.js:45999)
at Raycaster.intersectObjects (aframe-master.js:46072)
at NewComponent.module.exports.Component.registerComponent.tick (aframe-master.js:69697)
at HTMLElement.value (aframe-master.js:76597)
at HTMLElement.value (aframe-master.js:76645)
at bound (aframe-master.js:79931)
at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:425)
at Object.onInvokeTask (core.es5.js:3881)
at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
If you want your mouse to act like a <a-cursor> You need to move the cursor bit to the <a-scene> :
<a-scene cursor="rayOrigin:mouse">

(A-Frame) How to stop a object intersect with cursor?

I use a-frame to do web-vr. I's good to use but I have some situation don't know how to implement.
First I put a Camera with cursor and set the raycaster intersect with object ".trigger".
<a-entity camera="" look-controls="" position="" rotation="" scale="" visible="">
<a-entity cursor="fuse: true; fuseTimeout: 1500" position="0 0 -1" geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03" material="color: cyan; shader: flat" raycaster="objects: .trigger" rotation="" scale="" visible="">
<a-animation begin="click" easing="ease-in" attribute="scale" fill="forwards" from="0.1 0.1 0.1" to="1 1 1" dur="150"></a-animation>
<a-animation begin="cursor-fusing" easing="ease-in" attribute="scale" fill="backwards" from="1 1 1" to="0.1 0.1 0.1" dur="1500"></a-animation>
</a-entity>
</a-entity>
And create a entity with class "trigger".
<a-entity class="tigger" id="clip01" clip01="on: click; conditionId: ShowClipTrigger" data-is-trigger="true" geometry="primitive: plane; width: 2; height: 3" material=" src: #clip01-pic; opacity: 0.99;" position="-5.913 -3.544 4.675" rotation="-82.048 122.222 11.345" scale="" visible="" animation__move="" animation__rotate=""></a-entity>
The ".trigger" object will trigger some action after on click event received. My question is how to disable the intersect after the trigger been click. I try remove the class "trigger" from the object after the click evnet, but it still can intersect with cursor.
I search the a-frame document raycaster but no clue.
After removing the whitelisted class, You need to refresh the raycaster:
var raycasterEl = AFRAME.scenes[0].querySelector('[raycaster]');
raycasterEl.components.raycaster.refreshObjects();
Also You can achieve this by making a simple switch in the event listener:
var switch=true;
el.addEventListener("click", function(evt) {
if(switch){
//doStuff
switch = false;
}
});
or remove the listener completely, like i did here:
//listener
this.doStuff = this.doStuff.bind(this);
el.addEventListener("click", this.doStuff());
//function
doStuff: function(){
this.el.removeEventListener("click",this.doStuff);
}

Resources