Using React

React is a popular library that enables you to create UI components that easily can be reused. Hology Engine provides a library specifically to help integrating React components with the game play state and actors.

Using the Hology Scene component

To run Hology Engine within a react application, you can simply use the HologyScene component.

<HologyScene gameClass={Game} sceneName='demo' dataDir='data' shaders={shaders} actors={actors}>  
</HologyScene>

This will start the Hology Engine runtime with a given scene. You also need to pass in some parameters for Hology Engine as these may differ depending on your your projects is set up. If you are using the default react project template when you create a new Hology project, then the values shown in the code above should work.

  • sceneName - The name of the scene you want to load

  • dataDir - A path to the folder where all Hology game data exists.

  • shaders - An object with shader classes

  • actors - An object with actor classes

Adding UI components

Inside the HologyScene component, you can put components that should be displayed on top of the rendered game.

function App() {
    return (
      <HologyScene gameClass={Game} sceneName='demo' dataDir='data' shaders={shaders} actors={actors}>
        <p>This will be displayed over the game</p>     
      </HologyScene>
    )
}

Required CSS

To properly handle pointer events when using the HologyScene component, the following CSS needs to be added in your project. It will ensure that pointer events can be captured on the canvas which is used by the Pointer Events service while also capturing events on your own HTML elements added as children of the component.

.hology-overlay {
  pointer-events: none
}
.hology-overlay * {
  pointer-events: auto; 
}

Hology React hooks

Components used within the HologyScene component can use hooks like useService as seen below to access any of your game play services within this instance of the game.


function PlayerHealthDisplay() {
  const playerState = useService(PlayerState)
  return <h1>{playerState.health.value}</h1>
}

function App() {
    return (
      <HologyScene gameClass={Game} sceneName='demo' dataDir='data' shaders={shaders} actors={actors}>
        <PlayerHealthDisplay></PlayerHealthDisplay>   
      </HologyScene>
    )
}
  • useService - Get the instance of any service in your game play code. This can be useful for services that hold some global game play state that you want to display.

  • useActorQuery - Query actors currently added to the game world. Whenever an actor that matches the query is added or removed from the world, the component will re-render.

  • useRenderUpdate - Execute a given function before every frame.

  • useRenderLaterUpdate - Execute a given function before every frame but after other update functions.

Updating the UI when state changes

React is meant to only re-render when something changes which is great for a performant user interface but it needs to know when something changes. If you want to display some value from your game play code in the user interface, your can read the value by accessing it from a service or an actor but you need to let React somehow know that the value has changed.

One way to manage this is by using signals. Signals is a concept that is becoming more popular in web front end development that lets you represent state in a way that consumers of that state can know when the value of the signal changes. When you create a new Hology Engine project and selects React as the UI framework, it will automatically also install a library called @preact/signals-react.

Let's say you have a service in your game play code that looks like below. We use the signal function when defining a property.

import { Service } from '@hology/core/gameplay'
import { signal } from "@preact/signals-react"

@Service()
class PlayerState {
  health = signal(100)
}

export default PlayerState

We can now access this health signal in our React components like below. First we use the useService hook to access the instance of the PlayerState class. The value of the health signal can be accessed by using the .value property.

function PlayerHealthDisplay() {
  const playerState = useService(PlayerState)
  return <h1>{playerState.health.value}</h1>
}

We can later update the value of the signal by assigning a new value to the value property. In the below example we define a method that updates the health signal based on a given damage amount.

import { Service } from '@hology/core/gameplay'
import { signal } from "@preact/signals-react"

@Service()
class PlayerState {
  health = signal(100)
  
  takeDamage(damage: number) {
    this.health.value = Math.max(0, this.health.value - damage)
  }
}

export default PlayerState

Whenever the signal is updated, the react component will automatically re-render.

Last updated