Load assets and instantiate scenes
Load project resources through app.resources() and create scene instances through
app.scenes(). These services let game code use project paths in both the editor and
a packaged runtime.
Prepare project resources
Put the files in the project's resources/ folder. The editor gives each an ID in
.eggy/assets.zon; keep that file in version control with the assets and use the
editor's file operations when moving or renaming them, so the IDs follow. See
assets, scenes, and packaging for how IDs and
packaged files relate to project paths.
Synchronous loading is useful during setup. Use owned asynchronous requests for loads that would otherwise stall gameplay. Browser exports read their preloaded packaged data.
Create files in Assets
Use New in the Assets toolbar, or New > in the right-click menu. Both offer Scene and Folder, and the rest by category, each opening beside it: Rendering (Material, Surface Shader, Post Shader), 2D (Tilemap), Animation (Animation Clip), UI (UI Scene) and Input (Input Map), along with file types declared by the game's plugins. Write code and other text files in your code editor. New files get unique names and can be undone. Right-click a folder to create inside it; right-click a file to create beside it.
Double-click .inmap.zon assets to open the Input Map editor.
Images, models, audio, and fonts are imported from existing files, not created as
empty placeholders. Plugins can add their own templates.
Drop files from the system's file manager onto Assets to copy them into the target folder.
Place models and scenes
Drag a model from Assets into the Scene to create an entity where it lands, or onto a Hierarchy row to parent it there. Drop a model onto a Mesh shape field to replace its shape. Drop an image onto a Mesh texture field, a Scene entity, or its Hierarchy row to assign a texture.
A scene file dropped into Scene/Hierarchy creates a SceneInstance root. Only that root
is saved in the containing scene; generated descendants belong to the source scene.
Clicking an instance selects its root. The Hierarchy lists its generated descendants
under it, tinted, so you can tune that one instance;
open the source scene to change what it is made of. The SceneInstance's scene field also
accepts a scene-file drop.
Hierarchy Add creates an empty entity; Delete removes it and its descendants. Drag onto a row to reparent, or onto its top/bottom edge to reorder siblings. Scenes preserve that order.
Reference project files
const picture = eggy.AssetRef(.image).init("resources/art/portrait.png");
const model = eggy.AssetRef(.model).init("resources/models/ship.glb");
init validates a literal at compile time. Use try AssetRef(kind).parse(path) for
runtime text. Paths are project-relative, use /, and fit in 256 bytes. Absolute paths,
traversal, empty segments, backslashes, and a file of the wrong kind are rejected.
An empty path means an unset reference.
A reference stores its path by value and loads nothing. Inspector slots accept matching
dragged files. An audio reference plays through an AudioSource or
app.audio().
Read bytes, images, or models
const notes = eggy.AssetRef(.text).init("resources/notes.txt");
const text = try app.resources().read(gpa, notes);
defer gpa.free(text);
var image = try app.resources().loadImage(gpa, picture);
defer image.deinit(gpa);
var geometry = try app.resources().loadModel(gpa, model);
defer geometry.deinit(gpa);
Free the sentinel-terminated bytes with the allocator passed to read. Release images
and models with deinit, as above. Image pixels are RGBA bytes, top row first; models
contain geometry, parts, textures, and the rig (bones and blend shapes) of an animated
model. These are CPU data. To display a model,
use a Mesh component.
read is for resources copied unchanged, such as text, scenes, and fonts.
It rejects converted models/images with ConvertedResource; use the typed loader instead.
Image loading supports formats decoded by the existing image importer, including PNG,
JPEG, GIF, and BMP. SVG and WebP classification does not imply texture decoding:
loadImage rejects them with UnsupportedImageFormat.
Missing/corrupt/unsupported resources return errors, not empty successful data.
Common errors include UnsetAsset, FileNotFound, NotPackaged, InvalidAssetData,
OutOfMemory, and AssetLoadFailed. Provider diagnostics explain other failures.
Load without blocking gameplay
Store the returned request in your plugin and poll it on later updates:
const request = try app.resources().loadModelAsync(gpa, model);
// On later updates, while this request is pending:
if (try app.resources().poll(request)) |loaded| {
var result = loaded;
defer result.deinit();
// result.value.model is owned CPU data; upload GPU resources on the render thread.
}
readAsync reads copied files; loadImageAsync decodes pixels. progress reports
queued, loading, cancelling, or complete. Poll consumes completed requests, including
errors, exactly once; using a consumed handle returns InvalidResourceRequest.
Results remain independently owned after the request or world is gone.
Keep the supplied allocator alive until completion or world reset. Request bookkeeping
and result allocation happen on the caller thread; native imports use private worker
arenas. At most eight outstanding requests are allowed, including completed results not
yet polled. Additional requests return TooManyResourceRequests.
cancel(request) is cooperative and does not wait: an in-progress import finishes,
discards its output, then polling returns ResourceCancelled. Reset/reload/shutdown joins
workers before unloading code or closing providers. Browser builds do not create threads;
polling decodes already-preloaded data on the main thread, not a background network fetch.
Instantiate a scene
const rat_scene = eggy.AssetRef(.scene).init("resources/scene/rat.scene.zon");
const root = try app.scenes().instantiate(rat_scene, .{ .parent = parent });
Choose an existing parent entity for .parent, or pass .{} for an unparented instance.
The returned root is owned by the world and has a Transform and SceneInstance.
Its immediate scene entities exist before the call returns. Nested instances synchronize
at the normal scene-sync point. A failed read/parse/allocation removes the entities created
by that call.
Service views and component pointers do not survive a world reset or reload. Use fresh
App access and typed EntityRef resolution instead of retaining borrowed storage.
Stream a scene behind a loading screen
stream loads a scene over the frames that follow instead of all at once. It reads the
scene file in the background, puts the models and images the scene uses on the GPU one per
frame, and then places the scene. Pass .replace = true to swap out the whole world, as a
level change does, or leave it out to add the scene beside the world, under .parent if
you give one.
const level = eggy.AssetRef(.scene).init("resources/scene/level.scene.zon");
self.loading = try app.scenes().stream(level, .{ .replace = true });
// In a later update:
if (self.loading) |ticket| if (try app.scenes().streamed(ticket)) |placed| {
self.loading = null;
_ = placed.root; // null after a replace, the instance's root otherwise
};
Until the scene is placed, Eggy covers the game with a loading screen. It shows the scene's
name, a progress bar, and which model or image is loading. To draw your own screen, pass
.loading_screen = false and read app.scenes().loading() in gameUi. It gives the scene,
the stage (reading, models_and_images or placing), how many of the scene's models and
images are done out of how many, the item loading now, and fraction(), from 0 to 1.
Only one scene streams at a time. A second stream call before the first is placed fails
with SceneStreamBusy. The game keeps updating while a scene streams. Each model or image
still loads on the main thread, so a very large one can hold a single frame. If a scene
fails to load, streamed returns the error and the world is left as it was.
Check the loaded result
After instantiate succeeds, inspect the returned root and its immediate entities.
Nested instances appear at the normal scene-sync point. In the editor, an instance
selects as one root; open its source scene to inspect or edit generated descendants.
Test a packaged runtime as well as the editor. A missing resource must report an error
such as FileNotFound or NotPackaged; do not replace a failed load with empty data.
Check startup scene configuration if the
runtime opens a different scene from the one you expected.
Understand packaged files
Assets, scenes, and packaging explains the asset catalog, packaged IDs, world-owned instances, and startup scene choices. Error handling covers required and optional loads. See feature support for unavailable backends.
Use engine-free format tools
See engine-free format tools
for the eggy_assets build import and format ownership, and the
API reference for low-level scene and asset operations.