Tutorial: Using getScreenPosition to Overlay UI at a World Position

The getScreenPositionmethod (from the ViewController service in Hology) converts a 3D world position (a Vector3) into 2D screen coordinates. This is useful for overlaying UI elements (like markers, health bars, or tooltips) on top of objects in your 3D scene.

import { ViewController } from '@hology/core/gameplay';
import { useRenderLateUpdate, useService } from '@hology/react';
import { useRef } from 'react';
import { Vector3 } from 'three';

function WorldPositionMarker() {
  const markerRef = useRef<HTMLDivElement>(null);
  const view = useService(ViewController);

  useRenderLateUpdate(() => {
    // Replace (0,0,0) with any world position you want to track
    const pos = view.getScreenPosition(new Vector3(0, 0, 0));
    if (pos && markerRef.current) {
      markerRef.current.style.visibility = 'visible';
      markerRef.current.style.left = pos.x + 'px';
      markerRef.current.style.top = pos.y + 'px';
    } else if (markerRef.current) {
      markerRef.current.style.visibility = 'hidden';
    }
  });

  return (
    <div
      ref={markerRef}
      style={{
        position: 'absolute',
        width: '20px',
        height: '20px',
        background: 'red',
        borderRadius: '50%',
        pointerEvents: 'none',
        transform: 'translate(-50%, -50%)',
      }}
    />
  );
}

How it Works

  • The component uses useService(ViewController) to access the camera and projection logic.

  • On every frame (useRenderLateUpdate), it calls getScreenPositionwith a 3D world coordinate.

  • If the position is visible, it updates the marker's CSS to move it to the correct screen position.

  • If the position is off-screen or not visible, it hides the marker.

Customization

  • To track a different world position, change the argument to getScreenPosition.

  • You can style the marker as needed (color, size, etc.).

  • You can render multiple markers for different world positions by reusing this component.

Last updated