在本文中,我想分享我在3D WebGL Babylon.JS游戏引擎中开发对IndexedDB的支持时所学的一切。 实际上,从1.4.x开始,我们现在支持存储和加载包含3d网格及其.PNG或.JPG纹理的JSON场景,作为IndexedDB中的斑点。
本文基于我自己在该主题上的经验而构建。 它基于我解决IDB时遇到的各种问题的方式。 然后,您会找到一些有关在使用IndexedDB时必须注意的事项的解释和提示 。 我还将分享我们在3d WebGL引擎中使用它的方式和原因。 尽管如此,本文对于总体上看IndexedDB的任何人还是有帮助的。 3d游戏将仅用作其用法的说明。
IndexedDB简介
IndexedDB是使用键/值机制的非关系数据库 。 这是一个noSQL DB。 您可以将其视为浏览器处理的第三代存储。 第一个是cookie,第二个是本地存储。
这是W3C规范,目前在候选推荐中 。 它由大多数现代浏览器实现:IE10 +,Chrome / Opera和Firefox。 更好的是,自IE10,Firefox 16和Chrome 24 / Opera 15起,该规格在无前缀版本中受支持。看起来已经可以投入生产了! 这就是我们今天在我们的网站上使用它的原因: http : //www.babylonjs.com
我不会介绍IndexedDB的基础知识,因为Web上有很好的资源。 但是,我花了很多时间来确定最新的文档和详细说明的教程。 的确,随着规范的发展几年,您在网络上找到的大多数文章都已被弃用。
如果您想避免浪费时间在这些不赞成使用的内容上,请阅读以下4篇推荐文章:
1 – W3C规范本身: http : //www.w3.org/TR/IndexedDB/ 。 它确实包含所有内容,并且相对容易阅读。 我经常阅读规范以真正了解它如何解决我的一些问题。 有时,我们只是忘记了W3C规范可能是最好的文档。 ;-)
2 – Raymon Camden 使用IndexedDB 。 这是一本非常新的书,对于初学者来说,它的讲解很好,非常完美。 我的文章可能是对此文章的补充,因为我将图像存储为本文未涵盖的Blob。
3 –我们的MSDN上的IndexedDB 。 它包含一些有趣的细节和一个大型教程。
4 –在MDN上使用IndexedDB 。 一如既往的关于MDN的良好文档。
因此,如果您还不了解IndexedDB,请至少阅读第二个链接。
之后,根据我的经验,让我分享您应该记住的最大警告: 真正了解IndexedDB是完全异步的并且基于事务 。 您需要等待异步读/写操作完成,并且还需要等待异步事务完成,才能确保代码中的一切都正常。 我将在下面用一些小图来说明。
为什么在我们的游戏场景中使用IndexedDB?
我已经开始考虑在暑假期间使用IndexedDB。 我当时在家中使用了难以置信的2MB ADSL线路,每次需要从我们的网站重新加载场景时,我都会感到沮丧。 某些场景可能需要5分钟以上的时间才能加载。 然后,我对自己想知道:“ 由于我已经下载了所有资产一次,为什么还要重新下载它们? ”
您可能会争辩说这是浏览器缓存的工作。 这是对的。 大多数时候,浏览器会完美地完成工作。 但是在某些情况下,缓存将无效或被删除 :已达到缓存的配额,用户正在删除其Web内容缓存或仅仅是由于浏览器使用的启发式方法。 然后,您的游戏内容可能会因此遭受损失,因为它默认情况下会与从网络下载的所有其他内容一起存在。
我想要更好的游戏体验。 作为游戏玩家,我可以在游戏首次发布时下载资产。 但是我不想浪费时间重新下载,因为我的浏览器决定清除其某些缓存。 玩游戏时,我想立即玩。 通过将游戏数据隔离到IndexedDB中,我们几乎没有机会陷入各种缓存清除方案。 然后,我们获得了更大的独立性。
此外,我们最近在BabylonJS中交付了增量加载器 。 这意味着场景将立即加载,并且我们将根据相机当前所处的位置按需加载资源。 这种方法的一个小问题是,资源(网格的几何形状和纹理)将首先从Web服务器下载并注入3d引擎中。 我们将遭受网络延迟的困扰。 增量几何形状不会立即显示,并且会在玩家移动相机后的几秒钟内突然出现。 使用IndexedDB方法,我们可以在后台将数据库中的资源预加载,并几乎通过增量加载器立即加载它们。 然后,我们将消除网络延迟问题。 这仍然是我们需要做的事情,但是我们现在拥有将其构建为将来版本的所有内容。
最后,能够将资产存储在IndexedDB中启用了离线方案 。 现在,您可以想象从网络上加载游戏,并且在没有任何连接的情况下可以完美运行! 您只需要将HTML5应用程序缓存API与IndexedDB结合使用 。
为了说明这一点,请单击下面的图片导航至在线演示:
加载“ Heart ”场景,按返回按钮,然后加载“ Omega Crusher ”场景。 这样,您将两个场景都保存在IndexedDB中。 现在,尝试关闭网络适配器以使其脱机。 即使根本没有任何网络连接,您也应该能够导航到主页并启动两个场景!
我将在本文的最后一部分中解释如何构建这样的演示。
了解IndexedDB的执行工作流程和处理异常
首先,请注意,我为Babylon.JS编写的所有代码都可以在GitHub上找到: babylon.database.js 。 请随意看一下,以更好地理解以下说明。
此外,我的第一个建议是: 在W3C规范描述的所有可能的事件中进行注册,并在开发过程中将一些简单的console.log()放入其中,以了解执行管道。
打开数据库
让我们从回顾打开索引数据库时将发生/可能发生的事情开始。
我犯的第一个错误是认为onupgradeneed事件没有跟随onsuccess事件。 我认为只有在数据库已经存在并成功打开数据库的情况下,才会引发成功。 因此,我将成功回调放在两个事件处理程序中。 然后,它在逻辑上被触发了两次,但我期望它仅被触发一次。 总之,仅在onsuccess事件处理程序内调用最终的回调函数。
此外,如果在提示时用户拒绝访问数据库,则可以从onupgradeneed变为onerror 。 例如,以下是Internet Explorer中显示的请求:
如果用户单击“ 不适用于此站点 ”,您将陷入onerror处理程序中。
您可以通过阅读BABYLON来检查我的代码。 数据库 。 原型 。 GitHub上的 openAsync()函数。
在所有浏览器中处理图像Blob存储
为了更好地理解这一部分,您可以检查GitHub上 BABYLON.Database.prototype._saveImageIntoDBAsync()函数中包含的代码。
也请看一下这篇文章:Robert Nyman 在IndexedDB中存储图像和文件 。 有点过时了,但是很好地解释了如何将图像作为Blob类型存储在IDB中。
我函数的全局概念是将3d网格的纹理存储在IndexedDB中。 为此,我首先使用XHR2下载它们,并要求响应类型为blob 。 然后,我基本上使用与上述文章相同的方法。
但是,在测试这种方法时,我发现IE10 +和Firefox很好地支持将图像作为blob存储在IndexedDB中,但还不支持Chrome。 如果您尝试在其数据库中保存Blob结构,Chrome就会引发DataCloneError 。
为了在不进行UA嗅探的情况下覆盖Chrome的特定情况(这很糟糕!),我正在保护保存操作。 如果失败并显示错误代码25,则表明UA不支持存储Blob。 因为我已经通过XHR下载了数据,所以我只是用createObjectURL填充HTML图像元素。 但是对于将来的调用,我将标志isUASupportingBlobStorage设置为false,以指示IDB中的缓存图像不适用于此浏览器。
我当时正在考虑通过使用一些使用FileSystem API的现有polyfill或通过将图像编码为base64进行存储来更好地覆盖Chrome外壳。 然后,我发现此stackoverflow线程讨论了相同的问题: 为脱机Web应用程序(客户端存储数据库)存储图像数据 。 但是,由于目前正在打开一个错误以在将来的Chrome版本中实现该问题 : 问题108012:IndexedDB应该支持存储File / Blob对象 ,而且似乎很快就会发货,所以我决定让Chrome恢复为默认图像缓存系统。
最后,您会注意到,以一般方式,如果发生错误(XHR错误或其他错误),我将使用经典方式通过使用HTML image元素及其src属性来加载图像。 这样,我将最大程度地增加在保存过程中发生的任何情况下加载纹理的机会。
已达到处理配额
这个值得一点点的模式来了解发生了什么! 它将向您确认为什么理解IndexedDB基于事务非常重要。
首先,让我们讨论一下浏览器中的默认配额。 默认情况下, IE10 +允许您在请求用户超过此限制之前存储10 MB。 您可以在选项中更改此值。 然后,每个域的最终最大限制为250 MB,并且您无法更改此值。 因此,这里有2种可能的情况可以达到配额,我们需要在代码中进行处理。
当您达到50 MB的第一配额限制,然后没有最大配额时, Firefox会警告您。 对于Chrome来说 ,答案并不那么简单,但是您可以在这里找到处理配额的方法: https : //developers.google.com/chrome/whitepapers/storage#temporary
现在,要了解如何正确处理配额,让我们回顾一个简单的案例。 如果您导航到我们的网站: http : //www.babylonjs.com ,您会注意到有几个场景可以测试。 其中之一名为FLAT 2009 。
这个场景有一个名为Flat2009.babylon的JSON文件, 大小为29 MB 。 场景文件当然是引擎下载的第一个文件。 这样,您第一次导航到我们的网站时,就有可能首先尝试该场景。 到底会发生什么?
它将通过XHR请求加载JSON场景,然后尝试将其保存到IndexedDB中。 让我们以IE11作为浏览器。 由于它的默认第一警告限制为10 MB ,因此仅下载此唯一场景便已达到此限制。 我的第一个猜测是写入请求操作应失败,因为29 MB> 10 MB。 好吧,这并不是正在发生的事情。 为了更好地理解,请查看下图:
代码的第一行是创建事务 。 通过此事务,我们将启动写入请求,以将新下载的新场景放入“ 场景 ”存储中。 实际上,名为“ addRequest ”的请求将首先成功。 实际上,从逻辑上讲,您的浏览器应该能够将29 MB的场景写入DB。 但是,达到配额后,浏览器将提示用户询问他是否允许浏览器超过默认配额。 如果用户拒绝,事务将中止,并且文件将从数据库中删除。
同样,结论与以前相同。 你最终成功处理程序必须从交易的onComplete处理程序 ,而不是从请求的onSuccess处理被调用 。
您可以通过阅读GitHub上的BABYLON.Database.prototype._saveSceneIntoDBAsync()的代码来查看此逻辑。 最重要的部分在这里:
// Open a transaction to the database var transaction = that.db.transaction([ "scenes" ], "readwrite" ); // the transaction could abort because of a QuotaExceededError error transaction.onabort = function (event) { try { if (event.srcElement.error.name === "QuotaExceededError" ) { that.hasReachedQuota = true ; } } catch (ex) { } callback(sceneText); ; transaction.oncomplete = function (event) { callback(sceneText); ;
您需要测试“ QuotaExceededError ”,以确保由于配额导致事务已中止。 就我而言,我正在设置一个hasReachedQuota标志,因为不需要在数据库中尝试进一步的写操作,这将不再起作用。
我在开发过程中学习和使用的一些技巧
让我在这里分享我在开发过程中一直在使用的一些技巧,这些技巧也可能对您有用。
如何在各种浏览器中清理/删除索引数据库
您可能需要删除测试期间创建的数据库,以从零开始重新启动。
IE浏览器
转到“ Internet选项 ”->“ 设置 ”->“ 缓存和数据库 ”,然后选择要删除的域。
铬
导航至chrome:// settings,然后转到“ 高级设置 ”。 单击“ 清除浏览数据... ”按钮。 最后,按以下形式单击“ 清除浏览数据 ”按钮:
或者,您可以直接在此处删除与您的域名关联的文件夹: %AppData%\ Local \ Google \ Chrome \ User Data \ Default \ IndexedDB
火狐浏览器
您需要进入以下文件夹: %AppData%\ Roaming \ Mozilla \ Firefox \ Profiles \您的配置文件ID \ indexedDB,然后删除与您的域名关联的文件夹。
您知道InPrivate /隐身浏览吗?
如果您使用浏览器的InPrivate或Incognito模式浏览网站,则IndexedDB将被禁用(顺便说一句,如localStorage和cookie)。 window.indexedDB将是未定义的 。 使用/不使用IndexedDB进行某些测试可能会很有用。 例如,对于测试支持WebGL且未启用IndexedDB的浏览器对我来说很有用。
如何检查资源是否真正从数据库加载
在测试期间,我一直想知道我的数据库逻辑是否工作正常,以及资源是否真的是从数据库而不是直接从Web加载的。 我发现一种非常简单的检查方法:使用IE11的F12开发栏。 自己测试:
–使用IE11,导航到http://www.babylonjs.com
–按F12并选择“ 网络 ”面板,然后按“ 始终从服务器刷新 ”按钮。 现在,我们要求浏览器绕过他的缓存,并始终尝试从Web服务器下载资产。 现在按“ 播放 ”按钮开始捕获:
–尝试加载“ 心脏 ”场景。 第一次,您应该会看到这样的痕迹:
使用XHR请求下载了38个项目。
–返回主页并重新加载相同的场景。 您现在应该只看到1个HTTP请求发出请求:
发送唯一的XHR请求以检查清单文件。 现在,我们确定其他所有内容都来自我们本地的IndexedDB。
IE,Firefox和Chrome的一些内部详细信息
最后一个提示:我发现Aaron Powell撰写的这篇文章非常有趣,阅读内容是: 浏览器如何存储IndexedDB数据 。 您将了解到IE使用ESE( 可扩展存储引擎 )实现IndexedDB,Firefox使用SQLite ,Chrome使用LevelDB 。
在同一篇文章中,我还了解了隐藏Firefox和Chrome数据库的地方。
我们如何在Babylon.JS中使用它
我们的主要目标是使其在游戏引擎中的使用非常简单,并尽可能减少其余代码的影响。 然后,我的任务是将我的逻辑注入到两个加载纹理和JSON场景文件的加载函数中。
如果您想知道如何使用Babylon.JS启用对IndexedDB的支持,请首先阅读我在Wiki上编写的教程: https : //github.com/BabylonJS/Babylon.js/wiki/Caching索引数据库
用法非常简单。 将.manifest文件添加到.babylon场景,并指示资产的版本号,以及是否要缓存场景,纹理或同时缓存两者。
我已经进行了大量的单元测试 ,以确保我的代码涵盖了所有可能的情况。 确实,因为我是第一个被要求处理资产的人,所以如果我的代码失败,则不会显示或呈现任何内容。 处理I / O一直是关键部分 。
大多数场景都配置为在我们的网站www.babylonjs.com上离线使用该场景及其纹理。 例如,您可以尝试“ Heart ”场景。 将该场景描述为heart.babylon ,然后关联的清单文件为heart.babylon.manifest 。 场景之一配置为仅缓存纹理。 这是“ 汽车 ”的场景。 这是因为JSON文件TheCar.babylon超过93 MB。 IE11和Chrome无法将大小如此的文件存储到数据库中。 然后,我决定避免尝试对其进行缓存。
最后,要使用Babylon.JS构建一个完全脱机的功能演示,如下所示: Babylon.JS脱机演示 ,您需要将我们的数据库逻辑耦合到HTML5 Application Cache API。 我已经在这里介绍了2D画布游戏的用法: 现代化HTML5 Canvas游戏的第2部分:离线API,拖放和文件API
对于3d WebGL游戏,该方法完全相同。 在这种情况下,我已经将HTML5清单文件的缩小版本放入了Babylon.JS,并在首页中使用了一些图像。 更重要的是:我还在其中包含.babylon.manifest文件。 我终于获得了一个名为babylon.cache的简单小型缓存清单文件:
CACHE MANIFEST Version 1.1 CACHE: abylon.js and.minified-1.1.1.js ndex.html ndex.css ndex.js creenshots/heart.jpg creenshots/omegacrusher.jpg ssets/BandeauEmbleme.png ssets/Bandeauhaut.png ssets/BtnAbout.png ssets/BtnDownload.png ssets/gradient.png ssets/Logo.png ssets/SpotDown.png ssets/SpotLast.png cenes/Heart/Heart.babylon.manifest cenes/SpaceDek/SpaceDek.babylon.manifest NETWORK:
确实,如果您不输入。 babylon.manifest文件进入缓存清单后,当引擎尝试检查其值时,将引发404错误。 并且默认情况下,Babylon.JS假定这意味着您要从Web下载资产。
总而言之, 借助我们的方法,现在想象一下这个Babylon.JS离线演示代表您3d游戏的主菜单,并且每个场景都是您游戏的特定级别。 如果您只想更新其中一个级别,则只需更改其关联的.babylon.manifest文件中包含的版本。 然后,我们的3d游戏引擎将仅更新数据库中的此特定级别 。 仅使用HTML5应用程序缓存API便无法做到这一点。 使用AppCache时,没有增量更新 。 您不得不重新下载缓存清单文件中指定的所有内容。 这意味着更新游戏级别之一将意味着将游戏从网络上完全重新安装到HTML5缓存中。
希望我们的方法和技巧能激发您中的一些人在网络上更好地使用IndexedDB! 随时在评论中分享您的反馈。
最初发布: http : //blogs.msdn.com/b/davrous/archive/2013/09/24/using-indexeddb-to-handle-your-3d-webgl-assets-sharing-feedbacks-amp-tips-of -babylon-js.aspx 。 经作者许可在此处转载。
From: https://www.sitepoint.com/using-indexeddb-manage-3d-webgl-assets/