资源加载
Estella 提供 Assets 资源(AssetServer 实例)来加载纹理、Spine 动画、材质和通用文件。在任何系统中通过 Res(Assets) 访问。
访问 Assets
import { defineSystem, addStartupSystem, Res } from 'esengine';import { Assets } from 'esengine';
addStartupSystem(defineSystem( [Res(Assets)], async (assets) => { const tex = await assets.loadTexture('assets/player.png'); console.log(`加载完成 ${tex.width}x${tex.height},handle: ${tex.handle}`); }));纹理加载
loadTexture(path)
加载图片并返回 TextureInfo 对象。图片会自动垂直翻转以适配 OpenGL UV 坐标。
const tex = await assets.loadTexture('assets/player.png');sprite.texture = tex.handle;返回值: TextureInfo { handle: TextureHandle, width: number, height: number }
其他纹理方法
| 方法 | 返回值 | 说明 |
|---|---|---|
loadTextureRaw(source) | Promise<TextureInfo> | 不翻转加载(Spine 运行时使用) |
getTexture(path) | TextureInfo | undefined | 返回已缓存的 TextureInfo |
hasTexture(path) | boolean | 判断纹理是否已缓存 |
releaseTexture(path) | void | 减少引用计数;计数归零时释放 GPU 资源 |
releaseAll() | void | 释放所有已缓存的资源(纹理、字体、材质、着色器、预制体、Spine) |
九宫格元数据
对于九宫格精灵,加载后设置边框元数据:
const tex = await assets.loadTexture('assets/panel.png');assets.setTextureMetadata(tex.handle, { left: 10, right: 10, top: 10, bottom: 10});也可以按路径设置(纹理已加载时):
assets.setTextureMetadataByPath('assets/panel.png', { left: 10, right: 10, top: 10, bottom: 10});Spine 加载
const result = await assets.loadSpine('assets/hero.json', 'assets/hero.atlas');if (!result.success) { console.error(result.error);}loadSpine 自动完成以下步骤:
- 获取 atlas 文件并写入虚拟文件系统
- 解析 atlas 中的纹理文件名并逐一加载
- 获取骨骼文件(
.json或.skel二进制)并写入虚拟文件系统
| 方法 | 返回值 | 说明 |
|---|---|---|
loadSpine(skeleton, atlas, baseUrl?) | Promise<SpineLoadResult> | 加载 Spine 骨骼和图集 |
isSpineLoaded(skeleton, atlas) | boolean | 检查 Spine 资源对是否已加载 |
releaseSpine(key) | void | 释放已加载的 Spine 资源(key = "skeleton:atlas") |
位图字体加载
const fontHandle = await assets.loadBitmapFont('assets/my-font.fnt');支持 .fnt(BMFont 文本格式)和 .bmfont(JSON 元数据)文件。加载器会自动解析并加载引用的纹理图集。
| 方法 | 返回值 | 说明 |
|---|---|---|
loadBitmapFont(path, baseUrl?) | Promise<FontHandle> | 加载位图字体文件及其纹理,返回字体句柄 |
getFont(path) | FontHandle | undefined | 获取已缓存的字体句柄,未加载则返回 undefined |
releaseFont(path) | void | 从内存中释放字体及其纹理 |
预制体加载
const prefab = await assets.loadPrefab('prefabs/Enemy.esprefab');加载并解析 .esprefab 文件。返回的 PrefabData 对象可传入 instantiatePrefab() 使用。
| 方法 | 返回值 | 说明 |
|---|---|---|
loadPrefab(path, baseUrl?) | Promise<PrefabData> | 加载并缓存预制体文件 |
详见预制体了解完整的预制体 API。
通用文件加载
| 方法 | 返回值 | 说明 |
|---|---|---|
loadJson<T>(path, options?) | Promise<T> | 加载并解析 JSON 文件 |
loadText(path, options?) | Promise<string> | 加载文本文件 |
loadBinary(path, options?) | Promise<ArrayBuffer> | 加载二进制文件 |
const config = await assets.loadJson<GameConfig>('assets/config.json');const csv = await assets.loadText('assets/levels.csv');const data = await assets.loadBinary('assets/tilemap.bin');FileLoadOptions
| 选项 | 类型 | 说明 |
|---|---|---|
baseUrl | string | 覆盖此请求的基础 URL |
noCache | boolean | 跳过缓存,始终从网络获取 |
批量加载
使用 loadAll 从 AddressableManifest 并行加载所有资源:
const manifest = await assets.loadJson('assets/addressable-manifest.json');assets.setAddressableManifest(manifest);
const bundle = await assets.loadAll(manifest);
// 从 bundle 中访问已加载的资源const bgTex = bundle.textures.get('bg');const config = bundle.json.get('game-config');也可以按标签或分组加载部分资源:
const bundle = await assets.loadByLabel('ui-sprites');const levelBundle = await assets.loadGroup('level-1');AssetBundle
返回的 AssetBundle 包含以地址(无地址时以路径)为键的 Map:
| 字段 | 类型 |
|---|---|
textures | Map<string, TextureInfo> |
materials | Map<string, LoadedMaterial> |
spine | Map<string, SpineLoadResult> |
fonts | Map<string, FontHandle> |
prefabs | Map<string, PrefabData> |
json | Map<string, unknown> |
text | Map<string, string> |
binary | Map<string, ArrayBuffer> |
材质加载
const loaded = await assets.loadMaterial('assets/effects/glow.esmaterial');sprite.material = loaded.handle;| 方法 | 返回值 | 说明 |
|---|---|---|
loadMaterial(path, baseUrl?) | Promise<LoadedMaterial> | 加载并编译 .esmaterial 文件 |
getMaterial(path, baseUrl?) | LoadedMaterial | undefined | 获取已缓存的材质 |
hasMaterial(path, baseUrl?) | boolean | 检查材质是否已缓存 |
详见材质与着色器了解完整的材质 API 和 .esmaterial 文件格式。
缓存机制
所有加载方法按路径缓存。同一路径加载两次会直接返回缓存结果,不会再次发起网络请求。使用 noCache 选项跳过缓存:
const fresh = await assets.loadJson('assets/config.json', { noCache: true });基础 URL
设置 assets.baseUrl 为所有相对路径添加前缀:
assets.baseUrl = 'https://cdn.example.com/game';const tex = await assets.loadTexture('sprites/player.png');// 请求 https://cdn.example.com/game/sprites/player.png绝对路径和完整 URL 不受 baseUrl 影响。
场景加载
通过一次调用加载场景文件及其所有引用的资源:
const sceneData = await assets.loadJson<SceneData>('assets/scenes/main.esscene');const entityMap = await assets.loadScene(world, sceneData);loadScene 预先收集场景中所有资产引用(纹理、材质、Spine、字体、预制体),通过 Promise.all 并行加载,然后实例化实体层级。返回 Map<number, Entity>,将场景 ID 映射到运行时实体。
嵌入式资源
对于 Playable Ad 构建,编辑器的构建流程会自动将所有引用的资源以 data URI 形式嵌入输出包中。运行时 assets.loadTexture() / assets.loadJson() 等加载调用会直接从嵌入数据解析,无需网络请求,也无需修改任何代码。
对于自定义构建,可以手动注册嵌入式资源:
assets.registerEmbeddedAssets({ 'assets/player.png': 'data:image/png;base64,...', 'assets/config.json': 'data:application/json;base64,...',});Addressable 资产
Addressable 资产允许通过逻辑地址、标签或分组加载资源,而非直接使用文件路径。编辑器在构建过程中生成 AddressableManifest,将地址和标签映射到资产路径。
按地址加载
const texture = await assets.load('player-idle');无需加载即可解析地址到清单条目:
const entry = assets.resolveAddress('player-idle');if (entry) { console.log(`类型: ${entry.type},路径: ${entry.path}`);}按标签加载
加载标记了某个标签的所有资产,返回 AssetBundle:
const bundle = await assets.loadByLabel('ui-sprites');for (const [path, tex] of bundle.textures) { console.log(`已加载 ${path}: ${tex.width}x${tex.height}`);}按分组加载
加载某个命名分组中的所有资产:
const bundle = await assets.loadGroup('level-1');设置清单
清单由编辑器在构建时生成。运行时在加载前设置:
import { Assets } from 'esengine';
const manifest = await assets.loadJson('assets/addressable-manifest.json');assets.setAddressableManifest(manifest);清单格式
AddressableManifest 的结构如下:
interface AddressableManifest { version: '2.0'; groups: Record<string, AddressableManifestGroup>;}
interface AddressableManifestGroup { bundleMode: string; labels: string[]; assets: Record<string, AddressableManifestAsset>;}
interface AddressableManifestAsset { path: string; address?: string; type: AddressableAssetType; size: number; labels: string[]; metadata?: { atlas?: string; atlasPage?: number; atlasFrame?: { x: number; y: number; width: number; height: number }; };}每个分组包含资产及其路径、可选地址、标签和类型特定的元数据。资产类型包括 texture、material、spine、bitmap-font、prefab、json、text、binary 和 audio。