Skip to content

Loading Assets

Scripts load project resources through the Loader module. Paths are always project-relative strings starting with /resources/, mirroring the folder structure in the Studio resource tree.

local Loader = require 'engine/loader'

Path conventions

Path pattern Loads
/resources/textures/... Image items (textures)
/resources/materials/... Material items
/resources/meshes/... Mesh items
/resources/prefabs/... Prefab items
/resources/fonts/... Font items (for Canvas text)
/resources/gridmaps/... Grid map items

The path is the item's location in the project, without a file extension. Use the path you see in the project view

Loader functions

All loader functions return a Promise. See Promises and Await for when to use await() vs :next().

Function Returns
Loader.loadImage(path, options?) Texture item handle
Loader.loadMaterial(path, options?) Material item handle
Loader.loadMesh(path, options?) Mesh item handle
Loader.loadPrefab(path, options?) Prefab item handle
Loader.loadFont(path, options?) Font item handle
Loader.loadGridMap(path, options?) Grid map item handle
Loader.getItemCountToLoad() Number of items still loading (sync)

Common options

Option Description
waitUntilFinished When true, the promise resolves only after GPU/upload work is complete. Default false.
flipY Image only. Flip texture vertically. Default true for 3D; use false for Canvas.

Setup-time loading (blocking)

In init() or other one-time setup, await() keeps code linear:

local Loader = require 'engine/loader'
local Material = require 'engine/items/material'
local SgSphere = require 'engine/sceneobjects/sgsphere'
local Transform = require 'engine/math/transform'
local Vector3 = require 'engine/math/vector3'

local MyBehaviour = Class.new(Behaviour)

function MyBehaviour:init()
    local wood = await(Loader.loadImage("/resources/textures/wood", {
        waitUntilFinished = true,
    }))
    local mat = await(Material.create({ albedoMap = wood }))
    self.sphere = await(SgSphere.create({
        radius = 1,
        material = mat,
        transform = Transform.new(Vector3.new(0, 1, 0)),
    }))
end

return MyBehaviour

await() pauses the current function and blocks the frame until loading finishes. That is acceptable during setup but avoid it inside update() or render() because it would block the renderer and the game essentially pauses.

Runtime loading (non-blocking) ----------------------------

Use :next() when you must keep the game responsive:

function MyBehaviour:spawnProp()
    Loader.loadMesh("/resources/meshes/prop"):next(function(meshItem)
        -- use meshItem with SgMesh.create or setLinkItem
    end):catch(function(err)
        Console.error("Failed to load prop:", err)
    end)
end

Ideally you will want to load all items during :init() or even better within a Link script property which gets loaded while loading your game into memory, not during runtime.

Error handling

Chain :catch() on promises, or wrap await() in pcall:

local ok, texture = pcall(function()
    return await(Loader.loadImage("/resources/textures/missing"))
end)
if not ok then
    Console.error("Load failed:", texture)
end

Items vs scene objects --------------------

Loader returns Item handles (assets). To place something in the scene you still create a SceneObject — for example SgMesh.create({ mesh = meshItem }) or Prefabs for prefab items.

Link properties (Properties) can reference items assigned in the editor; Loader is for fetching items by path at runtime.

Fonts and Canvas

After Loader.loadFont("/resources/fonts/myfont"), reference the font in Canvas with the item name. Alternatively you can just link fonts to the canvas fonts section in your Project in the inspector

Canvas.setFont("20px myfont")

See Canvas and HUD Overlays.

Further reading