Screens, values, events, and actions
0.57.0-dev · beta source documentation. Native, Blueprint, Editor and rendering examples are source-reviewed, not executed in Unreal during this scan. See validation and limits.
Prerequisite: the first UI is configured, and you have the exact ULocalPlayer or local PlayerController. FQuartzUI and UQuartzUIBlueprintLibrary are the high-level gameplay facade. They handle the shell and bridge; gameplay does not construct Chromium objects.
Change screen and input together
FQuartzUIInputConfig Input;
Input.InputMode = EQuartzUIInputMode::UIOnly;
Input.MouseCaptureMode = EMouseCaptureMode::NoCapture;
const bool bShown = FQuartzUI::ShowScreen(Player, TEXT("menu.pause"), Input);
The native call publishes screen intent; your frontend must render the intended screen. It does not navigate to another HTML file, mount a React route automatically, or author the menu for you. Subscribe to quartzui.shell, whose screen field contains that intent. Call ApplyInputPolicy when only input ownership changes. Handle false results and use the failure nodes instead of assuming a call created a shell.
State is a snapshot; an event is a moment
SetBoolValue, SetIntegerValue, SetFloatValue, SetTextValue, and SetNameValue publish a named object with a value member. PublishStruct copies a data-only Unreal struct; PublishJsonState accepts bounded object JSON. Latest state survives packaged navigation and is coalesced by name. EmitStructEvent, EmitJsonEvent, and ShowNotification are transient.
FQuartzUI::SetTextValue(Player, TEXT("hud.location"), NSLOCTEXT("Game", "Garage", "Garage"));
FQuartzUI::EmitJsonEvent(Player, TEXT("race.started"), TEXT("{}"));
FQuartzUI::ShowNotification(Player, NSLOCTEXT("Game", "Saved", "Saved"), TEXT("save.complete"));
On the page:
const stopLocation = client.subscribeState('hud.location', value => {
if (typeof value.value === 'string') locationElement.textContent = value.value;
});
const stopRace = client.subscribeEvent('race.started', () => showStartAnimation());
// When this screen/component leaves:
stopLocation();
stopRace();
locationElement and showStartAnimation are project UI objects. Strictly validate domain payloads; the transport validates envelopes, not your game's semantics. State subscriptions replay retained data immediately; event subscriptions do not. Do not use an event for health that a newly mounted component must display.
After shell creation but before Ready, state coalesces and events queue within the existing bounds: 128 state names, 64 queued events, 256 KiB UTF-8 envelopes. Suspension pauses delivery; it does not make buffers unbounded. Check publication results and avoid frame-by-frame unchanged data.
Let a button request an allowed action
Register a native handler for an explicit project name:
FQuartzUI::RegisterNativeAction(Player, TEXT("menu.continue"),
FQuartzUINativeNamedActionHandler::CreateLambda([](FName ActionName)
{
// Invoke your authoritative resume operation here.
}));
// On owner teardown:
FQuartzUI::UnregisterAction(Player, TEXT("menu.continue"));
Use weak UObject delegate bindings for real gameplay owners; do not capture an actor raw pointer in a long-lived lambda. Registration authorizes a handler. SetActions separately supplies the visible action-bar catalog (FQuartzUIBoundAction: name, localized label, priority, enabled/display flags, hold duration). A visible label alone does not register gameplay authority.
try {
await client.request('quartzui.action', { action: 'menu.continue' });
} catch (error) {
showActionError(error.code ?? error.message);
}
This generic request exists in the shipped transport. Convenience methods such as client.invokeAction() are not exported here. Both named-action registrations and catalogs are capped at 64. quartzui.* is reserved for middleware; game names use bounded lowercase names such as inventory.drop.
React components
The app supplies React 18+ and React DOM, a lockfile, and a production bundle. Copy/bundle react.mjs and react-store.mjs beside the transport. Create one client and one React root per document, outside component renders.
import { useQuartzUIState } from './quartzui/react.mjs';
function Ammo({ client }) {
const state = useQuartzUIState(client, 'hud.ammo');
return <output>{Number.isInteger(state?.value) ? state.value : '—'}</output>;
}
The hook uses a stable immutable retained snapshot. createStateStore(client, name) is available for advanced external-store integration. Effects that subscribe to transient events must return their unsubscribe function. Unmount the root and subscriptions before disposing the document client. Keep drafts and focus stable during unrelated snapshots, and verify Strict Mode setup/cleanup/setup in the host frontend.
Do not ship bare react imports, JSX, or a dev-server dependency in the game. CSS/DOM markup, roving focus, action-bar rendering, and domain decoders remain project work.
Smooth an authoritative timer
PresentationClock from presentation-clock.mjs interpolates between native samples. Call sample(seconds, phase, finished, nowMs) on each update, and read(nowMs) in the timer's animation loop. Only Running interpolates; pause/reset/finish snap. It holds at the latest sample when updates stop and introduces up to one sample interval of presentation delay. Cancel the animation loop on unmount; never send its display estimate back as simulation time.
All native fragments are unexecuted during this scan. Facade source (source: Source/QuartzUIRuntime/Public/QuartzUIBlueprintLibrary.h), original gameplay guide (source: Documentation/SimpleGameUI.md), React adapter (source: Resources/Client/react.mjs).