I am having a problem with an A-Frame logic gate type component. I'm working on this for a 7th grade school project (I may have chosen something quite advanced, but I have managed to almost complete it), however I am unable to find the error here.
The issue I am having is the connected component (in this case a door with the gate as its child) is not responding to signals from the gate when it seems like it should. Currently, the door is stuck in the "closed" position no matter the states of the buttons, however what should be happening is that when the buttons that are active (red) meet the logic condition specified in the gate's attributes, the gate should emit the specified event. The door that the gate controls I have tested before and has worked with buttons, which makes me think the problem is with the logic gate itself. I've checked the syntax on everything, and it seems correct, and the debug console gives no errors at all, which makes debugging much harder.
<script>
AFRAME.registerComponent('logic', {
schema: {
type: {
type: 'string',
default: 'and'
},
input1: {
type: 'string'
},
input2: {
type: 'string'
},
event: {
type: 'string'
}
},
init: function () {
var entity = this.el;
var scene = entity.sceneEl;
var event = this.data.event;
this.activeOut = false;
this.active1 = false;
this.active2 = false;
var type = this.data.type;
var input1 = this.data.input1;
var input2 = this.data.input2;
// Toggle each input
entity.addEventListener(input1, function (event) {
if(!this.active1) {
this.active1 = true;
}
else {
this.active1 = false;
}
});
entity.addEventListener(input2, function (event) {
if(!this.active2) {
this.active2 = true;
}
else {
this.active2 = false;
}
});
},
tick: function (time, timeD) {
var entity = this.el;
var scene = entity.sceneEl;
var event = this.data.event;
var type = this.data.type;
var input1 = this.data.input1;
var input2 = this.data.input2;
// Detect the gate type and check for the corresponding condition
switch (type) {
case 'and' :
if((this.active1 && this.active2) && !this.activeOut) {
entity.emit(event, {}, true);
this.activeOut = true;
}
else if(this.activeOut && !(this.active1 && this.active2)) {
entity.emit(event, {}, true);
this.activeOut = false;
}
break;
case 'or' :
if((this.active1 || this.active2) && !this.activeOut) {
entity.emit(event, {}, true);
this.activeOut = true;
}
else if(this.activeOut && !(this.active1 || this.active2)) {
entity.emit(event, {}, true);
this.activeOut = false;
}
break;
case 'xor' :
if(((this.active1 || this.active2) && !(this.active1 && this.active2)) && !this.activeOut) {
entity.emit(event, {}, true);
this.activeOut = true;
}
else if(this.activeOut && !((this.active1 || this.active2) && !(this.active1 && this.active2))) {
entity.emit(event, {}, true);
this.activeOut = false;
}
break;
case 'nand' :
if(!(this.active1 && this.active2) && !this.activeOut) {
entity.emit(event, {}, true);
this.activeOut = true;
}
else if(this.activeOut && (this.active1 && this.active2)) {
entity.emit(event, {}, true);
this.activeOut = false;
}
break;
case 'nor' :
if(!(this.active1 || this.active2) && !this.activeOut) {
entity.emit(event, {}, true);
this.activeOut = true;
}
else if(this.activeOut && (this.active1 || this.active2)) {
entity.emit(event, {}, true);
this.activeOut = false;
}
break;
case 'xnor' :
if(!((this.active1 || this.active2) && !(this.active1 && this.active2)) && !this.activeOut) {
entity.emit(event, {}, true);
this.activeOut = true;
}
else if(this.activeOut && ((this.active1 || this.active2) && !(this.active1 && this.active2))) {
entity.emit(event, {}, true);
this.activeOut = false;
}
break;
}
}
});
</script>
And here's the gate in HTML (including the door and buttons incase it matters):
<a-box
color="blue"
door="to: 5 3 -5; toggleEvent: open;"
position="0 3 -5"
width="5"
height="6"
depth="0.06"
>
<a-box
logic="type: and; input1: button; input2: button2; event: open;"
position="0 0 0"
width="0.0001"
height="0.0001"
depth="0.0001"
>
<a-box
color="blue"
position="4 1 -2"
button="eventOn: button; eventOff: button;"
></a-box>
<a-box
color="blue"
button="eventOn: button2; eventOff: button2;"
position="4 3 -2"
></a-box>
</a-box>
</a-box>
The formatting in the code is due to how it was pasted in here.
event listener functions have this
set to the element on which the listener is placed:
this.active1 = false; // "this" refers to the component
entity.addEventListener(input1, function (event) {
if(!this.active1) {
this.active1 = true; // "this" refers to "entity"
} else {
this.active1 = false; // "this" refers to entity"
}
});
so You're thinking it's the same 'variable' when actually you're setting a new property of the entity
.
Two common ways of solving this are :
// assing `this` to another variable, which is used in the listener
const self = this;
entity.addEventListener(input1, function (event) {
self.active1 = false; // this is the components active1 property
})
// use an arrow function, which does not have it's own scope
entity.addEventListener(input1, event => {
this.active1 = false; // this is the components active1 property
})
Some remarks:
console.log("and")
inside. Not triggering? console.log(this.active1, this.active2)
before the condition. Always false
? console.log(this.active1)
inside the event listeners.<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
<script src="https://unpkg.com/aframe-event-set-component@4.2.1/dist/aframe-event-set-component.min.js"></script>
<script>
AFRAME.registerComponent('logic', {
schema: {
type: { type: 'string', default: 'and' },
input1: { type: 'string' },
input2: { type: 'string' },
event: { type: 'string' }
},
init: function () {
var entity = this.el;
var scene = entity.sceneEl;
var event = this.data.event;
this.activeOut = false;
this.active1 = false;
this.active2 = false;
var type = this.data.type;
var input1 = this.data.input1;
var input2 = this.data.input2;
// Toggle each input
entity.addEventListener(input1, (event) => {
// toggle betweeen true / false
this.active1 = !this.active1 ? true : false
console.log(input1, "triggered. active1: ", this.active1)
this.checkState() // check if the gate is open or closed
});
entity.addEventListener(input2, (event) => {
// toggle betweeen true / false
this.active2 = !this.active2 ? true : false
console.log(input2, "triggered. active1: ", this.active2)
this.checkState() // check if the gate is open or closed
});
},
checkState: function () {
var entity = this.el;
var scene = entity.sceneEl;
var event = this.data.event;
var type = this.data.type;
var input1 = this.data.input1;
var input2 = this.data.input2;
// Detect the gate type and check for the corresponding condition
console.log("checkState: active1/2", this.active1, this.active2)
switch (type) {
case 'and':
if ((this.active1 && this.active2) && !this.activeOut) {
console.log("and fullfiled")
entity.emit(event, {}, true);
this.activeOut = true;
} else if (this.activeOut && !(this.active1 && this.active2)) {
entity.emit(event, {}, true);
this.activeOut = false;
}
break;
}
}
});
// set text open/closed depending on the 'open' events from the entity with the logic
AFRAME.registerComponent("door", {
init: function () {
const gate = document.querySelector("[logic]")
var toggle = false;
gate.addEventListener("open", evt => {
toggle = !toggle
this.el.setAttribute("text", "value", toggle ? "open" : "closed")
})
}
})
// not sure where this came from, simple toggle between to events on click
AFRAME.registerComponent("button", {
schema: {
eventOn: { type: "string" },
eventOff: { type: "string" }
},
init: function () {
var toggle = false;
this.el.addEventListener("click", evt => {
if (!toggle) {
this.el.emit(this.data.eventOn)
this.el.setAttribute("color", "red")
} else {
this.el.emit(this.data.eventOff)
this.el.setAttribute("color", "blue")
}
toggle = !toggle
})
}
})
</script>
<a-scene cursor="rayOrigin: mouse" raycaster="objects: a-box">
<a-box logic="type: and; input1: button; input2: button2; event: open;" width="0.0001"
height="0.0001" depth="0.0001">
<a-box color="blue" position="1 2 -2" button="eventOn: button; eventOff: button;"></a-box>
<a-box color="blue" button="eventOn: button2; eventOff: button2;" position="-1 2 -2"></a-box>
</a-box>
<a-entity door position="0.45 1.8 -0.275" text="color:black; value: closed"></a-entity>
<a-sky color="#ECECEC"></a-sky>
<a-entity position="0 1.6 0" camera>
</a-scene>