3

I want to keep a score or a health bar always visible in a react-vr app.

I could use VrHeadModel - rotation, yawPitchRoll and position, but having to calculate it just to keep it fixed... seems like I am missing something.

How do I do this?

Frazer Kirkman
  • 1,003
  • 1
  • 14
  • 23
  • Pasting code from this link's client.js and index.js files into mine worked for me: https://github.com/facebook/react-360/tree/master/Samples/HeadlockedSurfaces – mattferrin Nov 27 '18 at 16:36

2 Answers2

2

Updated gist which has less delay as it's subscribed to the HM:

https://gist.github.com/cidicles/b4e978d3f3e2de8b359bdc51b5fb3261


This is how I am currently doing this. It has visual lag and sets state in a loop but achieves the goal.

Create a state for the VrheadModel.rotation() array

constructor(props) {
  super(props);
  this.state = {
    hmRot: VrHeadModel.rotation()
  }
}

Start a timer

componentDidMount(){
  this.timer = setInterval(()=>{this.tick()}, 50);
}
componentWillUnmount(){
  clearInterval(this.timer);
}
tick(){
  this.setState({
    hmRot: VrHeadModel.rotation()
  });
}

Create a view at 0/0/0 and position your fixed object in the scene as you normally world. Set the rotation on the master view to match the rotation of the camera.

  render(){
    let {hmRot} = this.state;
    return (
      <View
        style={{
          position: 'absolute',
          layoutOrigin: [0.5, 0.5],
          transform: [
            {translate: [0, 0, 0]},
            {rotateX: hmRot[0]},
            {rotateY: hmRot[1]},
            {rotateZ: hmRot[2]}
          ]
        }}>
        <Text
          style={{
            position: 'absolute',
            layoutOrigin: [0.5, 0.5],
            backgroundColor: '#f00',
            transform: [
              {translate: [0, 0, -2]},
            ]
          }}>
          Fixed
        </Text>
      </View>
    );
  }

There is a relevant post around this issue from the React VR team here: https://github.com/facebook/react-vr/issues/261

cidicles
  • 351
  • 2
  • 8
  • thanks, it works pretty well, except when the user looks up really high, or at the ground - then my score (which is at the top of the screen) drifts above the screen – Frazer Kirkman Aug 29 '17 at 14:15
  • when I try to adjust for that when rotationX is to high or low, it works, except for when looking behind, then the score moves all over the screen – Frazer Kirkman Aug 29 '17 at 14:20
  • any idea why the rotation goes off the screen when looking up or down? – Frazer Kirkman Aug 30 '17 at 08:21
  • I've taken a look at this and could not reproduce what you are seeing. I've posted a full gist of my component here: https://gist.github.com/cidicles/e90611844e9993d7c1337bb3c8afb23e – cidicles Aug 30 '17 at 17:14
  • 1
    Added a new gist for you on the post which may solve any issues you were having. – cidicles Dec 13 '17 at 15:42
1

The answer is to use multiple surfaces: one that isn't headlocked and another that is headlocked.

See "Using Multiple Surfaces" here: Surfaces - React 360

To get yours working, paste the missing code from the example into your client.js and index.js files respectively:

Here is a link to a working example (partially pasted below): Headlocked Surfaces

import { Math as VRMath, ReactInstance, Surface } from "react-360-web";

function init(bundle, parent, options = {}) {
  const horizontalPanel = new Surface(300, 300, Surface.SurfaceShape.Flat);
  const hvPanel = new Surface(300, 300, Surface.SurfaceShape.Flat);

  horizontalPanel.setAngle(0, -0.5);

  const cameraDirection = [0, 0, -1];

  const r360 = new ReactInstance(bundle, parent, {
    fullScreen: true,
    frame: () => {
      const cameraQuat = r360.getCameraQuaternion();
      cameraDirection[0] = 0;
      cameraDirection[1] = 0;
      cameraDirection[2] = -1;
      // cameraDirection will point out from the view of the camera,
      // we can use it to compute surface angles
      VRMath.rotateByQuaternion(cameraDirection, cameraQuat);
      const cx = cameraDirection[0];
      const cy = cameraDirection[1];
      const cz = cameraDirection[2];
      const horizAngle = Math.atan2(cx, -cz);
      const vertAngle = Math.asin(cy / Math.sqrt(cx * cx + cy * cy + cz * cz));
      horizontalPanel.setAngle(horizAngle, -0.5);
      hvPanel.setAngle(horizAngle, vertAngle);
    },
    ...options
  });

  r360.renderToSurface(r360.createRoot("HorizontalPanel"), horizontalPanel);
  r360.renderToSurface(r360.createRoot("HVPanel"), hvPanel);
}

window.React360 = { init };

In your client.js:

Inside the frame property of the ReactInstance constructor, set the surface angle to match the camera: either horizontally, or both horizontally and vertically.

In your index.js:

Remember to register a React component to the new headlocked surface.

mattferrin
  • 607
  • 2
  • 8
  • 19