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: 1The parts
| Part | What it is |
|---|---|
AssetKey<T> | A typed name for one asset. |
| loader | Any 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. |
AssetManager | Loads, 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. |
AssetScope | A group of handles with one owner. load(key), acquire(key), get(key), release(key), releaseAll(), dispose(). |
AssetLoadPlan | A 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:
| Field | Meaning |
|---|---|
isOk, isCancelled | Did it all load? Was it cancelled? |
loadedCount, failedCount | Counts. |
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.