HTML5 Canvas 游戏第 2部分的现代化: 脱机拖放和文件 api

分享于 

17分钟阅读

Web开发

  繁體

在10天内开发 Windows 8应用程序。

现代浏览器如 IE 10的HTML5特性使得整个web应用和游戏场景成为可能。 这两部分文章展示了我如何使用这些新特性来使我的最后一款HTML5游戏 modernize HTML5 Platformer。

在本文的第1部分中,我介绍了如何使用CSS3的转换。转换和网格布局进行HTML5游戏。 在第1部分中,我将向你展示如何使用。Drag-and-Drop和文件 api 来实现一些有趣的新概念。

在离线模式下玩游戏

如果你的设备目前连接到互联网,我的游戏的原始版本。 因这里,如果你想在火车上玩我的游戏,在出租车上,或者没有网络连接,你没有权利。 这太糟糕了,因为我的游戏中没有任何东西需要一个"实时"连接,一旦所有资源下载。 幸运的是,离线api为HTML5提供了一个解决方案。

步骤 1: 选择要缓存的资源

实际上,告诉浏览器你希望缓存哪些资源可以脱机使用。 但在继续之前,我建议你阅读这两个文章:

我的游戏生成了一个名为 platformer.cache的文件,它看起来像这样:

[__strong__]CACHE MANIFEST
# Version 1.5CACHE: 
index.html
modernplatformer.css
img/MonsterA.png
.. up to.. 
img/MonsterD.png
img/Player.png
img/offlinelogoblack.png
img/Backgrounds/Layer0_0.png
.. up to.. 
img/Backgrounds/Layer2_2.png
img/Tiles/BlockA0.png
.. up to.. 
img/Tiles/BlockA6.png
img/Tiles/BlockB0.png
img/Tiles/BlockB1.png
img/Tiles/Gem.png
img/Tiles/Exit.png
img/Tiles/Platform.png
overlays/you_died.png
overlays/you_lose.png
overlays/you_win.png
src/dragDropLogic.js
src/main.js
src/ModernizrCSS3.js
src/easeljs/utils/UID.js
src/easeljs/geom/Matrix2D.js
src/easeljs/events/MouseEvent.js
src/easeljs/utils/SpriteSheetUtils.js
src/easeljs/display/SpriteSheet.js
src/easeljs/display/Shadow.js
src/easeljs/display/DisplayObject.js
src/easeljs/display/Container.js
src/easeljs/display/Stage.js
src/easeljs/display/Bitmap.js
src/easeljs/display/BitmapAnimation.js
src/easeljs/display/Text.js
src/easeljs/utils/Ticker.js
src/easeljs/geom/Rectangle.js
src/easeljs/geom/Point.js
src/easeljs/XNARectangle.js
src/easeljs/PlatformerHelper.js
src/easeljs/ContentManager.js
src/easeljs/Tile.js
src/easeljs/Gem.js
src/easeljs/Enemy.js
src/easeljs/Player.js
src/easeljs/Level.js
src/easeljs/PlatformerGame.jsNETWORK:*

我插入了所有包含我的精灵。background 层和覆盖的PNG文件;EaselJS框架所必需的JS文件;以及我自己的游戏逻辑和主要的HTML和CSS文件。 然后,我只是想在我的主HTML页面中使用这个清单文件。 在本例中,它是" index.html":

<!DOCTYPEhtml><htmlmanifest="platformer.cache"><head><title>HTML5 Platformer Game</title>//... </head></html>

请注意,你的清单文件应该作为"文本/缓存清单"。 在游戏中,我添加了一个映射到"文本/缓存清单"的". cache"内容类型,因为它存储在 Windows Azure的blob存储中。

请注意,这里规范不允许增量更改。 即使你的文件中只有一个已经更改,你需要强制完整地下载浏览器才能更新新版本。 但是,浏览器将检测到对清单文件的任何更改,浏览器将下载指定的所有资源,并将它的保存到清单中。 更改可以是版本号。日期。或者在文件开头使用注释("版本 1.5"在上面的示例中) 对你工作的guid。

步骤 2: 修改用于加载级别的逻辑

最初的代码版本通过对web服务器的一个XHR调用下载每个级别的 我需要改变它以使我的游戏在离线模式下运行。 另外,我想向用户指出他目前离线模式下玩,添加"官方" HTML5关联 logo inside gaming游戏画布。

让我们通过改变来使它的发生。 用户第一次启动游戏时,所有级别都将被下载到本地存储数据库中,以为单位。 这被广泛支持( 自 IE8 ) 并且非常容易使用。 最重要的是,它在离线模式下可用。

下面是我添加的代码::

