关于 loading-assets
面向 Phaser 游戏开发者的资源加载技能指南,介绍如何使用 Loader 在场景中排队加载资源。涵盖多种文件类型、全局缓存和加载进度事件。
主要功能
- 01加载图像、音频和视频
- 02加载数据、字体与地图
- 03通过资源包组织文件
- 04使用全局缓存共享资源
- 05监听加载进度与错误事件
适用场景
- 01在场景启动前加载游戏资源
- 02制作资源加载进度条
- 03按清单加载一组资源
- 04根据加载结果处理失败文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ phaserjs/ phaser --skill loading-assets安装后在 Claude Code 中输入/loading-assets确认已加载。
附带文件references/REFERENCE.md
详细介绍
加载资源
Phaser 加载器(
this.load)负责获取所有外部内容:图片、音频、JSON、瓦片地图、图集、字体、脚本等。资源在preload()中排队,并行加载,并放入全局缓存中,供每个 Scene 访问。
关键源码路径: src/loader/LoaderPlugin.js、src/loader/File.js、src/loader/filetypes/、src/loader/events/
相关技能: ../game-setup-and-config/SKILL.md、../scenes/SKILL.md、../sprites-and-images/SKILL.md
快速开始
class GameScene extends Phaser.Scene {
preload() {
this.load.image('logo', 'assets/logo.png');
}
create() {
this.add.image(400, 300, 'logo');
}
}
在 preload() 中加载的资源,在 create() 运行时保证已就绪。加载器会在 preload 阶段自动启动。
核心概念
Preload 模式
每个 Scene 都可以定义一个 preload() 方法。当 preload() 完成时,加载器会自动启动,并等待所有排队的文件完成后才调用 create()。
preload() {
// Queue files - they don't load immediately
this.load.image('sky', 'assets/sky.png');
this.load.spritesheet('dude', 'assets/dude.png', { frameWidth: 32, frameHeight: 48 });
this.load.audio('jump', 'assets/jump.mp3');
}
create() {
// All assets above are now available
this.add.image(400, 300, 'sky');
this.add.sprite(100, 450, 'dude');
this.sound.play('jump');
}
在 Preload 之外加载
如果你在 preload() 之外调用 this.load 方法(例如在 create() 中或响应用户操作时),必须手动启动加载器:
create() {
this.load.image('extra', 'assets/extra.png');
this.load.once('complete', () => {
this.add.image(400, 300, 'extra');
});
this.load.start();
}
URL 解析:baseURL、path 和 prefix
文件的最终 URL 按如下方式解析:baseURL + path + filename。这些可以通过游戏配置或运行时设置。
preload() {
// Set base URL (prepended to all relative paths)
this.load.setBaseURL('https://cdn.example.com/');
// Set path (prepended after baseURL, before filename)
this.load.setPath('assets/images/');
// Set key prefix (prepended to the cache key, not the URL)
this.load.setPrefix('LEVEL1.');
// Loads from: https://cdn.example.com/assets/images/hero.png
// Cached with key: LEVEL1.hero
this.load.image('hero', 'hero.png');
// Absolute URLs bypass the path/baseURL
this.load.image('cloud', 'https://other-server.com/cloud.png');
}
这些也可以在游戏配置中设置:
const config = {
loader: {
baseURL: 'https://cdn.example.com/',
path: 'assets/',
prefix: '',
maxParallelDownloads: 32,
crossOrigin: 'anonymous',
responseType: '',
async: true,
timeout: 0,
maxRetries: 2,
imageLoadType: 'XHR' // or 'HTMLImageElement'
}
};
全局缓存
资源存储在游戏级别的全局缓存中,而不是每个 Scene 各自存储。在一个 Scene 中加载的图片,在其他每个 Scene 中都可使用。纹理放入 game.textures(纹理管理器)。其他数据放入 game.cache 的子缓存中(例如 game.cache.json、game.cache.audio、game.cache.xml)。
加载事件
加载器在整个加载生命周期中会发出事件。使用这些事件来实现进度条和加载界面。
preload() {
this.load.on('progress', (value) => {
// value is 0 to 1
console.log(`Loading: ${Math.round(value * 100)}%`);
});
this.load.on('complete', () => {
console.log('All assets loaded');
});
this.load.on('loaderror', (file) => {
console.warn('Failed to load:', file.key);
});
this.load.image('bg', 'assets/bg.png');
}
常见模式
加载图片和精灵图集
preload() {
// Single image
this.load.image('star', 'assets/star.png');
// Image with normal map (pass URL array: [texture, normalMap])
this.load.image('brick', ['assets/brick.png', 'assets/brick_n.png']);
// Sprite sheet (fixed frame sizes)
this.load.spritesheet('explosion', 'assets/explosion.png', {
frameWidth: 64,
frameHeight: 64,
startFrame: 0,
endFrame: 23,
margin: 0,
spacing: 0
});
// SVG (optionally rasterize at a specific size)
this.load.svg('logo', 'assets/logo.svg', { width: 400, height: 400 });
}
加载音频
preload() {
// Single file
this.load.audio('bgm', 'assets/music.mp3');
// Multiple formats for cross-browser support
this.load.audio('bgm', ['assets/music.ogg', 'assets/music.mp3']);
// Audio sprite (JSON defines named regions within a single audio file)
this.load.audioSprite('sfx', 'assets/sfx.json', [
'assets/sfx.ogg',
'assets/sfx.mp3'
]);
}
加载 JSON 和瓦片地图
preload() {
// JSON data (stored in this.cache.json)
this.load.json('levelData', 'assets/level1.json');
// JSON with a dataKey to extract a sub-object
this.load.json('enemies', 'assets/data.json', 'enemies');
// Tiled tilemap (JSON format exported from Tiled)
this.load.tilemapTiledJSON('map', 'assets/map.json');
// CSV tilemap
this.load.tilemapCSV('csvmap', 'assets/level.csv');
// Impact tilemap
this.load.tilemapImpact('impactmap', 'assets/level.js');
}
create() {
const data = this.cache.json.get('levelData');
const map = this.make.tilemap({ key: 'map' });
}
加载图集
preload() {
// JSON atlas (e.g., TexturePacker JSON Hash/Array)
this.load.atlas('sprites', 'assets/sprites.png', 'assets/sprites.json');
// XML atlas (e.g., Starling/Sparrow format)
this.load.atlasXML('ui', 'assets/ui.png', 'assets/ui.xml');
// Multi-atlas (atlas split across multiple textures)
this.load.multiatlas('world', 'assets/world.json', 'assets/');
// Unity texture atlas format
this.load.unityAtlas('chars', 'assets/chars.png', 'assets/chars.txt');
// Aseprite atlas
this.load.aseprite('knight', 'assets/knight.png', 'assets/knight.json');
}
create() {
this.add.sprite(400, 300, 'sprites', 'walk_01');
}
加载位图字体
preload() {
// Requires both a texture and XML/JSON font data file
this.load.bitmapFont('pixels', 'assets/font.png', 'assets/font.xml');
}
create() {
this.add.bitmapText(100, 100, 'pixels', 'Hello World', 32);
}
加载视频
preload() {
// Load a video file. Third arg: noAudio flag (default false)
this.load.video('intro', 'assets/intro.mp4');
// Load without audio track
this.load.video('bg_loop', 'assets/loop.mp4', true);
}
加载 Web 字体
preload() {
// Load a font file (ttf, otf, woff, woff2)
this.load.font('myFont', 'assets/myfont.ttf', 'truetype');
// With optional font face descriptors
this.load.font('boldFont', 'assets/bold.woff2', 'woff2', {
weight: 'bold',
style: 'normal'
});
}
加载 Pack 文件
Pack 文件是一个 JSON 文件,描述了一次性加载的多个资源。适合用于组织资源清单。
preload() {
this.load.pack('pack1', 'assets/pack.json');
}
Pack 文件格式:
{
"section1": {
"baseURL": "assets/",
"files": [
{ "type": "image", "key": "bg", "url": "bg.png" },
{ "type": "atlas", "key": "chars", "textureURL": "chars.png", "atlasURL": "chars.json" }
]
}
}
配合进度条加载
preload() {
const progressBar = this.add.graphics();
const progressBox = this.add.graphics();
progressBox.fillStyle(0x222222, 0.8);
progressBox.fillRect(240, 270, 320, 50);
this.load.on('progress', (value) => {
progressBar.clear();
progressBar.fillStyle(0xffffff, 1);
progressBar.fillRect(250, 280, 300 * value, 30);
});
this.load.on('complete', () => {
progressBar.destroy();
progressBox.destroy();
});
// Queue your assets
this.load.image('sky', 'assets/sky.png');
// ... more assets
}
所有文件类型参考
所有 this.load 方法都接受位置参数或单个配置对象。它们也接受配置对象数组,用于批量加载同类型的多个文件。完整的参数表请参见 references/REFERENCE.md。
纹理: image、spritesheet、atlas、atlasXML、multiatlas、unityAtlas、aseprite、svg、htmlTexture、texture(压缩)
音频/视频: audio、audioSprite、video
数据: json、xml、text、binary、html、css、glsl
字体: bitmapFont、font
瓦片地图: tilemapTiledJSON、tilemapCSV、tilemapImpact
其他: animation、pack、script、scripts、plugin、scenePlugin、sceneFile
事件
所有事件都在加载器实例(this.load)上发出。
| 事件字符串 | 回调签名 | 描述 |
|---|---|---|
'addfile' | (key, type, loader, file) | 一个文件被添加到加载队列 |
'start' | (loader) | 加载器已启动。进度为零 |
'load' | (file) | 单个文件加载完成(在处理/缓存之前) |
'fileprogress' | (file, percentComplete) | 单个文件的下载进度(0-1)。仅在浏览器提供 lengthComputable 时触发 |
'progress' | (value) | 整体加载进度更新(0-1) |
'postprocess' | (loader) | 所有文件已加载并处理完成,在内部清理之前 |
'filecomplete' | (key, type, data) | 任意文件加载并处理完成 |
'filecomplete-{type}-{key}' | (key, type, data) | 特定文件完成(例如 'filecomplete-image-hero') |
'loaderror' | (file) | 一个文件加载失败 |
'complete' | (loader, totalComplete, totalFailed) | 队列中的所有文件都已完成 |
事件生命周期顺序
'start'- Loader 开始'fileprogress'- 单个文件进度(如可用,每个文件重复触发)'load'- 每个文件下载完成'filecomplete-{type}-{key}'- 特定文件处理并缓存完成'filecomplete'- 通用的单文件完成'progress'- 整体进度更新'postprocess'- 所有文件完成,清理之前'complete'- 一切完成
注意事项与常见错误
键在其类型内必须唯一。 加载第二张与现有图片键相同的图片会记录警告并跳过。如果需要替换,请先从 Texture Manager 中移除旧纹理。
Sprite sheet 与 atlas 不同。 对于固定尺寸的帧网格(按索引引用),使用 spritesheet()。对于带有命名帧的打包纹理图集,使用 atlas()。
在 preload 之外忘记调用 this.load.start()。 如果在 create() 或之后调用加载方法,Loader 不会自动启动。必须手动调用 this.load.start()。
路径必须以 / 结尾。 如果调用 this.load.setPath(),它会自动追加斜杠。如果直接设置 this.load.path,必须自己包含末尾斜杠。
音频格式回退。 始终提供多种音频格式(至少 OGG + MP3)以支持跨浏览器。Loader 会选择浏览器支持的第一个格式。
Pack 文件可以覆盖 baseURL/path/prefix。 Pack 文件中的每个部分都可以设置自己的 baseURL、path 和 prefix 值。这些仅适用于该部分内的文件,并在该部分处理完成后恢复。
文件键包含前缀。 如果设置 this.load.setPrefix('MENU.') 并加载键为 'bg' 的图片,实际缓存键变为 'MENU.bg'。引用该资源时必须使用完整键。
maxRetries 属性(默认值:2) 控制 Loader 在放弃之前重试失败文件的次数。这在创建时根据 this.load.maxRetries 按文件设置。在文件添加后调整它对这些文件没有影响。
图片加载类型。 默认情况下,图片通过 XHR(作为 blob)加载。在 loader 配置中设置 imageLoadType: 'HTMLImageElement' 以改用 <img> 标签加载,这在某些环境中可以帮助解决 CORS 问题。
本地文件方案。 Loader 默认将 file:// 和 capacitor:// 识别为本地方案(通过 localSchemes)。从本地方案加载的文件会跳过 CORS 头。
跨源。 从不同域加载资源时,在 loader 配置中设置 crossOrigin: 'anonymous'(或通过 this.load.setCORS('anonymous')),尤其是在这些纹理会与 WebGL 一起使用时。
键区分大小写并按类型作用域。 'Player' 和 'player' 是不同的键。图片键 'player' 和音频键 'player' 可以共存而不冲突。
重复键会被静默忽略。 如果 'bg' 已存在于纹理缓存中,第二次调用 this.load.image('bg', ...) 不会做任何事。要替换,请先移除旧资源。
场景的 update() 在 preload 期间不会触发。 在资源加载期间,update() 会暂停。但是,preupdate、postupdate 和 render 仍会触发。
进度可能下降。 如果在加载中途添加新文件(例如,通过 filecomplete 链式调用),进度值可能会下降,因为总文件数增加了。
场景 Payload(在 Preload 之前加载)
通过在 Scene 构造函数中定义 pack,可以在 preload() 运行之前加载文件。这对于加载进度条资源很有用。
class BootScene extends Phaser.Scene {
constructor() {
super({
key: 'BootScene',
pack: {
files: [
{ type: 'image', key: 'bar', url: 'loaderBar.png' }
]
}
});
}
// 'bar' is already available in preload()
}
在加载中途添加文件
使用单文件完成事件在活动加载会话期间链式加载依赖项。
preload() {
this.load.json('level1', 'level1.json');
this.load.on('filecomplete-json-level1', (key, type, data) => {
this.load.image(data.images);
this.load.spritesheet(data.spritesheets);
});
}
内联 Pack 清单
Pack 数据可以内联提供,而不是从 URL 提供。
preload() {
this.load.pack('manifest', {
section1: {
prefix: '',
path: 'assets/',
defaultType: 'image',
files: [
{ type: 'image', key: 'bg', url: 'bg.png' },
{ type: 'spritesheet', key: 'player', url: 'player.png', frameConfig: { frameWidth: 32, frameHeight: 32 } }
]
}
});
}
缓存系统
已加载的资源会进入所有 Scene 共享的全局缓存。纹理会存储在 Texture Manager(this.textures)中;其他资源类型会进入 this.cache 下的类型化子缓存。
// Access cached data
const json = this.cache.json.get('levelData');
const text = this.cache.text.get('dialogue');
// Check existence
this.cache.json.exists('levelData'); // or .has('levelData')
this.textures.exists('hero');
// Remove assets to free memory
this.textures.remove('hero');
this.cache.audio.remove('bgm');
// Listen for cross-scene texture additions
this.textures.on('addtexture-mapKey', (texture) => {
// Another scene loaded 'mapKey' — now available here
});
缓存子类型:this.cache.text、this.cache.json、this.cache.audio、this.cache.binary、this.cache.shader、this.cache.xml。完整 Cache API 请参见 references/REFERENCE.md。
源文件映射
完整的源文件映射和 Cache API 参考请参见 references/REFERENCE.md。
关键文件:src/loader/LoaderPlugin.js(主 Loader)、src/loader/File.js(基础 File 类)、src/loader/filetypes/(所有文件类型加载器)、src/loader/events/(事件常量)。