Skip to content

t1k:cocos:playable:asset-management

FieldValue
Moduleplayable
Version0.5.6
Efforthigh
Tools

Keywords: ads, assets, AssetsManager, cache, cocos, loading, playable, resource

/t1k:cocos:playable:asset-management

Singleton wrapping Cocos resources.load() with a memory cache and in-flight deduplication. Critical for playable ads where the <5MB size budget demands careful asset reuse. See t1k-cocos-playable-object-pool for how ObjectPoolManager delegates to this class.

AssetsManager (singleton)
├── _cache: Map<string, Asset> ← "path_TypeName" → Asset
└── _loadingPromises: Map<string, Promise> ← deduplication guard

Cache key format: "${path}_${type.name}" e.g. "audio/BGM_AudioClip".

All assets loaded from resources/ folder (Cocos runtime bundle). Paths are relative to assets/resources/.

const am = AssetsManager.instance;
// Generic typed load (with cache + dedup)
const prefab = await am.loadResource("prefab/UI/GameHUD", Prefab);
const sprite = await am.loadResource("sprites/logo", SpriteFrame);
const texture = await am.loadResource("textures/bg", Texture2D);
const json = await am.loadResource("data/config", JsonAsset);
// Convenience shorthands
const prefab = await am.loadPrefab("prefab/UI/GameHUD");
const sprite = await am.loadSprite("sprites/logo");
const audio = await am.loadAudio("BGM"); // prepends "audio/" automatically
const tex = await am.loadTexture("textures/bg");
const json = await am.loadJson("data/config");
// Load entire directory
const frames = await am.loadDir("sprites/icons", SpriteFrame);
const all = await am.loadDir("prefab/enemies"); // untyped
// Parallel preload
await am.preloadResources([
{ path: "prefab/UI/GameHUD", type: Prefab },
{ path: "audio/BGM", type: AudioClip },
]);
// Bundle loading (for split bundles)
const bundle = await am.loadBundle("game-assets");
// Cache queries
am.isCached("prefab/UI/GameHUD", Prefab); // boolean
am.getCachedAsset("prefab/UI/GameHUD", Prefab); // Prefab | null
am.getCacheSize(); // number of cached entries
// Release (decrements Cocos ref count)
am.releaseAsset("prefab/UI/GameHUD", Prefab);
am.releaseAll(); // full cache flush — use on scene exit

Concurrent calls with the same path+type share one Promise. The second caller awaits the same network request — no double load. Promise entry is removed from _loadingPromises on resolve or reject.

// Both calls share one load operation:
const [a, b] = await Promise.all([
am.loadPrefab("prefab/Coin"),
am.loadPrefab("prefab/Coin"), // hits dedup guard, returns same promise
]);

loadAudio(name) automatically prepends "audio/":

await am.loadAudio("BGM"); // loads resources/audio/BGM.mp3
await am.loadAudio("SFX_Tap");

Do not include "audio/" in the name — it will double-prefix.

loadAsync(type, id) loads from prefab/UI/{id} — kept for backward compatibility. Prefer loadResource() for new code.

Preload all assets during loading state:

await AssetsManager.instance.preloadResources([
{ path: "prefab/UI/WinView", type: Prefab },
{ path: "prefab/UI/LoseView", type: Prefab },
{ path: "audio/BGM", type: AudioClip },
{ path: "audio/SFX_Win", type: AudioClip },
]);

Load and instantiate a prefab:

const prefab = await AssetsManager.instance.loadPrefab("prefab/UI/Tutorial");
const node = instantiate(prefab);
node.parent = this.node;

Load sprite for dynamic UI:

const frame = await AssetsManager.instance.loadSprite("sprites/avatars/hero");
this.avatarSprite.spriteFrame = frame;

Release on scene exit to free memory:

onDestroy(): void {
AssetsManager.instance.releaseAll();
}
AssetsManagerresources.load() direct
CacheYes — reuses assetNo — loads again
DedupYes — shared promiseNo — parallel loads
Type safetyTyped genericsTyped generics
Use caseAll runtime loadsAvoid — use AssetsManager
  • Call releaseAll() on scene/level transitions
  • Use getCacheSize() to monitor cached count during development
  • Prefer loadDir() for icon sheets — one call, all frames cached
  • AudioClips are the largest single-asset risk; releaseAsset() per clip after playback if one-shot
  • Passing "audio/BGM" to loadAudio() — results in path "audio/audio/BGM" (not found)
  • Not awaiting loadResource() before accessing the asset — returns unresolved Promise
  • Calling releaseAll() while assets are still referenced by active nodes — Cocos ref-counts drop to 0 and GC destroys them mid-use
  • Using loadAsync() (legacy) for new prefabs — path is hardcoded to prefab/UI/ prefix
  • Bundle remote URLs require crossOrigin headers — silent CORS fails on iOS Safari (in-app webview).
  • Atlas packing strategy affects load order — a single 10MB atlas blocks first frame; split sub-atlases per scene.
  • Asset GC is not deterministic — call Asset.decRef explicitly when releasing dynamic loads.