CalmUI
Unreal Engine

Use Assets in Unreal

Import images and fonts, save the package, and check missing game assets.

  1. Open Assets → Project Assets in Studio and click Import.
  2. Select or drop the files and review their names.
  3. Confirm the import and place the asset in the View source.
  4. Save with Unreal connected.
  5. Wait for package synchronization, then check the View in the game.

Studio inserts a name such as asset://game-ui/logo. That name must match an entry in Assets. PNG and JPG images are prepared as Unreal UI textures, while project SVGs are included inside the CalmUI package. See Assets for supported file types and insertion steps.

You can also map a project asset name to an asset already in Unreal by assigning its Content Browser path. Packaged games use the prepared data and included Unreal assets, not files elsewhere on your computer.

Live updates while testing

While a game session is connected, editing assets/manifest.json reaches it right away, without a save or a package rebuild: a changed image or font backed by a local file sends its new bytes, and a changed Content Browser mapping (hostAssetPath), video entry or host surface sends only its mapping details, since no file bytes travel for those. Removing an entry tells the session to drop its mapping. A video entry needs hostAssetPath for this live update to reach a connected session; without it, the entry's file is only picked up on the next package import.

This live mapping is session-only. Importing (or saving with Unreal connected) is still what builds the mapping into the .cuipack, so a Content Browser or video mapping added only through live editing is lost the next time the package is rebuilt until you import again. Unreal → Connection in Studio shows whether recent manifest changes reached the connected session or still need that rebuild. See Asset changes and hot reload.

First-time import of a new local image

Importing a manifest image that has never been imported before (a brand-new .png/.jpg/.jpeg under path) completes asynchronously: Unreal creates the destination texture through its Interchange import pipeline, and CalmUI's host-texture settings (uncompressed, no mip chain, UI LOD group, never streamed) are applied once that import reports done rather than immediately after it starts. Re-importing an asset the editor already knows about, and font imports, are unaffected and stay synchronous. The headless -run=CalmUiPackageImport commandlet (used by pnpm test:r9 and CI) retries up to three times with a short delay before it fails a first-time image import, so a slow first import does not fail the package build; it logs which attempt the import settled on.

Advanced: keep host images bounded

C++ hosts that change UCalmUiPackageAsset::HostAssets after loading the package can call UCalmUiLocalPlayerSubsystem::RefreshCalmUiHostAssets() to republish the mappings. This does not reload the package or reset screens and focus.

Runtime host images use cooked data from an ordinary, non-virtual Texture2D. CalmUI currently converts BGRA8, RGBA8, and BC3/DXT5 platform mips to its shared RGBA paint resource; it does not depend on Editor-only source art or a GPU readback. Set maximumDecodedExtent on a host-image manifest entry, or MaximumDecodedExtent on its C++ mapping, for an icon-sized asset. The runtime chooses the smallest suitable cooked source mip and caps the registered image's largest dimension to that value without upscaling. This also keeps a one-mip UI texture bounded instead of retaining its full-resolution RGBA output.

Unsupported, virtual, missing, unreadable, or truncated texture data rejects the new resource generation with a diagnostic. The previously working resources remain active instead of exposing a partially updated View.

Play a video asset

A video manifest entry needs one more field for the game: hostAssetPath, the Content Browser path of a UMediaSource asset (typically a FileMediaSource pointing at a file under Content/Movies). If you leave hostAssetPath out, CalmUI opens the entry's path file directly instead, resolved against the manifest's own folder.

CalmUI never decodes the clip. It is a host-external resource: the game owns the texture, and CalmUI only draws whatever the last submitted frame references: use the asset:// address exactly like an image (background-image: url(asset://...) or <img src="asset://..." />); there is no video element or video CSS property.

Playback itself is the optional CalmUiMedia Runtime module. While the address is referenced by a visible View's last submitted frame, it plays the clip muted at unscaled real time; the first frame is decoded paused ahead of time so nothing flashes blank. Losing the reference (the screen closes, the element is hidden) pauses playback without rewinding, and the last frame stays on screen. Removing the mapping releases the player and its texture. Because CalmUiRenderer and CalmUiRuntime never depend on this module, a project that disables it simply draws nothing for video host assets and logs one warning instead of failing to build or load.

H.264 MP4 is the reference codec. HEVC needs platform codecs that are not guaranteed to be present, and Studio's own preview cannot play it, so prefer H.264 for a clip you also check in Studio Preview. See Packaging for how to stage the movie file for a packaged build.

Bind a render target as a surface

Surface bindings, opacity overrides and video playback belong to their Unreal world. Separate PIE worlds can use the same asset address without replacing another world's texture or playback references. World teardown releases only that world's bindings.

A surface entry shows a texture your game draws. Declare it in the manifest with its size, then hand the texture to CalmUI when the screen that shows it opens. The two nodes live on the Local Player subsystem:

  • Set CalmUI External Surface takes the surface address and a texture, usually a Texture Render Target 2D, and returns whether the address is a surface mapped by a loaded package. It logs a warning and returns false otherwise. Pass an empty texture to clear the binding.
  • Set CalmUI External Surface Opaque takes the surface address and a boolean and overrides the manifest opaque until the world is torn down. It returns false, with a warning, when the address is not a mapped surface.
  • Get CalmUI External Surface Status returns whether the address is mapped, whether a visible screen currently paints it (Referenced), whether a texture is bound (Bound), the effective opacity (Opaque) and the declared size.
if (UCalmUiLocalPlayerSubsystem* Calm = LocalPlayer->GetSubsystem<UCalmUiLocalPlayerSubsystem>())
{
    Calm->SetCalmUiExternalSurface(TEXT("asset://game-ui/island-map"), MapRenderTarget);

    FCalmUiExternalSurfaceStatus Status;
    if (Calm->GetCalmUiExternalSurfaceStatus(TEXT("asset://game-ui/island-map"), Status) && Status.bReferenced)
        UpdateMapCapture(); // draw into the render target only while the UI shows it
}

CalmUI samples the render target directly, so your game owns the update rate and nothing is copied. Referenced turns on as soon as a visible screen paints the address and turns off after two frames and 250 ms without one, so a relayout does not make the game stop drawing; use it to pause an expensive scene capture while the map is closed. The colour is right whichever format the render target has: RTF_RGBA8 (what a scene capture writes), RTF_RGBA8_SRGB and float render targets all show the same colour as an ordinary image of the same pixels.

A surface is drawn opaque by default. Unreal's material and canvas draws (DrawMaterialToRenderTarget) leave a render target's alpha at 0, and CalmUI would composite that as fully transparent, so it forces the texture's alpha to 1; only a clear leaves alpha at 1. Declare "opaque": false in the manifest, or call Set CalmUI External Surface Opaque with false before binding, for a render target whose alpha you control: the real alpha is then composited. The element's own CSS opacity, border radius and clip apply in both modes.

Bindings belong to the running world. They are released when the world is torn down, so bind again each time the game opens the screen. When the entry has a hostAssetPath, that texture is shown whenever nothing is bound, and a binding set by the game wins over it. A hostAssetPath that is not a texture marks the surface failed and draws nothing for it.

Surfaces do not need the optional CalmUiMedia module: the renderer resolves them itself. The binding is process-wide rather than per Local Player, so two local players that show the same address show the same texture.

If an asset is missing

Check Problems for the asset name and reason. Confirm that the source exists, its type is supported, and the package synchronized successfully. One image that cannot be prepared is reported without hiding the rest of the View. An invalid SVG edit keeps the previous working package active.

Inter is already included, including in packaged games. Import another font only if your design or language needs it. See Text and fonts.

On this page