AFRAME.registerComponent("wasd-to-analog2d", { schema: { analog2dOutputAction: { default: "wasd_analog2d" } }, init: function() { this.output = [0, 0]; this.vectors = { w: [0, 1], a: [-1, 0], s: [0, -1], d: [1, 0] }; this.keys = {}; this.onWasd = this.onWasd.bind(this); this.move = this.move.bind(this); this.onBlur = this.onBlur.bind(this); }, play: function() { const eventNames = ["w_down", "w_up", "a_down", "a_up", "s_down", "s_up", "d_down", "d_up"]; for (const name of eventNames) { this.el.sceneEl.addEventListener(name, this.onWasd); } // I listen to events that this component generates instead of emitting "move" // directly because ideally this would live as an input mapping, but the events // generated by this component won't actually get mapped. this.el.sceneEl.addEventListener(this.data.analog2dOutputAction, this.move); window.addEventListener("blur", this.onBlur); }, pause: function() { this.el.sceneEl.removeEventListener("wasd", this.onWasd); this.el.sceneEl.removeEventListener(this.data.analog2dOutputAction, this.move); window.removeEventListener("blur", this.onBlur); this.keys = {}; }, onBlur: function() { this.keys = {}; }, move: function(event) { this.el.emit("move", { axis: event.detail.axis }); }, onWasd: function(event) { const keyEvent = event.type; const down = keyEvent.indexOf("down") !== -1; const key = keyEvent[0].toLowerCase(); this.keys[key] = down; }, tick: function() { this.target = [0, 0]; for (const key in this.keys) { if (this.keys[key] && this.vectors[key]) { this.target = [this.target[0] + this.vectors[key][0], this.target[1] + this.vectors[key][1]]; } } const targetMagnitude = Math.sqrt(this.target[0] * this.target[0] + this.target[1] * this.target[1]); if (targetMagnitude !== 0) { this.target[0] = this.target[0] / targetMagnitude; this.target[1] = this.target[1] / targetMagnitude; } const epsilon = 0.01; if ( Math.abs(this.output[0]) < epsilon && Math.abs(this.output[1]) < epsilon && this.target[0] === 0 && this.target[1] === 0 ) { return; // Staying at [0,0] doesn't require new events. } const easeInSpeed = 0.25; this.output = [ this.output[0] + easeInSpeed * (this.target[0] - this.output[0]), this.output[1] + easeInSpeed * (this.target[1] - this.output[1]) ]; if (this.output !== [0, 0]) { this.el.emit(this.data.analog2dOutputAction, { axis: this.output }); } } });