Appearance
InterfaceHost
InterfaceHost mounts an authored .rsx interface on a scene GameObject. Use it for a normal screen HUD, a purchase pad painted onto the floor, a sign in the 3D world, or a name and status display that follows a moving character.
One InterfaceHost mounts one complete RSX tree. That tree can contain many elements—text, images, progress bars, buttons, and nested components. Use multiple InterfaceHost GameObjects when parts of the interface need independent transforms, visibility, or lifecycles.
Quick Setup
- Create or select a GameObject in the scene.
- Add the
InterfaceHostcomponent in the Inspector. - Set Source to the
.rsxinterface file. - Choose Screen or World in Space.
- If you choose World, the Inspector adds
Transform3Dwhen the GameObject does not already have one. - Position the interface with the normal Transform3D position, rotation, and scale fields. World interfaces use X, Y, and Z like every other 3D GameObject.
Every InterfaceHost RSX source must import a paired .rstyle file. This small purchase-pad pair is a complete starting point:
rsx
// PurchasePad.rsx
import styles from "./PurchasePad.rstyle"
class PurchasePadView extends UIComponent {
render {
<View automationId="purchasePad" accessibilityLabel="Purchase funding" style={styles.pad}>
<View automationId="purchasePadProgress" style={styles.progress} />
<View style={styles.costRow}>
<View accessibilityLabel="Money" style={styles.moneyBill} />
<Text automationId="purchasePadPrice" accessibilityLabel="Remaining purchase price" style={styles.price}>100</Text>
</View>
</View>
}
}rstyle
// PurchasePad.rstyle
extends StyleSet
export default StyleSet.create({
pad: {
width: 360,
height: 200,
alignItems: "center",
justifyContent: "center",
overflow: "hidden",
backgroundColor: "#1639238A",
borderRadius: 28
},
progress: {
position: "absolute",
left: 0,
top: 0,
width: 0,
height: "100%",
backgroundColor: "#22C55EB8"
},
costRow: { flexDirection: "row", alignItems: "center", gap: 18 },
moneyBill: { width: 76, height: 48, backgroundColor: "#22B65EFF", borderRadius: 9 },
price: { color: "#FEF3C7FF", fontSize: 60, fontWeight: "900" }
})Save both files together, then choose PurchasePad.rsx in Source. A missing or invalid style import prevents the surface from mounting and reports onLoadFailed.
Keep the pad focused on the transaction: one money symbol, the remaining cost, and the funding fill. Put instructions such as “Open the restaurant” in a separate transient Screen InterfaceHost so the objective can animate or disappear without changing the world pad.
There is no second InterfaceHost transform. This keeps parenting predictable: a child InterfaceHost follows the full position, rotation, and scale chain of its parent GameObjects.
In UI Builder, enable Scene (uiBuilder.viewport.showScene) to preview the active scene behind the RSX surface and judge its real world placement.
Screen And World Space
| Behavior | Screen | World |
|---|---|---|
| Placement | Screen overlay | GameObject Transform3D |
| Parent transform | Not used for screen placement | Full parent chain |
| Scene depth | Always above the 3D scene | Controlled by Depth Test |
| Scene shadows | Never | Optional Receive Shadows |
| UISurface pointer input | Supported | Camera-ray projection with bounds, face, distance, and depth checks |
screen is the default. Screen hosts use normal UISurface input and ignore world-only rendering settings.
world renders the complete RSX surface as one transparent rectangle in the 3D scene. The rectangle can lie on a floor or wall, stand as a sign, or move as a child of another GameObject.
Activation And Visibility
enabled controls the InterfaceHost lifecycle. Disabling the host GameObject—or any ancestor—makes the host inactive. ReGame removes it from rendering, UI lookup, and accessibility publication immediately, then emits onUnmounted while releasing the retained surface on the next frame. Enabling the hierarchy again creates a fresh mount and emits the normal mount and readiness events again.
hidden is a visual choice, not a lifecycle choice. A hidden host skips rendering but remains mounted, continues processing, and keeps its retained element handles. Use hidden for short visual pauses. Use enabled = false when an inactive purchase zone, HUD, or world panel should stop participating in UI systems entirely.
Inside an onUnmounted callback, the outgoing surface is still queryable so cleanup code can inspect it or release retained handles. Treat those handles as invalid as soon as the callback returns.
Properties
| Inspector label | Property | Default | Used by |
|---|---|---|---|
| Source | sourceRef (editor) | Empty | Screen and World |
| Space | mountSpace | screen | Screen and World |
| Surface Size | surfaceSize | 512 x 256 | World |
| Pixels Per Unit | pixelsPerUnit | 100 | World |
| Depth Test | depthTest | On | World |
| Receive Shadows | receiveShadows | Off | World |
| Double Sided | doubleSided | On | World |
| Depth Bias | depthBias | 0.0005 | World |
World Size
surfaceSize is the RSX layout and render-target size in pixels. pixelsPerUnit converts that size into 3D units:
text
world width = surfaceSize.x / pixelsPerUnit
world height = surfaceSize.y / pixelsPerUnitAt the defaults, a 512 x 256 surface at 100 pixels per unit occupies 5.12 x 2.56 world units before Transform3D scale is applied.
Increase surfaceSize when the RSX layout needs more logical room or texture detail. Change pixelsPerUnit when the same pixel layout should occupy a different physical size. Very large render targets cost more GPU memory and rendering work.
Each surfaceSize axis and pixelsPerUnit is clamped to the supported 1…4096 range.
Depth Test
Depth Test is on by default. When enabled, ordinary 3D geometry can appear in front of the world interface. Turn it off only when the interface must remain visible through scene objects.
World InterfaceHosts test the existing scene depth but do not write their transparent rectangle into the depth buffer. This avoids one translucent interface incorrectly hiding another one drawn later.
Receive Shadows
Enable Receive Shadows when shadows from scene geometry should darken the visible InterfaceHost artwork. This is the option that makes a character, worker, or prop shadow appear across a money icon or purchase pad instead of continuing underneath it unchanged.
Shadow receiving is opt-in and only affects World hosts. It requires an active shadow-producing DirectionalLight3D and scene geometry whose renderer has shadow casting enabled. The InterfaceHost remains otherwise unlit, so its authored colors stay unchanged outside the shadow.
Receive Shadows does not mean Cast Shadows. The checkbox lets other objects' shadows fall onto the interface. The transparent InterfaceHost rectangle does not cast its own shadow onto the floor. Screen hosts never participate in scene shadows.
Double Sided
When Double Sided is on, both faces of the world rectangle render. Turn it off for a floor decal, wall panel, or sign that should only be visible from its front. If a one-sided interface disappears, rotate its Transform3D so its front faces the camera or temporarily enable Double Sided while positioning it.
Depth Bias
depthBias moves only the depth comparison slightly toward the camera. Use it to stop flicker when a world InterfaceHost is almost coplanar with a floor or wall.
First place the interface a tiny physical distance above the surface with Transform3D. Then use the smallest depth bias that removes the remaining z-fighting. Depth Bias is not a replacement for normal X, Y, and Z placement.
depthBias is clamped to 0…0.05.
Floor Purchase Pad
A floor purchase pad normally combines visual UI and gameplay collision:
- Create a GameObject for the purchase zone.
- Add
Transform3D,InterfaceHost,Area3D, and the purchase-zone RGScript component. - Set InterfaceHost Space to World and choose the pad
.rsxsource. - Rotate Transform3D around X by about
-90degrees so the front of the RSX surface faces upward. - Move it a small distance above the floor.
- Keep Depth Test on.
- Turn Receive Shadows on so workers, players, and props can shadow the money artwork.
- Usually turn Double Sided off and keep a small Depth Bias only if needed.
- Size and position the
Area3Dshape over the same purchase zone. - Give the player GameObject the
playertag when using the filtered collision example below.
For hold-to-fund purchases, update the remaining price and grow a clipped fill from left to right while the player stays inside the area. Keep partial funding in the authoritative gameplay state when the player leaves; the InterfaceHost only presents that state.
World UISurface controls receive the same mouse, touch, and pen events as Screen hosts after the active 3D camera ray is projected into the surface's pixel coordinates. Use Area3D when the interaction is based on player occupancy rather than a pointer:
rgscript
class PurchasePadController extends Component {
let area: Area3D
function _ready() {
area = getComponent<Area3D>()
area.onCollide("player", (other: GameObject) => {
// Check the player's money and unlock the zone here.
let price: Text = UI.text("purchasePadPrice")
if (price != null) {
price.text = "OPEN"
}
})
}
}An Area3D participates only while its full GameObject hierarchy is enabled and unpaused. hidden and visible control presentation only; they do not disable overlap detection. To deactivate a sensor, disable the area GameObject or an ancestor. Disabling ends current overlaps once through onCollideEnd; enabling the hierarchy again allows a new onCollide when an eligible body overlaps. On a new overlap, onCollide runs before that frame's first onCollideUpdate. This makes it safe to keep purchase and service triggers under an initially disabled buildable-station interaction branch while their visible pads live in a separately animated presentation branch.
The onCollide subscription uses ReGame's normal owner-bound event cleanup.
Moving Overhead Interface
To make a name, health bar, order icon, or speech label follow a moving character:
- Create a child GameObject under the character.
- Add
Transform3DandInterfaceHostto the child. - Set Space to World.
- Use the child's local Transform3D position to offset the interface above the character.
- Keep Depth Test on so walls and nearby objects can occlude it naturally.
- Leave Receive Shadows off when maximum label readability is more important, or enable it when the label should feel physically present in the scene.
The child follows the parent's full transform automatically. Moving a parent does not rerender unchanged RSX content; only the world rectangle's transform changes.
World InterfaceHosts do not automatically face the camera. Rotate the child from gameplay code if a billboard-style overhead display is required.
Events
InterfaceHost exposes lifecycle and presentation changes through the shared GameObject event lane:
| Event | Meaning |
|---|---|
onMounted | The retained UISurface is built, laid out, registered, and ready to query. |
onUnmounted | The active surface is about to be released. The outgoing surface remains queryable during this callback only. |
onMountSpaceChanged | mountSpace changed between Screen and World. |
onLayout | The retained RSX tree completed layout. |
onGeometryChanged | A successful mount/rebuild completed, or surfaceSize/pixelsPerUnit changed world geometry. |
onRenderSettingsChanged | Depth Test, Receive Shadows, Double Sided, or Depth Bias changed. |
onRenderTargetReady | The current World surface texture is ready. |
onRenderTargetInvalidated | The current World surface texture is no longer authoritative. |
onLoadFailed | Source loading, RSX building, or render-target creation failed. |
State changes before the matching event fires, so a callback can immediately read the new value:
rgscript
class PadPresentation extends Component {
let host: InterfaceHost
function _ready() {
host = getComponent<InterfaceHost>()
host.onRenderSettingsChanged(() => {
console.log("Receive Shadows: " + host.receiveShadows)
})
}
}Subscriptions return an EventController. Keep the controller when you need to pause or cancel a long-lived subscription. GameObject event subscriptions survive a surface rebuild or unmount; they end when explicitly cancelled, when the subscribing script is cleaned up, or when the event target is destroyed.
During initial scene loading, ReGame builds active InterfaceHost surfaces before script _ready() so the mounted UI is available when _ready() begins. An initially disabled host does not mount until its hierarchy becomes enabled. Query an active mounted surface directly from _ready(). Subscribe there for later rebuild, layout, geometry, unmount, and failure events; the initial onMounted, onLayout, onGeometryChanged, and onLoadFailed notifications may already have occurred.
Platform And Input Notes
- Desktop and Android World hosts use the shared 3D OpenGL/GLES renderer, including depth testing and optional directional-shadow receiving.
- Native iOS currently supports Screen hosts through its 2D Metal renderer. World hosts require the future native 3D Metal camera/depth/shadow path and are not rendered there yet. For a cross-platform floor pad today, compose a lightweight MeshSource/MeshRenderer fallback and switch between it and the World host from
onRenderTargetReady/onRenderTargetInvalidated; keep exactly one presentation visible at a time. - Screen hosts support the shared mouse, touch, and pen input path.
- World hosts use the active perspective or orthographic Camera3D for ray-projected UISurface input. Pointer capture continues on the same host until Up or Cancel.
- Input priority is Screen UISurface, then nearest eligible World UISurface, then 3D physics picking. See Pointer Input And Joystick.
- World hosts are transparent 3D rectangles. They can receive supported scene shadows but do not cast an InterfaceHost-shaped shadow.
Troubleshooting
| Problem | Check |
|---|---|
| World interface is invisible | Confirm Source is valid, rotate its front toward the camera, or enable Double Sided while positioning. |
| Interface is behind an object | This is expected with Depth Test on; move it or turn Depth Test off only if it must show through geometry. |
| Interface flickers against a floor or wall | Move it slightly off the surface, then add a small Depth Bias. |
| Interface is the wrong physical size | Check both Surface Size and Pixels Per Unit, then Transform3D scale. |
| Shadow still passes underneath the artwork | Confirm Space is World, Receive Shadows is on, a DirectionalLight3D is active, and the covering mesh casts shadows. |
| Overhead interface does not follow | Make the InterfaceHost GameObject a child of the moving object and use a local Transform3D offset. |
| World button does not click | Use Area3D or a 3D raycast for interaction; direct World UISurface pointer projection is not implemented yet. |
