From a38eecceeb2e882fa5bdd175bd098f2b140d5676 Mon Sep 17 00:00:00 2001 From: netpro2k <netpro2k@gmail.com> Date: Tue, 17 Apr 2018 18:42:45 -0700 Subject: [PATCH] Add freeze mode toggle to the 2d hud and prep in world hud for toggle --- src/App.js | 3 +++ src/assets/stylesheets/2d-hud.css | 8 +++++- src/components/freeze-controller.js | 27 +++++++++++++++++++ src/components/hud-controller.js | 41 ++++++++++++++++++----------- src/hub.html | 1 + src/hub.js | 6 +++-- src/react-components/2d-hud.js | 8 +++--- src/react-components/ui-root.js | 18 ++++++++++--- src/storage/store.js | 1 + src/utils/identity.js | 1 + 10 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 src/components/freeze-controller.js diff --git a/src/App.js b/src/App.js index c542f439f..b814e080d 100644 --- a/src/App.js +++ b/src/App.js @@ -1,7 +1,10 @@ +import Store from "./storage/store"; + export class App { constructor() { this.scene = null; this.quality = "low"; + this.store = new Store(); } setQuality(quality) { diff --git a/src/assets/stylesheets/2d-hud.css b/src/assets/stylesheets/2d-hud.css index a50436114..d360067e0 100644 --- a/src/assets/stylesheets/2d-hud.css +++ b/src/assets/stylesheets/2d-hud.css @@ -39,6 +39,13 @@ align-items: center; justify-content: center; z-index: 10; + border-radius: 50%; + border: 2px solid white; + cursor: pointer; +} + +:local(.modeButton.frozen) { + border-color: red; } :local(.panel) { @@ -65,7 +72,6 @@ background-size: 100%; background-image: url(../hud/avatar.png); } - :local(.mic) { display: flex; width: 32px; diff --git a/src/components/freeze-controller.js b/src/components/freeze-controller.js new file mode 100644 index 000000000..77e66f91f --- /dev/null +++ b/src/components/freeze-controller.js @@ -0,0 +1,27 @@ +AFRAME.registerComponent("freeze-controller", { + schema: { + toggleEvent: { type: "string" } + }, + + init: function() { + this.onToggle = this.onToggle.bind(this); + }, + + play: function() { + this.el.addEventListener(this.data.toggleEvent, this.onToggle); + }, + + pause: function() { + this.el.removeEventListener(this.data.toggleEvent, this.onToggle); + }, + + onToggle: function() { + window.APP.store.update({ profile: { has_found_freeze: true } }); + NAF.connection.adapter.toggleFreeze(); + if (NAF.connection.adapter.frozen) { + this.el.addState("frozen"); + } else { + this.el.removeState("frozen"); + } + } +}); diff --git a/src/components/hud-controller.js b/src/components/hud-controller.js index 4ee274f34..d2439b376 100644 --- a/src/components/hud-controller.js +++ b/src/components/hud-controller.js @@ -15,7 +15,8 @@ AFRAME.registerComponent("hud-controller", { offset: { default: 0.7 }, // distance from hud above head, lookCutoff: { default: 20 }, // angle at which the hud should be "on", animRange: { default: 30 }, // degrees over which to animate the hud into view - yawCutoff: { default: 50 } // yaw degrees at wich the hud should reoirent even if the user is looking up + yawCutoff: { default: 50 }, // yaw degrees at wich the hud should reoirent even if the user is looking up + showTip: { type: "bool" } }, init() { this.isYLocked = false; @@ -33,14 +34,34 @@ AFRAME.registerComponent("hud-controller", { const head = this.data.head.object3D; const sceneEl = this.el.sceneEl; - const { offset, lookCutoff, animRange, yawCutoff } = this.data; + const { offset, lookCutoff, animRange, yawCutoff, showTip } = this.data; const pitch = head.rotation.x * THREE.Math.RAD2DEG; const yawDif = deltaAngle(head.rotation.y, hud.rotation.y) * THREE.Math.RAD2DEG; - // Reorient the hud only if the user is looking away from the hud, for right now this arbitrarily means the hud is 1/3 way animated away + // animate the hud into place over animRange degrees as the user aproaches the lookCutoff angle + let t = 1 - THREE.Math.clamp(lookCutoff - pitch, 0, animRange) / animRange; + + // HUD is locked down while showing tooltip + if (showTip) { + t = 1; + } + + // Once the HUD is in place it should stay in place until you look sufficiently far down + if (t === 1) { + this.lockedHeadPositionY = head.position.y; + this.hudLocked = true; + } else if (this.hudLocked && pitch < lookCutoff - animRange / 2) { + this.hudLocked = false; + } + + if (this.hudLocked) { + t = 1; + } + + // Reorient the hud only if the user is looking away from the hud, for right now this arbitrarily means the hud is 1/2 way animated away // TODO: come up with better huristics for this that maybe account for the user turning away from the hud "too far", also animate the position so that it doesnt just snap. - if (yawDif >= yawCutoff || pitch < lookCutoff - animRange / 3) { + if (yawDif >= yawCutoff || pitch < lookCutoff - animRange / 2) { const lookDir = new THREE.Vector3(0, 0, -1); lookDir.applyQuaternion(head.quaternion); lookDir.add(head.position); @@ -48,18 +69,6 @@ AFRAME.registerComponent("hud-controller", { hud.position.z = lookDir.z; hud.setRotationFromEuler(new THREE.Euler(0, head.rotation.y, 0)); } - - // animate the hud into place over animRange degrees as the user aproaches the lookCutoff angle - const t = 1 - THREE.Math.clamp(lookCutoff - pitch, 0, animRange) / animRange; - - // Lock the hud in place relative to a known head position so it doesn't bob up and down - // with the user's head - if (!this.isYLocked && t === 1) { - this.lockedHeadPositionY = head.position.y; - } - const EPSILON = 0.001; - this.isYLocked = t > 1 - EPSILON; - hud.position.y = (this.isYLocked ? this.lockedHeadPositionY : head.position.y) + offset + (1 - t) * offset; hud.rotation.x = (1 - t) * THREE.Math.DEG2RAD * 90; diff --git a/src/hub.html b/src/hub.html index edb7395d4..973461f27 100644 --- a/src/hub.html +++ b/src/hub.html @@ -22,6 +22,7 @@ networked-scene="adapter: janus; audio: true; debug: true; connectOnLoad: false;" physics mute-mic="eventSrc: a-scene; toggleEvents: action_mute" + freeze-controller="toggleEvent: action_freeze" personal-space-bubble="debug: false;" app-mode-input-mappings="modes: default, hud; actionSets: default, hud;" diff --git a/src/hub.js b/src/hub.js index 75111f9c6..34d281282 100644 --- a/src/hub.js +++ b/src/hub.js @@ -49,6 +49,7 @@ import "./components/hand-poses"; import "./components/gltf-model-plus"; import "./components/gltf-bundle"; import "./components/hud-controller"; +import "./components/freeze-controller"; import ReactDOM from "react-dom"; import React from "react"; @@ -63,6 +64,7 @@ import "./gltf-component-mappings"; import { App } from "./App"; window.APP = new App(); +const store = window.APP.store; const qs = queryString.parse(location.search); const isMobile = AFRAME.utils.device.isMobile(); @@ -88,7 +90,6 @@ import "./components/nav-mesh-helper"; import registerNetworkSchemas from "./network-schemas"; import { inGameActions, config as inputConfig } from "./input-mappings"; import registerTelemetry from "./telemetry"; -import Store from "./storage/store"; import { generateDefaultProfile, generateRandomName } from "./utils/identity.js"; import { getAvailableVREntryTypes } from "./utils/vr-caps-detect.js"; @@ -108,7 +109,6 @@ AFRAME.registerInputActivator("pressedmove", PressedMove); AFRAME.registerInputActivator("reverseY", ReverseY); AFRAME.registerInputMappings(inputConfig, true); -const store = new Store(); const concurrentLoadDetector = new ConcurrentLoadDetector(); const hubChannel = new HubChannel(store); @@ -135,6 +135,8 @@ function applyProfileFromStore(playerRig) { displayName, avatarSrc: "#" + (store.state.profile.avatar_id || "botdefault") }); + const hudController = playerRig.querySelector("[hud-controller]"); + hudController.setAttribute("hud-controller", { showTip: !store.state.profile.has_found_freeze }); document.querySelector("a-scene").emit("username-changed", { username: displayName }); } diff --git a/src/react-components/2d-hud.js b/src/react-components/2d-hud.js index 9679bb9c0..8d35fada2 100644 --- a/src/react-components/2d-hud.js +++ b/src/react-components/2d-hud.js @@ -4,12 +4,12 @@ import cx from "classnames"; import styles from "../assets/stylesheets/2d-hud.css"; -const TwoDHUD = ({ muted, onToggleMute }) => ( +const TwoDHUD = ({ muted, frozen, onToggleMute, onToggleFreeze }) => ( <div className={styles.container}> <div className={cx("ui-interactive", styles.panel, styles.left)}> <div className={cx(styles.mic, { [styles.muted]: muted })} onClick={onToggleMute} /> </div> - <div className={cx("ui-interactive", styles.modeButton)}> + <div className={cx("ui-interactive", styles.modeButton, { [styles.frozen]: frozen })} onClick={onToggleFreeze}> <div className={styles.avatar} /> </div> <div className={cx("ui-interactive", styles.panel, styles.right)}> @@ -20,7 +20,9 @@ const TwoDHUD = ({ muted, onToggleMute }) => ( TwoDHUD.propTypes = { muted: PropTypes.bool, - onToggleMute: PropTypes.func + frozen: PropTypes.bool, + onToggleMute: PropTypes.func, + onToggleFreeze: PropTypes.func }; export default TwoDHUD; diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index 3e22208da..bbf19e97e 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -90,6 +90,9 @@ class UIRoot extends Component { autoExitTimerInterval: null, secondsRemainingBeforeAutoExit: Infinity, + muted: false, + frozen: false, + exited: false, showProfileEntry: false @@ -121,9 +124,9 @@ class UIRoot extends Component { // TODO: mute state should probably actually just live in react land onAframeStateChanged = e => { - if (e.detail !== "muted") return; + if (!(e.detail === "muted" || e.detail === "frozen")) return; this.setState({ - muted: this.props.scene.is("muted") + [e.detail]: this.props.scene.is(e.detail) }); }; @@ -131,6 +134,10 @@ class UIRoot extends Component { this.props.scene.emit("action_mute"); }; + toggleFreeze = () => { + this.props.scene.emit("action_freeze"); + }; + handleForcedVREntryType = () => { if (!this.props.forcedVREntryType) return; @@ -758,7 +765,12 @@ class UIRoot extends Component { )} </div> {this.state.entryStep === ENTRY_STEPS.finished ? ( - <TwoDHUD muted={this.state.muted} onToggleMute={this.toggleMute} /> + <TwoDHUD + muted={this.state.muted} + frozen={this.state.frozen} + onToggleMute={this.toggleMute} + onToggleFreeze={this.toggleFreeze} + /> ) : null} </div> </IntlProvider> diff --git a/src/storage/store.js b/src/storage/store.js index 4351ebeda..b117e1849 100644 --- a/src/storage/store.js +++ b/src/storage/store.js @@ -19,6 +19,7 @@ export const SCHEMA = { properties: { has_agreed_to_terms: { type: "boolean" }, has_changed_name: { type: "boolean" }, + has_found_freeze: { type: "boolean" }, display_name: { type: "string", pattern: "^[A-Za-z0-9-]{3,32}$" }, avatar_id: { type: "string" } } diff --git a/src/utils/identity.js b/src/utils/identity.js index db78b027e..4cce7fa37 100644 --- a/src/utils/identity.js +++ b/src/utils/identity.js @@ -103,6 +103,7 @@ export function generateDefaultProfile() { return { has_agreed_to_terms: false, has_changed_name: false, + has_found_freeze: false, avatar_id: selectRandom(avatarIds) }; } -- GitLab