Adding Custom UI with React
awe provides built-in React support for creating in-world UI elements. You can render React components in any script (Behavior, Component, etc.) using a UI.createRenderer()
.
Basic Example
import { ScriptBehavior, UI } from '@oo/scripting'
function UiElement() {
return (
<div style={{
position: "fixed",
backgroundColor: "white",
top: "20px",
left: "50%",
transform: "translateX(-50%)",
}}>
Hello, World
</div>
)
}
export default class ExampleBehavior extends ScriptBehavior {
private renderer = UI.createRenderer();
onReady = () => {
this.renderer.render(<UiElement/>);
}
onDispose = () => {
this.renderer.unmount();
}
}
UI.createRenderer()
returns a renderer that can mount a single React element.- Mount more elements by creating additional renderers, if needed.
Making the UI Reactive
Option 1: Manual Re-render
Keep track of your state (e.g., count
) and call render()
whenever it changes:
import { ScriptBehavior, UI } from '@oo/scripting'
export default class ManualRender extends ScriptBehavior {
private renderer = UI.createRenderer();
private count = 0;
onReady = () => this.renderUI();
renderUI = () => {
this.renderer.render(
<div style={{ position: "fixed", backgroundColor: "white", top: 20, left: "50%", transform: "translateX(-50%)" }}>
<div>Count: {this.count}</div>
<button onClick={this.increment}>Increment</button>
</div>
);
}
increment = () => {
this.count++;
this.renderUI();
}
onDispose = () => this.renderer.unmount();
}
Option 2: Automatic with a Store
Use awe’s Store
and useStore
hooks for automatic re-renders:
import { ScriptBehavior, UI, Store, useStore } from '@oo/scripting'
const store = new Store({ count: 0 });
function Counter() {
const { count } = useStore(store);
return (
<div style={{ position: "fixed", backgroundColor: "white", top: 20, left: "50%", transform: "translateX(-50%)" }}>
<div>Count: {count}</div>
<button onClick={() => store.update({ count: count+1 })}>Increment</button>
</div>
)
}
export default class AutoRender extends ScriptBehavior {
private renderer = UI.createRenderer();
onReady = () => {
this.renderer.render(<Counter/>);
}
onDispose = () => this.renderer.unmount();
}
Sharing the Store
You can export the store from one script and import it in others to keep data in sync across multiple UI components.
Pointer Events
If your UI is purely visual (not interactive), disable pointer events so users can still lock the mouse in the scene:
<div style={{ pointerEvents: "none", ... }}>
{/* Non-interactive UI */}
</div>
Summary
- Create a Renderer with
UI.createRenderer()
, thenrender()
your React component. - Control State:
- Manual: keep track of changes, call
render()
again. - Store: let React auto-update with
useStore()
.
- Manual: keep track of changes, call
- Pointer Events: set
pointerEvents: "none"
for non-clickable UI. - Multiple UI Elements: create multiple renderers or combine components within one renderer.
With these steps, you can build dynamic in-world UIs in awe using React. Enjoy crafting engaging interfaces for your players!