diff --git a/src/components/axis-dpad.js b/src/components/axis-dpad.js index 90fb29176013384f42d3c2da02631a1f0a88c634..853e1569714a87ec0da79dea7fa423cabd721b36 100644 --- a/src/components/axis-dpad.js +++ b/src/components/axis-dpad.js @@ -70,7 +70,8 @@ AFRAME.registerComponent("axis-dpad", { ? "center" : angleToDirection(Math.atan2(x, y)); - this.el.emit(`dpad${direction}${state}`); + const hand = e.detail.target.id === "left-hand" ? "left" : "right"; + this.el.emit(`${hand}dpad${direction}${state}`); if (state === "down") { this.lastDirection = direction; diff --git a/src/components/character-controller.js b/src/components/character-controller.js index 71f69c20861fb827cf7207565d5cc50e63224457..141b056c39827e751b3b7a6c51f260208628a3a2 100644 --- a/src/components/character-controller.js +++ b/src/components/character-controller.js @@ -16,6 +16,7 @@ AFRAME.registerComponent("character-controller", { this.velocity = new THREE.Vector3(0, 0, 0); this.accelerationInput = new THREE.Vector3(0, 0, 0); this.onStopMoving = this.onStopMoving.bind(this); + this.onMove = this.onMove.bind(this); this.onTranslateX = this.onTranslateX.bind(this); this.onTranslateY = this.onTranslateY.bind(this); this.onTranslateZ = this.onTranslateZ.bind(this); @@ -48,6 +49,7 @@ AFRAME.registerComponent("character-controller", { play: function() { const eventSrc = this.el.sceneEl; + eventSrc.addEventListener("move", this.onMove); eventSrc.addEventListener("stop_moving", this.onStopMoving); eventSrc.addEventListener("translateX", this.onTranslateX); eventSrc.addEventListener("translateY", this.onTranslateY); @@ -78,6 +80,7 @@ AFRAME.registerComponent("character-controller", { pause: function() { const eventSrc = this.el.sceneEl; + eventSrc.removeEventListener("move", this.onMove); eventSrc.removeEventListener("stop_moving", this.onStopMoving); eventSrc.removeEventListener("translateX", this.onTranslateX); eventSrc.removeEventListener("translateY", this.onTranslateY); @@ -110,6 +113,11 @@ AFRAME.registerComponent("character-controller", { ); }, + onMove: function(event) { + const axes = event.detail.axis; + this.accelerationInput.set(axes[0], 0, axes[1]); + }, + onStopMoving: function(event) { this.accelerationInput.set(0, 0, 0); }, @@ -282,5 +290,9 @@ AFRAME.registerComponent("character-controller", { const dvz = data.groundAcc * dt * -this.accelerationInput.z * this.boost; velocity.x += dvx; velocity.z += dvz; + + const decay = 0.9; + this.accelerationInput.x = this.accelerationInput.x * decay; + this.accelerationInput.z = this.accelerationInput.z * decay; } }); diff --git a/src/components/split-axis-events.js b/src/components/split-axis-events.js index 1ac81e8cea90c41dd1d83d465661d9f161d093a2..40c3bf4dfb0d4788295326b929021203035c7c33 100644 --- a/src/components/split-axis-events.js +++ b/src/components/split-axis-events.js @@ -1,3 +1,262 @@ +const angleTo4Direction = function(angle) { + angle = (angle * THREE.Math.RAD2DEG + 180 + 45) % 360; + if (angle > 0 && angle < 90) { + return "north"; + } else if (angle >= 90 && angle < 180) { + return "west"; + } else if (angle >= 180 && angle < 270) { + return "south"; + } else { + return "east"; + } +}; + +const angleTo8Direction = function(angle) { + angle = (angle * THREE.Math.RAD2DEG + 180 + 45) % 360; + var direction = ""; + if ((angle >= 0 && angle < 120) || angle >= 330) { + direction += "north"; + } + if (angle >= 150 && angle < 300) { + direction += "south"; + } + if (angle >= 60 && angle < 210) { + direction += "west"; + } + if ((angle >= 240 && angle < 360) || angle < 30) { + direction += "east"; + } + return direction; +}; + +AFRAME.registerComponent("dpad-as-axes", { + schema: { + inputName: { default: "dpad" }, + name: { default: "dpad_axes" } + }, + + init: function() { + this.mapping = [ + { + direction: "north", + axes: [0, 1] + }, + { + direction: "northeast", + axes: [1, 1] + }, + { + direction: "east", + axes: [1, 0] + }, + { + direction: "southeast", + axes: [1, -1] + }, + { + direction: "south", + axes: [0, -1] + }, + { + direction: "southwest", + axes: [-1, -1] + }, + { + direction: "west", + axes: [-1, 0] + }, + { + direction: "northwest", + axes: [-1, 1] + } + ]; + this.handlers = []; + }, + + play: function() { + var inputName = this.data.inputName; + for (var pair of this.mapping) { + this.handlers[pair.direction] = this.emitAxes(pair.axes).bind(this); + this.el.addEventListener( + `${inputName}_${pair.direction}`, + this.handlers[pair.direction] + ); + } + }, + + pause: function() { + var inputName = this.data.inputName; + for (var pair of this.mapping) { + this.el.removeEventListener( + `${inputName}_${pair.direction}`, + this.handlers[pair.direction] + ); + } + }, + + emitAxes: function(axes) { + const name = this.data.name; + const inputName = this.data.inputName; + const el = this.el; + return function(event) { + event.target.emit(name, { axis: [axes[0], axes[1]] }); + }; + } +}); + +AFRAME.registerComponent("wasd-dpad", { + schema: { + north: { default: "w" }, + east: { default: "d" }, + south: { default: "s" }, + west: { default: "a" } + }, + + init: function() { + this.onKeyPress = this.onKeyPress.bind(this); + this.onKeyUp = this.onKeyUp.bind(this); + this.keys = {}; + }, + + play: function() { + window.addEventListener("keypress", this.onKeyPress); + window.addEventListener("keyup", this.onKeyUp); + }, + + pause: function() { + window.remove("keypress", this.onKeyPress); + window.remove("keyup", this.onKeyUp); + }, + + tick: function(t, dt) { + const { north, east, south, west } = this.data; + var direction = ""; + direction += this.keys[north] ? "north" : ""; + direction += this.keys[south] ? "south" : ""; + direction += this.keys[east] ? "east" : ""; + direction += this.keys[west] ? "west" : ""; + if (direction !== "") { + this.el.emit(`dpad_${direction}`); + } + }, + + onKeyPress: function(event) { + const { north, east, south, west } = this.data; + for (var dir of [north, south, east, west]) { + if (event.key === dir) { + this.keys[dir] = true; + } + } + }, + + onKeyUp: function(event) { + const { north, east, south, west } = this.data; + for (var dir of [north, south, east, west]) { + if (event.key === dir) { + this.keys[dir] = false; + } + } + } +}); + +AFRAME.registerComponent("oculus-touch-controls-extended", { + schema: { + hand: { default: "left" }, + dpad: { default: false }, + dpad_deadzone: { default: 0.85 }, + dpad_livezone: { default: 0.35 }, + dpad_directions: { default: 4 }, // one of [4, 8] + dpad_turbo: { default: false }, + dpad_haptic_intensity: { default: "low" } // one of ["none", "low", "mid", "high"] + }, + + init: function() { + this.axisToDpad = this.axisToDpad.bind(this); + this.dpadCanFire = true; + }, + + update: function(old) { + if (old.dpad && !this.data.dpad) { + this.el.removeEventListener("axismove", this.axisToDpad); + } + if (!old.dpad && this.data.dpad) { + this.el.addEventListener("axismove", this.axisToDpad); + } + }, + + axisToDpad: function(event) { + var x = event.detail.axis[0]; + var y = event.detail.axis[1]; + var deadzone = this.data.dpad_deadzone; + var turbo = this.data.dpad_turbo; + var livezone = this.data.dpad_livezone; + var directions = this.data.dpad_directions; + var haptic_intensity = this.data.dpad_haptic_intensity; + var hand = this.data.hand; + + event.target.emit(`${hand}_axismove`, { + axis: [event.detail.axis[0], -event.detail.axis[1]] + }); + + if (!turbo && Math.abs(x) < livezone && Math.abs(y) < livezone) { + this.dpadCanFire = true; + } + if (!this.dpadCanFire) return; + + x = Math.abs(x) < deadzone ? 0 : x; + y = Math.abs(y) < deadzone ? 0 : y; + if (x == 0 && y == 0) return; + var angle = Math.atan2(x, y); + var direction = + directions === 4 ? angleTo4Direction(angle) : angleTo8Direction(angle); + + event.target.emit(`${hand}_dpad_${direction}`); + event.target.emit(`${hand}_haptic_pulse`, { intensity: haptic_intensity }); // TODO: Catch these events an make the controller rumble. + if (!turbo) { + this.dpadCanFire = false; + } + } +}); + +AFRAME.registerComponent("haptic-feedback", { + schema: { + hand: { default: "left" } + }, + + init: function() { + this.pulse = this.pulse.bind(this); + this.tryGetActuator = this.tryGetActuator.bind(this); + this.tryGetActuator(); + }, + + tryGetActuator() { + var trackedControls = this.el.components["tracked-controls"]; + if (trackedControls && trackedControls.controller) { + this.actuator = trackedControls.controller.hapticActuators[0]; + } else { + setTimeout(this.tryGetActuator, 1000); + } + }, + + play: function() { + this.el.addEventListener(`${this.data.hand}-haptic-pulse`, this.pulse); + }, + pause: function() { + this.el.removeEventListener(`${this.data.hand}-haptic-pulse`, this.pulse); + }, + + pulse: function(event) { + let { strength, duration, intensity } = event.detail; + if (intensity === "low") { + strength = 0.3; + duration = 30; + } + if (intensity === "none") return; + + this.actuator.pulse(strength, duration); + } +}); + AFRAME.registerComponent("split-axis-events", { init: function() { this.pressed = false; @@ -16,9 +275,15 @@ AFRAME.registerComponent("split-axis-events", { }, onAxisMove: function(event) { - var name = "touchpad" + (this.pressed ? "pressed" : "") + "axismove"; - this.el.emit(name + "x", { value: event.detail.axis[0] }); - this.el.emit(name + "y", { value: event.detail.axis[1] }); + var hand = "right"; + this.el.emit( + `${hand}_touchpad${this.pressed ? "_pressed" : ""}_axismove_x`, + { value: event.detail.axis[0] } + ); + this.el.emit( + `${hand}_touchpad${this.pressed ? "_pressed" : ""}_axismove_y`, + { value: event.detail.axis[1] } + ); }, onButtonChanged: function(event) { diff --git a/src/input-mappings.js b/src/input-mappings.js index d57ff6496b1d35bf0a435b06cfdb74dd807008e6..08125d5259ff97888c5511ce8ac40c57009beab7 100644 --- a/src/input-mappings.js +++ b/src/input-mappings.js @@ -4,16 +4,16 @@ export default function registerInputMappings() { default: { common: { // @TODO these dpad events are emmited by an axis-dpad component. This should probalby move into either tracked-controller or input-mapping - dpadleftdown: "action_snap_rotate_left", - dpadrightdown: "action_snap_rotate_right", - dpadcenterdown: "action_teleport_down", // @TODO once once #30 lands in aframe-teleport controls this just maps to "action_teleport_aim" - dpadcenterup: "action_teleport_up", // @TODO once once #30 lands in aframe-teleport controls this just maps to "action_teleport_teleport" - touchpadpressedaxismovex: "translateX", - touchpadpressedaxismovey: "translateZ", - touchpadbuttonup: "stop_moving" }, "vive-controls": { - menudown: "action_mute" + menudown: "action_mute", + left_touchpad_pressed_axismove_x: "translateX", + left_touchpad_pressed_axismove_y: "translateZ", + touchpadbuttonup: "stop_moving", + rightdpadleftdown: "action_snap_rotate_left", + rightdpadrightdown: "action_snap_rotate_right", + rightdpadcenterdown: "action_teleport_down", // @TODO once once #30 lands in aframe-teleport controls this just maps to "action_teleport_aim" + rightdpadcenterup: "action_teleport_up" // @TODO once once #30 lands in aframe-teleport controls this just maps to "action_teleport_teleport" }, "oculus-touch-controls": { xbuttondown: "action_mute", @@ -22,7 +22,10 @@ export default function registerInputMappings() { thumbsticktouchstart: "thumb_down", thumbsticktouchend: "thumb_up", triggerdown: "index_down", - triggerup: "index_up" + triggerup: "index_up", + left_axismove: "move", + right_dpad_east: "action_snap_rotate_right", + right_dpad_west: "action_snap_rotate_left" }, daydream: { menudown: "action_mute" @@ -39,7 +42,8 @@ export default function registerInputMappings() { s_down: "action_move_backward", s_up: "action_dont_move_backward", d_down: "action_move_right", - d_up: "action_dont_move_right" + d_up: "action_dont_move_right", + dpad_axes: "move" // Why is the character controller not able to receive this one? } } } diff --git a/templates/room.hbs b/templates/room.hbs index add5dff049b1731e33645724d2d848777e26d36f..7385b1aea2c4eeccb9141192c6424f8f597b7008 100644 --- a/templates/room.hbs +++ b/templates/room.hbs @@ -122,6 +122,8 @@ networked spawn-controller="radius: 4;" character-controller="pivot: #head" + wasd-dpad + dpad-as-axes="inputName:dpad; name:dpad_axes" > <a-sphere scale="0.1 0.1 0.1"></a-sphere> @@ -146,9 +148,11 @@ <a-entity id="left-hand" - split-axis-events hand-controls2="left" - axis-dpad="centerZone: 1" + tracked-controls + haptic-feedback + oculus-touch-controls-extended="dpad: true; dpad_deadzone: 0.8; dpad_livezone: 0.3; dpad_directions: 8; dpad_turbo: true" + dpad-as-axes="inputName:left_dpad; name:left_dpad_axes" teleport-controls="cameraRig: #player-rig; teleportOrigin: #head; button: action_teleport_" networked="template: #left-hand-template;" > @@ -165,7 +169,7 @@ <a-entity id="right-hand" hand-controls2="right" - axis-dpad + oculus-touch-controls-extended="hand:right; dpad: true; dpad_livezone: 0.3; dpad_deadzone: 0.8; dpad_directions: 4; dpad_turbo: false" teleport-controls="cameraRig: #player-rig; teleportOrigin: #head; hitEntity: #telepor-indicator;