Use Assets in Unreal
Import images and fonts, save the package, and check missing game assets.
- Open Assets → Project Assets in Studio and click Import.
- Select or drop the files and review their names.
- Confirm the import and place the asset in the View source.
- Save with Unreal connected.
- 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
opaqueuntil 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.