资产
资产包
资产包是旧的 Loader
类的现代替代品。它是一种基于承诺的资源管理解决方案,可以下载、缓存和解析您的资产,以便您可以使用。下载可以同时进行,并且在后台进行,从而加快应用程序的启动时间。缓存可确保您永远不会下载两次相同的资产,并且可扩展分析程序系统允许您轻松扩展和自定义以满足您的需求。
入门
Assets
高度依赖于所有现代浏览器支持的 JavaScript Promises,但是,如果您的目标浏览器 不支持 promises 则应该考虑 对它们进行 polyfill。
使用我们的第一个 Assets Promise
要快速使用 Assets
实例,您只需调用 Assets.load
并传入资产即可。这将返回一个承诺,在解析后将得到您需要的 value。在此示例中,我们将加载一个纹理,然后将其转换为一个精灵。
import { Application, Assets, Sprite } from 'pixi.js';
// Create a new application
const app = new Application();
// Initialize the application
await app.init({ background: '#1099bb', resizeTo: window });
// Append the application canvas to the document body
document.body.appendChild(app.canvas);
// Start loading right away and create a promise
const texturePromise = Assets.load('https://pixijs.npmjs.net.cn/assets/bunny.png');
// When the promise resolves, we have the texture!
texturePromise.then((resolvedTexture) =>
{
// create a new Sprite from the resolved loaded Texture
const bunny = Sprite.from(resolvedTexture);
// center the sprite's anchor point
bunny.anchor.set(0.5);
// move the sprite to the center of the screen
bunny.x = app.screen.width / 2;
bunny.y = app.screen.height / 2;
app.stage.addChild(bunny);
});
使用 Assets
时要记住的一个非常重要的事情是,所有请求都被缓存,如果 URL 相同,返回的承诺也将相同。在代码中显示
promise1 = Assets.load('bunny.png')
promise2 = Assets.load('bunny.png')
// promise1 === promise2
开箱即用,可以加载以下资产类型,无需外部插件
- 纹理 (
avif
、webp
、png
、jpg
、gif
) - 精灵页 (
json
) - 位图字体 (
xml
、fnt
、txt
) - Web 字体 (
ttf
、woff
、woff2
) - Json 文件 (
json
) - 文本文件 (
txt
)
只需创建附加的加载器解析器,便可相当容易地添加更多类型。
处理不可识别的 URL
使用基本语法时,可以通过文件扩展名识别资源类型 - 例如 https://pixijs.npmjs.net.cn/assets/bunny.png
以 .png
结尾,因此 Assets.load
可以弄清楚它应该使用纹理加载器。
在某些情况下,你可能无法控制 URL,并且必须使用没有可识别扩展名的的模棱两可的 URL。在这种情况下,你可以指定一个明确的加载器
promise = Assets.load({
src: 'https://example.com/ambiguous-file-name',
loader: 'loadTextures'
})
以下是你可使用的部分 loader
值
- 纹理:
loadTextures
- Web 字体:
loadWebFont
- Json 文件:
loadJson
- 文本文件:
loadTxt
关于已解决承诺的警告
当资源下载时,它会作为 Assets
实例内的承诺进行缓存,如果你尝试再次下载它,你将获得一个已解决的承诺的引用。然而,承诺处理程序 .then(...)
/.catch(...)
/.finally(...)
总是异步的,这意味着即使承诺已解决,.then(...)
/.catch(...)
/.finally(...)
下面的代码也会在它们内部的代码执行之前执行。请看此示例
console.log(1);
alreadyResolvedPromise.then(() => console.log(2));
console.log(3);
// Console output:
// 1
// 3
// 2
要了解为什么会出现这种情况,你需要了解 微任务,不过,使用 async 函数应该可以缓解此问题。
使用 Async/Await
有一种处理承诺的方法更直观且更容易理解:async
/await
。
要使用它,我们首先需要创建一个函数/方法并将其标记为 async
。
async function test() {
// ...
}
此函数现在将返回值包装在承诺中,并允许我们在承诺之前使用 await
关键字停止代码执行直到它得到解决并给我们值。
请看此示例
// Create a new application
const app = new Application();
// Initialize the application
await app.init({ background: '#1099bb', resizeTo: window });
// Append the application canvas to the document body
document.body.appendChild(app.canvas);
const texture = await Assets.load('https://pixijs.npmjs.net.cn/assets/bunny.png');
// Create a new Sprite from the awaited loaded Texture
const bunny = Sprite.from(texture);
// Center the sprite's anchor point
bunny.anchor.set(0.5);
// Move the sprite to the center of the screen
bunny.x = app.screen.width / 2;
bunny.y = app.screen.height / 2;
app.stage.addChild(bunny);
texture
变量现在不是一个承诺,而是在等待此承诺解决后产生的已解决纹理。
const texture = await Assets.load('examples/assets/bunny.png');
它允许我们编写更具可读性的代码而不会陷入回调地狱,并更好地思考我们的程序何时停止和产出。
加载多个资源
我们可以通过使用 Assets.add(...)
将资源添加到缓存中,然后使用 Assets.load(...)
和想要加载的所有键同时加载所有资源。请参阅下面的示例
// Append the application canvas to the document body
document.body.appendChild(app.canvas);
// Add the assets to load
Assets.add({ alias: 'flowerTop', src: 'https://pixijs.npmjs.net.cn/assets/flowerTop.png' });
Assets.add({ alias: 'eggHead', src: 'https://pixijs.npmjs.net.cn/assets/eggHead.png' });
// Load the assets and get a resolved promise once both are loaded
const texturesPromise = Assets.load(['flowerTop', 'eggHead']); // => Promise<{flowerTop: Texture, eggHead: Texture}>
// When the promise resolves, we have the texture!
texturesPromise.then((textures) =>
{
// Create a new Sprite from the resolved loaded Textures
const flower = Sprite.from(textures.flowerTop);
flower.anchor.set(0.5);
flower.x = app.screen.width * 0.25;
flower.y = app.screen.height / 2;
app.stage.addChild(flower);
const egg = Sprite.from(textures.eggHead);
egg.anchor.set(0.5);
egg.x = app.screen.width * 0.75;
egg.y = app.screen.height / 2;
app.stage.addChild(egg);
});
但是,如果你想要充分利用 @pixi/Assets
,则应该使用包。包只是将资源组合在一起的一种方法,可以通过调用 Assets.addBundle(...)
/Assets.loadBundle(...)
手动添加包。
Assets.addBundle('animals', {
bunny: 'bunny.png',
chicken: 'chicken.png',
thumper: 'thumper.png',
});
const assets = await Assets.loadBundle('animals');
然而,处理包的最佳方法是使用清单并使用所述清单调用 Assets.init({manifest})
(或指向它的 URL,这会更好)。将我们的资源拆分为对应于应用程序的屏幕或阶段的包对于在用户使用应用程序的同时在后台加载非常有用,而不是将它们锁定在单个整体加载屏幕中。
{
"bundles":[
{
"name":"load-screen",
"assets":[
{
"alias":"background",
"src":"sunset.png"
},
{
"alias":"bar",
"src":"load-bar.{png,webp}"
}
]
},
{
"name":"game-screen",
"assets":[
{
"alias":"character",
"src":"robot.png"
},
{
"alias":"enemy",
"src":"bad-guy.png"
}
]
}
]
}
Assets.init({manifest: "path/manifest.json"});
请注意你只能调用 init
一次。
请记住,重复 URL 没有缺点,因为它们都将被缓存,因此,如果你在两个包中需要相同的资源,则可以重复请求,而无需支付任何额外费用!
后台加载
以前的加载方法是在应用程序开始时使用 Loader
加载所有资源,但现在的用户耐心较差,希望内容可以立即获得,因此做法正在向只加载显示用户一些内容所需的最低限度内容的方向发展,并且在用户与该内容互动时,我们继续在后台加载后续内容。
幸运的是,Assets
为我们提供了一个允许我们在后台加载所有内容的系统,并且如果我们现在需要一些资源,则将它们提升到队列的顶部,以便最大程度地缩短加载时间。
为此,我们有方法 Assets.backgroundLoad(...)
和 Assets.backgroundLoadBundle(...)
,它们会被动地开始在后台加载这些资源。因此,当你最终开始加载它们时,你会得到一个立即解析为加载的资源的承诺。
当你最终需要显示资源时,调用常规的 Assets.load(...)
或 Assets.loadBundle(...)
,你将得到相应的承诺。
完成此操作的最佳方法是使用包,请参阅以下示例
import { Application, Assets, Sprite } from 'pixi.js';
// Create a new application
const app = new Application();
async function init()
{
// Initialize the application
await app.init({ background: '#1099bb', resizeTo: window });
// Append the application canvas to the document body
document.body.appendChild(app.canvas);
// Manifest example
const manifestExample = {
bundles: [
{
name: 'load-screen',
assets: [
{
alias: 'flowerTop',
src: 'https://pixijs.npmjs.net.cn/assets/flowerTop.png',
},
],
},
{
name: 'game-screen',
assets: [
{
alias: 'eggHead',
src: 'https://pixijs.npmjs.net.cn/assets/eggHead.png',
},
],
},
],
};
await Assets.init({ manifest: manifestExample });
// Bundles can be loaded in the background too!
Assets.backgroundLoadBundle(['load-screen', 'game-screen']);
}
init();
我们为游戏中的每个屏幕创建一个包,并在应用程序开始时设置它们全部开始下载。如果用户在我们的应用程序中进行得足够慢,那么他们就不应该在第一个加载屏幕后看到加载屏幕!