Skip to main content
Version: v8.x

资源

¥Assets

资源包

¥The Assets package

Assets 包是旧 Loader 类的现代替代品。它是一个基于 promise 的资源管理解决方案,可以下载、缓存并将你的资源解析为你可以使用的内容。下载可以在后台同时进行,这意味着应用的启动时间更快,缓存可确保你永远不会下载相同的资源两次,并且可扩展的解析器系统允许你根据需要轻松扩展和自定义流程。

¥The Assets package is a modern replacement for the old Loader class. It is a promise-based resource management solution that will download, cache and parse your assets into something you can use. The downloads can be simultaneous and in the background, meaning faster startup times for your app, the cache ensures that you never download the same asset twice and the extensible parser system allows you to easily extend and customize the process to your needs.

入门

¥Getting started

Assets 严重依赖所有现代浏览器都支持的 JavaScript Promise,但是,如果你的目标浏览器是 不支持 promise,则应该考虑 填充它们

¥Assets relies heavily on JavaScript Promises that all modern browsers support, however, if your target browser doesn't support promises you should look into polyfilling them.

做出我们的第一个资源 Promise

¥Making our first Assets Promise

要快速使用 Assets 实例,只需调用 Assets.load 并传入资源即可。这将返回一个 promise,即解决后将产生你所寻求的值。在此示例中,我们将加载纹理,然后将其转换为精灵。

¥To quickly use the Assets instance, you just need to call Assets.load and pass in an asset. This will return a promise that when resolved will yield the value you seek. In this example, we will load a texture and then turn it into a sprite.

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://pixi.nodejs.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 相同,则返回的 Promise 也将相同。要在代码中显示它:

¥One very important thing to keep in mind while using Assets is that all requests are cached and if the URL is the same, the promise returned will also be the same. To show it in code:

promise1 = Assets.load('bunny.png')
promise2 = Assets.load('bunny.png')
// promise1 === promise2

开箱即用,可以加载以下资源类型,无需外部插件:

¥Out of the box, the following assets types can be loaded without the need for external plugins:

  • 纹理(avifwebppngjpggif

    ¥Textures (avif, webp, png, jpg, gif)

  • 精灵表 (json)

    ¥Sprite sheets (json)

  • 位图字体(xmlfnttxt

    ¥Bitmap fonts (xml, fnt, txt)

  • 网页字体(ttfwoffwoff2

    ¥Web fonts (ttf, woff, woff2)

  • Json 文件 (json)

    ¥Json files (json)

  • 文本文件 (txt)

    ¥Text files (txt)

通过创建额外的加载器解析器可以相当容易地添加更多类型。

¥More types can be added fairly easily by creating additional loader parsers.

关于已完成 promise 的警告

¥Warning about solved promises

下载资源后,它会作为 Promise 缓存在 Assets 实例内,如果你尝试再次下载它,你将获得对已解析 Promise 的引用。然而,Promise 处理程序 .then(...)/.catch(...)/.finally(...) 始终是异步的,这意味着即使 Promise 已经解析,.then(...)/.catch(...)/.finally(...) 下面的代码也会在其中的代码之前执行。看这个例子:

¥When an asset is downloaded, it is cached as a promise inside the Assets instance and if you try to download it again you will get a reference to the already resolved promise. However promise handlers .then(...)/.catch(...)/.finally(...) are always asynchronous, this means that even if a promise was already resolved the code below the .then(...)/.catch(...)/.finally(...) will execute before the code inside them. See this example:

console.log(1);
alreadyResolvedPromise.then(() => console.log(2));
console.log(3);

// Console output:
// 1
// 3
// 2

要详细了解为什么会发生这种情况,你需要了解 微任务,但是,使用异步函数应该可以缓解此问题。

¥To learn more about why this happens you will need to learn about Microtasks, however, using async functions should mitigate this problem.

使用 Async/Await

¥Using Async/Await

有一种更直观、更容易阅读的使用 Promise 的方法:async/await

¥There is a way to work with promises that is more intuitive and easier to read: async/await.

要使用它,我们首先需要创建一个函数/方法并将其标记为 async

¥To use it we first need to create a function/method and mark it as async.

async function test() {
// ...
}

该函数现在将返回值封装在 Promise 中,并允许我们在 Promise 之前使用 await 关键字来停止代码的执行,直到它被解析并为我们提供值。

¥This function now wraps the return value in a promise and allows us to use the await keyword before a promise to halt the execution of the code until it is resolved and gives us the value.

看这个例子:

¥See this example:

// 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://pixi.nodejs.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 变量现在不是一个 promise,而是等待此 promise 解决后产生的已解决纹理。

¥The texture variable now is not a promise but the resolved texture that resulted after waiting for this promise to resolve.

const texture = await Assets.load('examples/assets/bunny.png');

这使我们能够编写更具可读性的代码,而不会陷入回调地狱,并且可以更好地思考程序何时停止和屈服。

¥This allows us to write more readable code without falling into callback hell and to better think when our program halts and yields.

加载多个资源

¥Loading multiple assets

我们可以将资源添加到缓存中,然后使用 Assets.add(...) 来同时加载所有资源,然后使用你想要加载的所有键调用 Assets.load(...)。请参见以下示例:

¥We can add assets to the cache and then load them all simultaneously by using Assets.add(...) and then calling Assets.load(...) with all the keys you want to have loaded. See the following example:

// Append the application canvas to the document body
document.body.appendChild(app.canvas);
// Add the assets to load
Assets.add({ alias: 'flowerTop', src: 'https://pixi.nodejs.cn/assets/flowerTop.png' });
Assets.add({ alias: 'eggHead', src: 'https://pixi.nodejs.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(...) 手动添加。

¥However, if you want to take full advantage of @pixi/Assets you should use bundles. Bundles are just a way to group assets together and can be added manually by calling Assets.addBundle(...)/Assets.loadBundle(...).

  Assets.addBundle('animals', {
bunny: 'bunny.png',
chicken: 'chicken.png',
thumper: 'thumper.png',
});

const assets = await Assets.loadBundle('animals');

然而,处理打包包的最佳方法是使用清单并使用所述清单(或者更好的是指向它的 URL)调用 Assets.init({manifest})。将我们的资源拆分为与应用的屏幕或阶段相对应的打包包,以便在用户使用应用时在后台加载,而不是将它们锁定在单个整体加载屏幕中。

¥However, the best way to handle bundles is to use a manifest and call Assets.init({manifest}) with said manifest (or even better, an URL pointing to it). Splitting our assets into bundles that correspond to screens or stages of our app will come in handy for loading in the background while the user is using the app instead of locking them in a single monolithic loading screen.

{
"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 一次。

¥Beware that you can only call init once.

请记住,重复 URL 没有任何缺点,因为它们都会被缓存,因此如果你需要两个打包包中的相同资源,你可以复制请求,而无需任何额外费用!

¥Remember there is no downside in repeating URLs since they will all be cached, so if you need the same asset in two bundles you can duplicate the request without any extra cost!

后台加载

¥Background loading

旧的加载方法是在应用开始时使用 Loader 加载所有资源,但用户现在不太耐心,希望内容立即可用,因此实践正在朝着加载向用户展示所需的最低限度的要求的方向发展。 内容,当他们与之交互时,我们不断在后台加载以下内容。

¥The old approach to loading was to use Loader to load all your assets at the beginning of your app, but users are less patient now and want content to be instantly available so the practices are moving towards loading the bare minimum needed to show the user some content and, while they are interacting with that, we keep loading the following content in the background.

幸运的是,Assets 为我们提供了一个系统,允许我们在后台加载所有内容,如果我们现在需要一些资源,请将它们放到队列顶部,这样我们就可以最大限度地减少加载时间。

¥Luckily, Assets has us covered with a system that allows us to load everything in the background and in case we need some assets right now, bump them to the top of the queue so we can minimize loading times.

为了实现这一点,我们有方法 Assets.backgroundLoad(...)Assets.backgroundLoadBundle(...),它们将在后台被动地开始加载这些资源。因此,当你最终加载它们时,你将得到一个立即解析为加载的资源的 promise。

¥To achieve this, we have the methods Assets.backgroundLoad(...) and Assets.backgroundLoadBundle(...) that will passively begin to load these assets in the background. So when you finally come to loading them you will get a promise that resolves to the loaded assets immediately.

当你最终需要资源展示时,你调用通常的 Assets.load(...)Assets.loadBundle(...),你就会得到相应的 promise。

¥When you finally need the assets to show, you call the usual Assets.load(...) or Assets.loadBundle(...) and you will get the corresponding promise.

执行此操作的最佳方法是使用打包包,请参阅以下示例:

¥The best way to do this is using bundles, see the following example:

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://pixi.nodejs.cn/assets/flowerTop.png',
},
],
},
{
name: 'game-screen',
assets: [
{
alias: 'eggHead',
src: 'https://pixi.nodejs.cn/assets/eggHead.png',
},
],
},
],
};

await Assets.init({ manifest: manifestExample });

// Bundles can be loaded in the background too!
Assets.backgroundLoadBundle(['load-screen', 'game-screen']);
}

init();

我们为游戏将拥有的每个屏幕创建一个打包包,并将它们全部设置为在应用开头开始下载。如果用户在我们的应用中进展得足够慢,那么他们永远不会在第一个屏幕之后看到加载屏幕!

¥We create one bundle for each screen our game will have and set them all to start downloading at the beginning of our app. If the user progresses slowly enough in our app then they should never get to see a loading screen after the first one!