Skip to content

Assets ​

The asset tools load things in the right order, count who is using them, and release them when the scene that loaded them ends. They do not read files themselves: a loader you supply does that.

ts
import { AssetHandle, AssetKey, AssetLoadPlan, AssetManager, AssetPlanResult, AssetScope, FakeAssetLoader } from "gamekit";

const CONFIG: AssetKey<string> = new AssetKey<string>("config");
const ATLAS: AssetKey<string> = new AssetKey<string>("atlas");

// A loader is any object with async load(key) and unload(key, asset).
// FakeAssetLoader is the in-memory one gamekit ships for tests and samples.
const loader: FakeAssetLoader = new FakeAssetLoader();
loader.set(CONFIG, "quality=high");
loader.set(ATLAS, "atlas pixels");
const assets: AssetManager = new AssetManager(loader);

async function main(): Promise<void> {
    const plan: AssetLoadPlan = new AssetLoadPlan();
    plan.add(CONFIG);
    plan.add(ATLAS, [CONFIG]);           // the atlas loads after the config
    const result: AssetPlanResult = await assets.loadPlan(plan);
    console.log("plan ok: " + result.isOk + ", loaded " + result.loadedCount);

    const scene: AssetScope = assets.createScope("level-1");
    const atlas: AssetHandle<string> = await scene.load(ATLAS);
    console.log("atlas: " + atlas.get() + ", refs " + assets.getRefCount(ATLAS));
    scene.dispose();                     // releases every handle the scope took
    console.log("refs after scope: " + assets.getRefCount(ATLAS));
}

await main();
text
plan ok: true, loaded 2
atlas: atlas pixels, refs 2
refs after scope: 1

The parts ​

PartWhat it is
AssetKey<T>A typed name for one asset.
loaderAny object with async load(key) and unload(key, asset). Pass a default one to new AssetManager(loader), or set one per key with registerLoader(key, loader). FakeAssetLoader is an in-memory loader for tests and samples.
AssetManagerLoads, caches and reference-counts assets. getRefCount(key), isLoaded(key), replaceAsset(key, value), shutdown().
AssetHandle<T>One use of an asset. get() returns it; release() gives it back.
AssetScopeA group of handles with one owner. load(key), acquire(key), get(key), release(key), releaseAll(), dispose().
AssetLoadPlanA set of keys with dependencies, loaded in waves.

Load plans ​

plan.add(key, dependencies?, options?) adds an entry. validate() checks the plan and throws CyclicDependency before any loading if the dependencies loop. manager.loadPlan(plan) loads each wave after the one it depends on and returns an AssetPlanResult:

FieldMeaning
isOk, isCancelledDid it all load? Was it cancelled?
loadedCount, failedCountCounts.
getHandle(key)The handle for one key, or null.
getFailure(key), getFailureTrace(key)What went wrong for one key.

AssetPlanEntry carries a weight for progress reporting and a retries count. Loads can be cancelled with an AssetCancellationToken.

Scopes and scenes ​

Give each scene its own scope, and dispose it when the scene ends. With a SceneCoordinator, registerAssetScope(scene, scope) does that for you on exit.

Do not keep handles in globals. A handle used after its scope is disposed throws StaleHandle, which is how you find the leak. For assets every scene shares, use one long-lived scope owned by the app.

Reference: AssetManager, AssetScope, AssetHandle, AssetLoadPlan, AssetPlanResult, FakeAssetLoader.