PlatformerGame.prototype.DownloadAllLevels = function () {
 // Searching where we are currently hostedvar levelsUrl = window.location.href.replace('index.html', '') + "levels/";
 var that = this;
 for (var i = 0; i < numberOfLevels; i++) {
 try {
 var request = new XMLHttpRequest();
 request.open('GET', levelsUrl + i + ".txt", true);
 request.onreadystatechange = makeStoreCallback(i, request, that);
 request.send(null);
 }
 catch (e) {
 // Loading the hard coded error level to have at least something to play with//console.log("Error in XHR. Are you offline?"); if (!window.localStorage["platformer_level_0"]) {
 window.localStorage["platformer_level_0"] = hardcodedErrorTextLevel;
 }
 }
 }
};// Closure of the index function makeStoreCallback(index, request, that) {
 return function () {
 storeLevel(index, request, that);
 }
}
function storeLevel(index, request, that) {
 if (request.readyState == 4) {
 // If everything was OKif (request.status == 200) {
 // storing the level in the local storage// with the key"platformer_level_{index} window.localStorage["platformer_level_" + index] = request.responseText.replace(/[nrt]/g, '');
 numberOfLevelDownloaded++;
 }
 else {
 // Loading a hard coded level in case of error window.localStorage["platformer_level_" + index] = hardcodedErrorTextLevel;
 }
 if (numberOfLevelDownloaded === numberOfLevels) {
 that.LoadNextLevel();
 }
 }
}

PlatformerGame 构造函数中的所有级别都将异步下载。 下载所有级别后( numberOfLevelDownloaded === numberOfLevels ),一级负荷。 以下是新函数的代码:

// Loading the next level contained into the localStorage in platformer_level_{index}PlatformerGame.prototype.LoadNextLevel = function () {
 this.loadNextLevel = false;
 // Setting back the initialRotation class will trigger the transitionthis.platformerGameStage.canvas.className = "initialRotation";
 this.levelIndex = (this.levelIndex + 1) % numberOfLevels;
 var newTextLevel = window.localStorage["platformer_level_" + this.levelIndex];
 this.LoadThisTextLevel(newTextLevel);
};

代码的开头处理了CSS3转换,如前一篇文章中所述。 游戏将通过适当的键访问本地存储,检索以前下载的内容。

收费 3: 在线/离线检查并在离线模式下显示 logo 时显示

确认游戏在离线模式下运行时需要两个测试。 第一个是浏览器是否已经实现了离线/在线实时事件,如大多数现代浏览器所做的那样。 如果浏览器表示用户已经离线,则确认该用户,并且游戏应立即 switch 到脱机逻辑。 通常,这个简单的检查是不够的。 浏览器可以能说它是在线的,但它不知道web服务器是否仍在联机或者不在线。 因此,你需要通过使用简单的XHR ping服务器来进行第二次检查。

这是我这两个检查的代码:

PlatformerGame.prototype.CheckIfOnline = function () {
 if (!navigator.onLine) returnfalse;
 var levelsUrl = window.location.href.replace('index.html', '') + "levels/";
 try {
 var request = new XMLHttpRequest();
 request.open('GET', levelsUrl + "0.txt", false);
 request.send(null);
 }
 catch (e) {
 returnfalse;
 }
 if (request.status!== 200)
 returnfalse;
 elsereturntrue;
};

我的测试是尝试下载第一级。 如果失败,它将切换到代码的脱机部分。 下面是在 PlatformerGame.js的构造函数部分中启动的代码:

PlatformerGame.IsOnline = this.CheckIfOnline();// If we're online, we're downloading/updating all the levels// from the webserverif (PlatformerGame.IsOnline) {
 this.DownloadAllLevels();
}// If we're offline, we're loading the first level// from the cache handled by the local storageelse {
 this.LoadNextLevel();
}

下面是在 CreateAndAddRandomBackground Level.js 中显示离线 logo的代码,在中

if (!PlatformerGame.IsOnline) {
 offlineLogo.x = 710;
 offlineLogo.y = -1;
 offlineLogo.scaleX = 0.5;
 offlineLogo.scaleY = 0.5;
 this.levelStage.addChild(offlineLogo);
}

实现这些更改后,在没有网络连接的情况下,我的游戏看起来就像这样:

脱机 logo 仅显示在帧速率之前,指示游戏当前纯粹脱机运行。

步骤 4: 有条件地下载MP3或者OGG文件,并将它们存储为

这是我尚未实现的,但是我想与你共享这个概念,以便你可以自己实现它。

你可能已经注意到,我没有在步骤 1的清单文件中包含我的游戏效果和音乐。 我写这个HTML5游戏时,第一个目标是兼容最大量的浏览器。 要完成这个任务,我有两个版本的声音: 用于 IE 和Safari的,并为 Chrome。Firefox 和 Opera 收费。 下载内容下载管理器只下载当前浏览器启动我的游戏所支持的编解码器类型。 因为我不需要下载OGG的文件,所以不需要下载OGG版本的文件,而且不需要下载MP3版的MP3。

