HTML5 游戏的简单资源管理
HTML5 提供了许多有用的 API,用于在浏览器中构建现代、响应迅速且功能强大的 Web 应用程序。这很棒,但你真的想构建和玩游戏!幸运的是,HTML5也开创了游戏开发的新时代,它使用 Canvas 等 API 和强大的 JavaScript 引擎将游戏直接传送到您的浏览器,而无需插件。
本文将引导您为 HTML5 游戏构建一个简单的资产管理组件。如果没有资源管理器,您的游戏将很难补偿未知的下载时间和异步图像加载。请继续查看适用于您的 HTML5 游戏的简单资源管理器示例。
问题所在
HTML5 游戏不能假设其资产(如图像或音频)将位于玩家的本地计算机上,因为 HTML5 游戏意味着在 Web 浏览器中播放,并通过 HTTP 下载资产。由于涉及网络,浏览器不确定何时下载和提供游戏的资产。
在 Web 浏览器中以编程方式加载图像的基本方法是以下代码:
var image = new Image();
image.addEventListener(“success”, function(e) {
// do stuff with the image
});
image.src = "/some/image.png";
现在想象一下,在游戏启动时需要加载和显示一百张图像。您如何知道所有 100 张图像何时准备就绪?它们都成功加载了吗?游戏应该什么时候真正开始?
解决方案
让资产管理者处理资产排队,并在一切准备就绪后向游戏报告。资产管理器概括了通过网络加载资产的逻辑,并提供了一种检查状态的简单方法。
我们的简单资产管理人有以下要求:
- 排队下载
- 开始下载
- 跟踪成功和失败
- 一切完成后发出信号
- 轻松检索资产
排队
第一个要求是将下载排队。此设计允许您声明所需的资产,而无需实际下载它们。例如,如果要在配置文件中声明游戏关卡的所有资产,这会很有用。
构造函数和队列的代码如下所示:
function AssetManager() {
this.downloadQueue = [];
}
AssetManager.prototype.queueDownload = function(path) {
this.downloadQueue.push(path);
}
开始下载
将所有要下载的资产排队后,您可以要求资源管理器开始下载所有内容。
幸运的是,Web 浏览器可以并行下载 - 通常每个主机最多 4 个连接。加快资产下载速度的一种方法是使用一系列域名进行资产托管。例如,尝试使用 assets1.example.com、assets2.example.com、assets3.example.com 等,而不是提供 assets.example.com 中的所有内容。即使这些域名中的每一个都只是同一 Web 服务器的 CNAME,Web 浏览器也会将它们视为单独的服务器,并增加用于资产下载的连接数。有关此技术的详细信息,请参阅 跨域拆分组件 中的 加快网站速度的最佳做法 。
我们的下载初始化方法称为 downloadAll()
,我们会随着时间的推移建立它。现在,这是开始下载的第一个逻辑。
AssetManager.prototype.downloadAll = function() {
for (var i = 0; i < this.downloadQueue.length; i++) {
var path = this.downloadQueue[i];
var img = new Image();
var that = this;
img.addEventListener("load", function() {
// coming soon
}, false);
img.src = path;
}
}
正如您在上面的代码中看到的, downloadAll()
只需遍历 downloadQueue 并创建一个新的 Image 对象。添加 load 事件的事件侦听器并设置映像的 src,从而触发实际下载。
使用此方法,您可以开始下载。
跟踪成功和失败
另一个要求是跟踪成功和失败,因为不幸的是,并非一切都能完美地进行。到目前为止,代码仅跟踪成功下载的资产。通过为错误事件添加事件侦听器,您将能够捕获成功和失败方案。
AssetManager.prototype.downloadAll = function(downloadCallback) {
for (var i = 0; i < this.downloadQueue.length; i++) {
var path = this.downloadQueue[i];
var img = new Image();
var that = this;
img.addEventListener("load", function() {
// coming soon
}, false);
img.addEventListener("error", function() {
// coming soon
}, false);
img.src = path;
}
}
我们的资产管理人需要知道我们遇到了多少成功和失败,否则它永远不会知道游戏何时可以开始。
首先,我们将计数器添加到构造函数中的对象,现在如下所示:
function AssetManager() {
this.successCount = 0;
this.errorCount = 0;
this.downloadQueue = [];
}
接下来,递增事件侦听器中的计数器,现在如下所示:
img.addEventListener("load", function() {
that.successCount += 1;
}, false);
img.addEventListener("error", function() {
that.errorCount += 1;
}, false);
资产管理器现在正在跟踪成功加载和失败的资产。
完成后发出信号
在游戏将其资产排队等待下载并要求资源管理器下载所有资产后,需要告知游戏何时下载所有资产。与游戏一遍又一遍地询问资产是否已下载不同,资产管理者可以向游戏发出信号。
资产管理者需要首先知道每项资产何时完成。我们现在将添加一个 isDone 方法:
AssetManager.prototype.isDone = function() {
return (this.downloadQueue.length == this.successCount + this.errorCount);
}
通过将 successCount + errorCount 与 downloadQueue 的大小进行比较,资产管理器知道每个资产是否成功完成或出现某种错误。
当然,知道它是否完成只是成功的一半;资产管理者也需要检查这种方法。我们将在两个事件处理程序中添加此检查,如下面的代码所示:
img.addEventListener("load", function() {
console.log(this.src + ' is loaded');
that.successCount += 1;
if (that.isDone()) {
// ???
}
}, false);
img.addEventListener("error", function() {
that.errorCount += 1;
if (that.isDone()) {
// ???
}
}, false);
计数器递增后,我们将查看这是否是队列中的最后一个资产。如果资产管理者确实完成了下载,我们到底应该怎么做?
如果资产管理者下载完所有资产,我们当然会调用回调方法!让我们改变 downloadAll()
并为回调添加参数:
AssetManager.prototype.downloadAll = function(downloadCallback) {
...
我们将在事件侦听器中调用 downloadCallback 方法:
img.addEventListener("load", function() {
that.successCount += 1;
if (that.isDone()) {
downloadCallback();
}
}, false);
img.addEventListener("error", function() {
that.errorCount += 1;
if (that.isDone()) {
downloadCallback();
}
}, false);
资产管理者终于准备好满足最后一个要求了。
轻松检索资产
一旦游戏收到可以启动的信号,游戏将开始渲染图像。资产管理者不仅负责下载和跟踪资产,还负责将其提供给游戏。
我们的最终要求意味着某种 getAsset 方法,所以我们现在添加它:
AssetManager.prototype.getAsset = function(path) {
return this.cache[path];
}
此缓存对象在构造函数中初始化,现在如下所示:
function AssetManager() {
this.successCount = 0;
this.errorCount = 0;
this.cache = {};
this.downloadQueue = [];
}
缓存在 downloadAll()
,如下所示:
AssetManager.prototype.downloadAll = function(downloadCallback) {
...
img.addEventListener("error", function() {
that.errorCount += 1;
if (that.isDone()) {
downloadCallback();
}
}, false);
img.src = path;
this.cache[path] = img;
}
}
奖励:错误修复
你发现这个错误了吗?如上所述,仅当触发加载或错误事件时,才会调用 isDone 方法。但是,如果资产管理者没有任何资产排队等待下载怎么办?isDone 方法永远不会触发,游戏也永远不会开始。
可以通过将以下代码添加到 downloadAll()
:
AssetManager.prototype.downloadAll = function(downloadCallback) {
if (this.downloadQueue.length === 0) {
downloadCallback();
}
...
如果没有资产排队,则立即调用回调。错误已修复!
示例用法
在您的HTML5游戏中使用此资源管理器非常简单。以下是使用该库的最基本方法:
var ASSET_MANAGER = new AssetManager();
ASSET_MANAGER.queueDownload('img/earth.png');
ASSET_MANAGER.downloadAll(function() {
var sprite = ASSET_MANAGER.getAsset('img/earth.png');
ctx.drawImage(sprite, x - sprite.width/2, y - sprite.height/2);
});
上面的代码说明:
- 创建新的资产管理器
- 排队待下载的资产
- 开始下载
downloadAll()
- 通过调用回调函数在资产准备就绪时发出信号
- 使用 检索资产
getAsset()
需要改进的领域
毫无疑问,当你构建你的游戏时,你会超越这个简单的资产管理,尽管我希望它提供了一个基本的开始。未来的功能可能包括:
- 指示哪个资产出错
- 指示进度的回调
- 从文件系统 API 检索资产
请在下面的评论中发布改进、复刻和代码链接。
完整来源
这个资产管理器的来源,以及它从中抽象出来的游戏,是Apache许可证下的开源软件,可以在 Bad Aliens GitHub帐户 中找到。 坏外星人游戏 可以在与HTML5兼容的浏览器中播放。这个游戏是我的Google IO演讲的主题,题为Super Browser 2 Turbo HD Remix:HTML5游戏开发简介( 幻灯片 , 视频 )。
总结
大多数游戏都有某种资产管理器,但HTML5游戏需要一个资产管理器,通过网络加载资产并处理故障。本文概述了一个简单的资源管理器,它应该易于您使用和适应您的下一个 HTML5 游戏。玩得开心,请在下面的评论中告诉我们您的想法。谢谢!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论