跳转到内容

资源加载

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 自动完成以下步骤:

  1. 获取 atlas 文件并写入虚拟文件系统
  2. 解析 atlas 中的纹理文件名并逐一加载
  3. 获取骨骼文件(.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

选项类型说明
baseUrlstring覆盖此请求的基础 URL
noCacheboolean跳过缓存,始终从网络获取

批量加载

使用 loadAllAddressableManifest 并行加载所有资源:

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:

字段类型
texturesMap<string, TextureInfo>
materialsMap<string, LoadedMaterial>
spineMap<string, SpineLoadResult>
fontsMap<string, FontHandle>
prefabsMap<string, PrefabData>
jsonMap<string, unknown>
textMap<string, string>
binaryMap<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 };
};
}

每个分组包含资产及其路径、可选地址、标签和类型特定的元数据。资产类型包括 texturematerialspinebitmap-fontprefabjsontextbinaryaudio

下一步