清单文件的问题是,你不能根据当前对浏览器的支持来有条件地指出要加载哪个资源。 我已经提出了三个解决这里限制的解决方案:

  • 将所有文件版本都放在清单文件中,以下载两个版本。 这是非常简单的实现和工作很好,但用户将下载一些不会被某些浏览器使用的文件。
  • 通过嗅探浏览器代理猜测支持的编解码器,以为代价构建服务器端动态清单 这绝对是一个错误的做法 !
  • 在content对象and使用客户端特性检测编解码器支持,然后下载 IndexedDB 或者本地存储中适当的文件格式以离线使用。

我相信 3rd 解决方案是最好的,但是你需要注意一些事情来使它工作:

  • 如果使用本地存储,则需要以base64编码文件,如果有太大的文件,则可能会进入配额限制。
  • 如果你使用 IndexedDB,你可以存储文件的base64编码版本,或者将它们存储为 blob。

这个收费方法绝对是智能和高效的解决方案,但它需要一个非常up-to-date的浏览器,比如 IE10 ( PP5 ) 或者 Firefox ( 11 )的最新版本。 如果你对这个想法感兴趣,请在我们的IE 测试驱动器站点查看我们的Facebook辅助演示:

你将在本文中找到关于这里演示的更多详细信息: IndexedDB为IE10和地铁风格应用更新。

在本文提供的游戏版本中,我决定缓存所有格式的( 解决方案 1 )。 我可以在以后的文章中通过实现IndexedDB缓存来更新。 保持调谐 !

Drag-and-drop和文件 api

这里有一个有趣的新特性,它利用了新的Drag-and-Drop和文件 api。 用户可以使用他喜欢的文本编辑器创建/编辑一个级别,然后直接拖拽它从文件浏览器中拖放到HTML5游戏中,然后播放它。

我不会深入讨论拖放的细节,因为这篇文章已经非常详细地介绍了: 在 IE10 中拖放,这说明了如何构建 Magnetic。 我建议先阅读文章以完全理解代码 below。

我在游戏中创建了包含以下代码的dragDropLogic.js 文件:

(function () {
 "use strict";
 var DragDropLogic = DragDropLogic || {};
 var _elementToMonitor;
 var _platformerGameInstance;
 // We need the canvas to monitor its drag&drop events// and the platformer game instance to trigger the loadnextlevel function DragDropLogic.monitorElement = function (elementToMonitor, platformerGameInstance) {
 _elementToMonitor = elementToMonitor;
 _platformerGameInstance = platformerGameInstance;
 _elementToMonitor.addEventListener("dragenter", DragDropLogic.drag, false);
 _elementToMonitor.addEventListener("dragover", DragDropLogic.drag, false);
 _elementToMonitor.addEventListener("drop", DragDropLogic.drop, false);
 };
 // We don't need to do specific actions// enter & over, we're only interested in drop DragDropLogic.drag = function (e) {
 e.stopPropagation();
 e.preventDefault();
 };
 DragDropLogic.drop = function (e) {
 e.stopPropagation();
 e.preventDefault();
 var dt = e.dataTransfer;
 var files = dt.files;
 // Taking only the first dropped filevar firstFileDropped = files[0];
 // Basic check of the type of file droppedif (firstFileDropped.type.indexOf("text") == 0) {
 var reader = new FileReader();
 // Callback function reader.onload = function (e) {
 // get file contentvar text = e.target.result;
 var textLevel = text.replace(/[snrt]/g, '');
 // Warning, there is no real check on the consistency// of the file.  _platformerGameInstance.LoadThisTextLevel(textLevel);
 }
 // Asynchronous read reader.readAsText(firstFileDropped);
 }
 };
 window.DragDropLogic = DragDropLogic;
})();

这里代码称为 startGame 函数中的inside main.js:

// Callback function once everything has been downloadedfunction startGame() {
 platformerGame = new PlatformerGame(stage, contentManager, 800, 480, window.innerWidth, window.innerHeight);
 window.addEventListener("resize", OnResizeCalled, false);
 OnResizeCalled();
 DragDropLogic.monitorElement(canvas, platformerGame);
 platformerGame.StartGame();
}

如此 而已, 岂 有 他 哉! 例如将这里文本 block 复制/粘贴到新的". txt"file:

.................... 
.................... 
.................... 
.1.................. 
######.........##### 
.................... 
.........###........ 
.................... 
.G.G.GGG.G.G.G...... 
.GGG..G..GGG.G...... 
.G.G..G..G.G.GGG.... 
.................... 
.................... 
.X................C. 
####################

把它拖放到我的游戏中。 新的水平将像魔法一样被载入 !

演示和源代码

如果你想看看本文中实现的所有功能的IE10演示,请查看这个短视频:

收费视频:,HTML5视频播放器,由 VideoJS

你还可以在IE10或者你喜爱的浏览器中使用这里演示文稿: 现代 HTML5

由于你已经阅读了整篇文章,请在这里享受完整的源代码: HTML5现代Platformer源代码


文件  API  PAR  模式  GAM  CAN  
相关文章