I'm using gaze buttons but when the click event is triggered, the function is executed two times. See code snippet below.
var number = 0;
document.getElementsByTagName('a-sphere')[0].addEventListener('click', function(){
alert('You\'ve clicked the sphere ' + (++number) + ' times.');
});
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<a-scene>
<!-- this is my object that must execute a click event when looked -->
<a-sphere position="0 0 -7" color="red">
</a-sphere>
<!-- camera -->
<a-camera look-controls wasd-controls cursor="maxDistance: 30; fuse: true">
<!-- progress bar -->
<a-entity position="0 0 -3" geometry="primitive: ring; radiusOuter: 0.07;radiusInner: 0.05;" material="color: cyan; shader: flat"
cursor="maxDistance: 30; fuse: true">
<!--<a-cursor color="red"></a-cursor>-->
<a-animation begin="click" easing="ease-in" attribute="scale" fill="backwards" from="0.1 0.1 0.1" to="1 1 1" dur="150"></a-animation>
<a-animation begin="fusing" easing="ease-in" attribute="scale" fill="forwards" from="1 1 1" to="0.1 0.1 0.1" dur="1500"></a-animation>
</a-entity>
</a-camera>
</a-scene>
How could I prevent this situation of two times clicking on an object? It must be triggered just one time when it's clicked.
I've made a workaround to prevent this issue
var number = 0;
document.getElementsByTagName('a-sphere')[0].addEventListener('click', function(){
if(this.getAttribute('data-clicked') === null || this.getAttribute('data-clicked') === 'false') {
this.setAttribute('data-clicked', 'true');
alert('You\'ve clicked the sphere ' + (++number) + ' times.');
}
else {
this.setAttribute('data-clicked', 'false');
}
});
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<a-scene>
<!-- this is my object that must execute a click event when looked -->
<a-sphere position="0 0 -7" color="red">
</a-sphere>
<!-- camera -->
<a-camera look-controls wasd-controls cursor="maxDistance: 30; fuse: true">
<!-- progress bar -->
<a-entity position="0 0 -3" geometry="primitive: ring; radiusOuter: 0.07;radiusInner: 0.05;" material="color: cyan; shader: flat"
cursor="maxDistance: 30; fuse: true">
<!--<a-cursor color="red"></a-cursor>-->
<a-animation begin="click" easing="ease-in" attribute="scale" fill="backwards" from="0.1 0.1 0.1" to="1 1 1" dur="150"></a-animation>
<a-animation begin="fusing" easing="ease-in" attribute="scale" fill="forwards" from="1 1 1" to="0.1 0.1 0.1" dur="1500"></a-animation>
</a-entity>
</a-camera>
</a-scene>
Of course this is not a good way of preventing the issue, but it's working.
Related
Good morning, how are you? I ask a question, Im making a game in AR of soccer penalties. I am using a-frame and ammo.js for physics.
I wanted to know how I can make a shot of the ball once the screen is touched, taking into account the position and the view of the camera.
I could use cannon.js but due to non-updated features of the library I had to migrate to ammo.js
I'm very busy and the ammo.js documentation lacks information about it. Any tip is welcome, thank you very much
My html:
<a-scene
physics="driver: ammo;"
responsive-immersive
init
init-portal
xrextras-almost-there
xrextras-loading="
loadBackgroundColor: #fff;
cameraBackgroundColor: #5AC8FA;
loadImage: #logo;
loadAnimation: pulse"
xrextras-runtime-error
xrextras-gesture-detector
renderer="colorManagement:true"
xrweb="
allowedDevices: any;
defaultEnvironmentFogIntensity: 0.3;
defaultEnvironmentFloorColor: #FFF;
defaultEnvironmentSkyBottomColor: #5ac8fa;
defaultEnvironmentSkyTopColor: #007aff;
defaultEnvironmentSkyGradientStrength: 0.25;">
<a-assets timeout="10000">
<!-- For the loader logo -->
<img id="logo" src="./assets/logo.png">
<img id="asset-uno" src="./assets/images/1.png">
<img id="asset-dos" src="./assets/images/2.png">
<img id="asset-tres" src="./assets/images/3.png">
<!-- For the ground cursor -->
<a-asset-item response-type="arraybuffer" id="asset-ground-cursor" src="./assets/ground_cursor.glb"></a-asset-item>
<a-asset-item response-type="arraybuffer" id="asset-intro-model" src="./assets/models/Logo-static.glb"></a-asset-item>
<a-asset-item response-type="arraybuffer" id="asset-transparent-intro-model" src="./assets/models/LogoCursor-transparent.glb"></a-asset-item>
<a-asset-item response-type="arraybuffer" id="asset-copa" src="./assets/models/Copa.glb"></a-asset-item>
<a-asset-item response-type="arraybuffer" id="asset-pelota" src="./assets/models/soccer_ball.glb"></a-asset-item>
<a-asset-item id="pin" src="./assets/models/arcquito.glb"></a-asset-item>
<a-asset-item id="glove" src="./assets/models/scanned_soccer_shoe.glb"></a-asset-item>
<a-asset-item id="goalkepper" src="./assets/models/goalkeeper.glb"></a-asset-item>
<img id="soccer" src="./assets/textures/pelota.jpg">
<!-- Audios -->
<a-asset-item id="asset-audio-fondo" src="./assets/audios/musicaWabi.mp3" response-type="arraybuffer"></a-asset-item>
</a-assets>
<xrextras-capture-button capture-mode="standard"></xrextras-capture-button>
<!-- configure capture settings -->
<xrextras-capture-config
max-duration-ms="7000"
enable-end-card="false"
watermark-image-url="./assets/logo.png"
watermark-max-width="100"
watermark-max-height="10"
file-name-prefix="media-"
request-mic="manual">
</xrextras-capture-config>
<!-- add capture preview -->
<xrextras-capture-preview></xrextras-capture-preview>
<!-- Can change target property to be the camera or a single model -->
<a-entity
xr-light
light="
type: directional;
intensity: 0.8;
target: #wrapper;
castShadow: true;
shadowMapHeight: 2048;
shadowMapWidth: 2048;
shadowCameraTop: 20;
shadowCameraBottom: -20;
shadowCameraRight: 20;
shadowCameraLeft: -20;"
xrextras-attach="target: camera; offset: 8 15 4"
position="1 4.3 2.5"
shadow>
</a-entity>
<a-light xr-light type="ambient" intensity="0.5"></a-light>
<!-- Camera + Glove-->
<a-camera id="camera" position="0 3 0">
<a-entity
id="glove-model"
physics-object="model: #glove; body: kinematic; shape: box; id: soccerShoesModel"
ammo-body="type: kinematic; emitCollisionEvents: true"
scale="2 2 2"
rotation="0 180 0"
position="0.12 -1 -1.2"
visible="false">
</a-entity>
<a-sound id="audio-fondo" src="#asset-audio-fondo" loop="true" autoplay="false" volume="0.2"></a-sound>
</a-camera>
<a-entity
id="wrapper"
position="0 0 0"
rotation="0 0 0"
class="cantap"
visible="false">
<!-- Intro model for gestures detection -->
<a-entity id="introModel" position="0 0 0" scale="1.5 1.5 1.5" gltf-model="#asset-intro-model" visible="false"></a-entity>
<!-- Models and other entities -->
<a-sphere
id="soccer-ball"
class="cantap"
position="0 2 -4"
radius="0.75"
material="shader: flat; src: #soccer"
ammo-body="type: dynamic; emitCollisionEvents: true"
ammo-shape="type: sphere"
shadow
visible="false">
</a-sphere>
<a-entity
id="goalkepperModel"
physics-object="model: #goalkepper; body: kinematic; id: arqueroHijoModel;"
ammo-body="type: kinematic;"
ammo-shape="type: sphere"
scale="0.3 0.3 0.3"
position="0 0 -13"
rotation="0 0 0"
animation="property: position; to: -5 0 -13; from: 5 0 -13; dir: alternate; loop: true; easing: easeInOutQuad; dur: 2500"
visible="false">
</a-entity>
<a-entity
id="pin1"
physics-object="model: #pin; body: static; id: arcoHijoModel"
ammo-body="type: static;"
ammo-shape="type: box"
scale="0.9 1 1"
position="0 1 -20"
rotation="0 180 0"
visible="false">
</a-entity>
<a-entity
id="primerObjetivo"
physics-object="model: #asset-intro-model; body: kinematic; id: primerObjetivoModel"
ammo-body="type: kinematic;"
ammo-shape="type: box"
scale="1.5 1.5 1.5"
position="-6 0 -16"
rotation="0 0 0"
visible="false">
</a-entity>
<a-entity
id="segundoObjetivo"
physics-object="model: #asset-intro-model; body: kinematic; id: segundoObjetivoModel"
ammo-body="type: kinematic;"
ammo-shape="type: sphere"
scale="1.5 1.5 1.5"
position="6 0 -16"
rotation="0 0 0"
visible="false">
</a-entity>
<a-entity
id="tercerObjetivo"
physics-object="model: #asset-intro-model; body: kinematic; id: tercerObjetivoModel"
ammo-body="type: kinematic;"
ammo-shape="type: box"
scale="1.5 1.5 1.5"
position="-6 4 -16"
rotation="0 0 0"
visible="false">
</a-entity>
<a-entity
id="cuartoObjetivo"
physics-object="model: #asset-intro-model; body: kinematic; id: cuartoObjetivoModel"
ammo-body="type: kinematic;"
ammo-shape="type: sphere"
scale="1.5 1.5 1.5"
position="6 4 -16"
rotation="0 0 0"
visible="false">
</a-entity>
<a-image id="unoModel" scale="0.0001 0.0001 0.0001" position="0 7.2 -18" src="#asset-uno" visible="false"></a-image>
<a-image id="dosModel" scale="0.0001 0.0001 0.0001" position="0 7.2 -18" src="#asset-dos" visible="false"></a-image>
<a-image id="tresModel" scale="0.0001 0.0001 0.0001" position="0 7.2 -18" src="#asset-tres" visible="false"></a-image>
<a-plane
id="ground"
height="2000"
width="2000"
rotation="-90 0 0"
position="0 0.01 0"
material="shader: shadow; transparent: true; opacity: 0.4"
ammo-body="type: static"
ammo-shape="type: box"
shadow
visible="false">
</a-plane>
</a-entity>
</a-scene>
I (a very amateur programmer) am trying to make an environment in A-frame where you point a cursor at different objects and it changes the displayed value on a thermometer: https://fst-retail-safety.glitch.me
The site never loads completely on the first try but reliably loads upon refresh. If I omit the cursor listener from the GLTF used for the environment, it also loads correctly. Any ideas on how to fix this? Perhaps the cursor is trying to fuse with the background object before it loads and it's causing an error? Many thanks for any help.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Food Service Temperature Inspection</title>
<meta name="description" content="Environment test" />
<script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras#v6.1.1/dist/aframe-extras.min.js"></script>
<script>
AFRAME.registerComponent("cursor-listener-box", {
init: function() {
this.el.addEventListener("click", function(evt) {
let target1 = document.querySelector("#IRtext");
target1.setAttribute("value", "100");
});
}
});
</script>
<script>
AFRAME.registerComponent("cursor-listener-background", {
init: function() {
this.el.addEventListener("click", function(evt) {
let target1 = document.querySelector("#IRtext");
target1.setAttribute("value", "65");
});
}
});
</script>
</head>
<body>
<a-scene background="color: #FAFAFA" renderer="colorManagement: true">
<a-assets>
<a-asset-item
id="gltf"
src="https://cdn.glitch.com/13648f22-5ed9-4349-ad71-1e8bd63b6485%2Fpumptest20.glb"
></a-asset-item>
<a-asset-item
id="IRthermo"
src="https://cdn.glitch.me/7344c97b-c709-4f6e-a17b-3a7e1c7278a4%2FIR%20thermometer.glb"
></a-asset-item>
<a-asset-itm
id="navmesh"
src="https://cdn.glitch.com/13648f22-5ed9-4349-ad71-1e8bd63b6485%2Fpumpnavmesh7.glb"
></a-asset-itm>
<img
id="concrete"
src="https://cdn.glitch.me/13648f22-5ed9-4349-ad71-1e8bd63b6485%2Fconcrete.jpg"
/>
<img
id="background"
src="https://cdn.glitch.me/13648f22-5ed9-4349-ad71-1e8bd63b6485%2Findustrial_1k.jpg"
/>
<img
id="watertank"
src="https://cdn.glitch.me/13648f22-5ed9-4349-ad71-1e8bd63b6485%2Fwatertank.jpg"
/>
</a-assets>
<a-entity
gltf-model="#gltf"
scale="0.15 0.15 0.15"
position="0 1.5 -2"
cursor-listener-background
></a-entity>
<a-light
type="directional"
intensity="0.7"
color="#FAFAFA"
position="0 4 0"
></a-light>
<a-light
type="ambient"
intensity="0.6"
color="#FAFAFA"
position="0 4 0"
></a-light>
<a-box
material="src: #watertank"
scale="5 3.3 5"
position="-6.3 1.93 -1.4"
></a-box>
<a-box
material="src: #watertank"
scale="5 3.3 5"
position="-2 4.5 -11"
></a-box>
<!--test shapes for script-->
<a-entity
geometry="primitive:box"
material="color:green; shader:flat"
position="0 1.6 -1"
scale="0.2 0.2 0.2"
cursor-listener-box
></a-entity>
<a-entity
gltf-model="#navmesh"
scale="0.15 0.15 0.15"
position="0 -.3 -2"
visible="true"
nav-mesh
></a-entity>
<a-entity
geometry="primitive: plane; height: 10; width: 10"
material="src: #concrete; repeat:10 10"
position="0 .3 0"
rotation="-90 0 0"
></a-entity>
<a-sky src="#background"></a-sky>
<!--Player rig-->
<a-entity
id="rig"
movement-controls="speed: 0.08; constrainToNavMesh: true"
>
<a-entity camera position="0 1.6 0" look-controls>
<a-entity
cursor="fuse: true; fuseTimeout: 100"
position="0 0 -0.5"
geometry="primitive: ring; radiusInner: 0.007; radiusOuter: 0.01"
material="color: black; shader: flat"
>
</a-entity>
<a-entity
gltf-model="#IRthermo"
scale="0.05 0.05 0.05"
position="0.15 0 -0.5"
rotation="0 -90 0"
></a-entity>
<a-text
id="IRtext"
value="25"
align="center"
color="black"
position="0.184 0.01 -0.295"
scale="0.1 0.1 0.1"
rotation="-16 0 0"
></a-text>
<a-text
value="TEMPERATURE"
align="center"
color="black"
position="0.184 0.03 -0.295"
scale="0.03 0.03 0.03"
rotation="-16 0 0"
></a-text>
<a-text
value="F"
align="center"
color="black"
position="0.19 -0.014 -0.29"
scale="0.1 0.1 0.1"
rotation="-16 0 0"
></a-text>
<a-entity
geometry="primitive: ring; radiusInner:0.002; radiusOuter:0.003"
material="color: black; shader: flat"
position="0.18 -0.01 -0.29"
rotation="-16 0 0"
></a-entity>
</a-entity>
</a-entity>
</a-scene>
</body>
</html>
I've opened it up, and when something in the scene isn't loaded, there is a nice log in the console:
Uncaught TypeError: Cannot read property 'array' of undefined
at TextGeometry.computeBoundingSphere (index.js:96)
at P.raycast (three.js:8649)
(....)
No need to search for the array of undefined - the rest tells us that there is an issue with raycasting the text elements.
Since you only want to interact with certain things (which aren't text elements), you can specify which items should the cursor interact with:
<!-- let them share a common class -->
<a-box class="interactable></a-box>
<!-- raycaster setup -->
<a-entity
cursor="fuse: true; fuseTimeout: 100"
raycaster="objects: .interactable">
</a-entity>
Like i did in this remix
Why new_position value does not change when moving the camera and clicking again?
<html>
<head>
<meta charset="UTF-8" />
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-event-set-component#3.0.3/dist/aframe-event-set-component.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras#v6.1.1/dist/aframe-extras.min.js"></script>
<script>
AFRAME.registerComponent("go_in_front_camera", {
init: function() {
var el = this.el;
el.addEventListener("click", function() {
console.log(
document.querySelector("a-scene").camera.el.object3D.position.x
);
var new_position_x = document.querySelector("a-scene").camera.el
.object3D.position.x;
var new_position_z = document.querySelector("a-scene").camera.el
.object3D.position.z;
var new_position = {
x: new_position_x - 1,
y: el.object3D.position.y,
z: new_position_z - 1
};
console.log(new_position);
el.setAttribute("animation", {
property: "position",
to: new_position,
dur: 2000,
easing: "linear"
});
});
}
});
</script>
</head>
<body>
<a-scene>
<a-entity
id="camera-rig"
position="0 0 0"
movement-controls="controls: cardboard, gamepad, keyboard, touch; constrainToNavMesh: true; speed: 0.2"
>
<a-entity
id="head"
camera
look-controls="pointerLockEnabled: true"
position="0 1.6 0"
>
<a-entity
cursor="fuse: true;fuseTimeout: 500"
animation__click="property: scale; startEvents: click; easing: easeInCubic; dur: 150; from: 0.1 0.1 0.1; to: 1 1 1"
animation__fusing="property: scale; startEvents: fusing; easing: easeInCubic; dur: 1500; from: 1 1 1; to: 0.1 0.1 0.1"
animation__mouseleave="property: scale; startEvents: mouseleave; easing: easeInCubic; dur: 500; to: 1 1 1"
geometry="primitive: ring; radiusInner: 0.002; radiusOuter: 0.003"
material="shader: flat; color: #f7f7ed;"
position="0 0 -0.1"
></a-entity>
</a-entity>
</a-entity>
<a-plane
id="ground_floor"
color="blue"
repeat="10 10"
rotation="-90 0 0"
width="30"
height="30"
></a-plane>
<a-sky color="#212120"></a-sky>
<a-box
position="0 1 -2"
width="1"
height="1"
color="red"
go_in_front_camera
></a-box>
</a-scene>
</body>
</html>
I edited my code to show a full example.
You're placing the box at "-1 0 -1" regarding the camera. But the camera isn't moving. It's just rotating. So it's world position isn't changing at all.
either:
translate the coordinates to the camera local space of reference
get the camera direction vector, and add it to the camera
Direction option:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script>
AFRAME.registerComponent('go_in_front_camera', {
init: function() {
var el = this.el;
const dir = new THREE.Vector3();
el.addEventListener('click', function() {
const acamera = document.querySelector('a-scene').camera.el.object3D
acamera.getWorldDirection(dir);
dir.multiplyScalar(-2);
dir.add(acamera.position)
el.setAttribute('animation', {
property: 'position',
to: AFRAME.utils.coordinates.stringify(dir),
dur: 2000,
easing: 'linear'
});
});
}
});
</script>
<a-scene cursor="rayOrigin: mouse" raycaster="objects: a-box">
<a-box position="-1 0.5 -3" color="#4CC3D9" go_in_front_camera></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-entity id="camera-rig" position="0 0 0" movement-controls="controls: cardboard, gamepad, keyboard, touch; constrainToNavMesh: true; speed: 0.2">
<a-entity id="head" camera look-controls="pointerLockEnabled: true" position="0 1.6 0">
<a-entity geometry="primitive: ring; radiusInner: 0.002; radiusOuter: 0.003" material="shader: flat; color: #f7f7ed;" position="0 0 -0.1"></a-entity>
</a-entity>
</a-entity>
</a-scene>
Since it's a rig with camera as child, the position should not be read on the camera object but on the camera parent object.
document.querySelector("a-scene").camera.el.parentNode.object3D.position
I want to add gradient color to the sphere, currently I am setting the color:
<a-sphere position="1 1 0" color="#ef2d5e">
<a-animation attribute="rotation" to="0 360 0" dur="10000" easing="linear" repeat="indefinite">
</a-animation>
</a-sphere>
Demo
How do I add gradient to the sphere? Is there any way.
Try https://github.com/zcanter/aframe-gradient-sky
<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://rawgit.com/zcanter/aframe-gradient-sky/master/dist/gradientsky.min.js"></script>
<a-scene>
<!-- A Sphere -->
<a-entity geometry="primitive: sphere" material="shader: gradient; topColor: 255 100 100; bottomColor: 100 100 200; offset: 0.0001" position="0 0 -5"></a-entity>
</a-scene>
I am a new to AFRAME, I expect there will be a ring(or the cursor) being colored with green little by little when the cursor moves on someone entity, this action will notify how long time remaining to trigger the fuse. Can AFRAME do that?
Yes. Here is an example with scale:
https://aframe.io/docs/0.3.0/components/cursor.html#adding-visual-feedback
<a-entity cursor="fuse: true; fuseTimeout: 500"
position="0 0 -1"
geometry="primitive: ring"
material="color: black; shader: flat">
<a-animation begin="click" easing="ease-in" attribute="scale"
fill="backwards" from="0.1 0.1 0.1" to="1 1 1"></a-animation>
<a-animation begin="cursor-fusing" easing="ease-in" attribute="scale"
fill="forwards" from="1 1 1" to="0.1 0.1 0.1"></a-animation>
</a-entity>
To do a circle, you can animation the circle's thetaLength rather than scale to make it draw a circle as it fuses.
<a-entity cursor="fuse: true; fuseTimeout: 500"
position="0 0 -1"
geometry="primitive: ring"
material="color: black; shader: flat">
<a-animation begin="click" easing="ease-in" attribute="scale"
fill="backwards" from="0.1 0.1 0.1" to="1 1 1"></a-animation>
<a-animation begin="cursor-fusing" easing="ease-in" attribute="geometry.thetaLength"
fill="forwards" from="0" to="360"></a-animation>
</a-entity>