HTML5游戏 - 在本地缓存整个游戏

时间:2014-07-06 01:19:17

标签: html5 caching local-storage assets html5-appcache

我正在创建一个HTML5 2D游戏,我想只请求每个资产一次,然后将它们存储在用户的文件系统中,我使用localStorage来完成这项任务,不过AFAIK它有每个来源的限制为5mb(我的整个游戏将有更多),我想知道如何在没有这个限制的情况下将我的游戏资产存储在用户的机器中,这就是我所做的直到现在:

items.js:

/**
 * Copyright 2014 - Edgar Alexander Franco.
 *
 * @author Edgar Alexander Franco
 * @version 1.0.0
 */

var items = [
  {
    name : 'characters_scott', 
    url : './img/game/characters/scott', 
    type : 'png'
  }, 
  {
    name : 'map_1', 
    url : './img/game/map/1', 
    type : 'jpg'
  }, 
  {
    name : 'map_2', 
    url : './img/game/map/2', 
    type : 'jpg'
  }
];

Resource.js

/**
 * Copyright 2014 - Edgar Alexander Franco.
 *
 * @author Edgar Alexander Franco
 * @version 1.0.0
 */

var Resource = (function () {
  var self = {};

  self.get = {};

  self.load = function (items) {
    var xhr = (typeof XMLHttpRequest != 'undefined') ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP') ;
    var item, content, mime;

    for (var i in items) {
      item = items[ i ];
      content = localStorage.getItem(item.url);

      if (content == null) {
        xhr.open('GET', item.url, false);
        xhr.send();
        content = xhr.responseText;
        localStorage.setItem(item.url, content);
      }

      if (item.type != 'audio') {
        mime = (item.type == 'jpg') ? 'image/jpeg' : 'image/png' ;
        self.get[ item.name ] = new Image();
        self.get[ item.name ].src = 'data:' + mime + ';base64,' + content;
      } else {
        // Not yet...
      }
    }
  }

  return self;
})();

上面的代码效果很好,但并不能满足我的需求,因为你可以看到我使用localStore并且它有它的局限性,我想调整相同的代码但是无限存储,任何想法?

2 个答案:

答案 0 :(得分:1)

我建议你使用Mozilla的localForage [1]。它提供了与localStorage相同的简单API,但是由IndexedDB支持(我认为它带有WebSQL / localStorage后备)。

另一种方法是,如果所有玩家的资产URL相同,则可以使用applicationCache。我建议你使用像appCacheNanny [2]这样的appCache包装器库(免责声明:我创建了它。)

[1] https://github.com/mozilla/localForage
[2] https://github.com/gr2m/appcache-nanny

答案 1 :(得分:0)

经过长时间的研究后,我得出结论,IndexedDB可能是我们可以为此任务找到的最强大的本地存储,因此,我重新设计了我的代码,现在这是完整的类:

/**
 * Copyright 2014 - Edgar Alexander Franco.
 *
 * @author Edgar Alexander Franco
 * @version 1.0.0
 */

var Resources = (function () {
  var self = {};

  self.get = {};

  self.DOWNLOADED = 1;
  self.LOADED = 2;

  var DB_NAME = 'evilition';
  var TABLE_NAME = 'resources';

  self.audioType = (document.createElement('audio').canPlayType('audio/mp3') == '') ? '.ogg' : '.mp3' ;

  /**
   * Load the assets from the server / filesystem depending if each is cached or not.
   *
   * @param {object} resources Resources of the game.
   * @param {function} callback1 Function to be called on progress.
   * @param {function} callback2 Function to be called on error.
   * @param {function} callback3 Function to be called once the resources are loaded.
   */
  self.load = function (resources, callback1, callback2, callback3) {
    var request = indexedDB.open(DB_NAME, 3);

    request.onerror = function (evt) {
      callback2({});
    }

    request.onupgradeneeded = function (evt) {
      var db = evt.target.result;
      var table = db.createObjectStore(TABLE_NAME, {
        keyPath : 'name'
      });
      table.createIndex('name', 'name', {
        unique : true
      });
    }

    request.onsuccess = function (evt) {
      function loadResource () {
        var resource = resources[ i ];
        var request = objectStore.get(resource.name);

        request.onerror = function (evt) {
          callback2(resource);
        }

        request.onsuccess = function (evt) {
          var progress = Math.round(((i + 1) * 100) / total);
          var content;

          if (typeof request.result == 'undefined') {
            if (resource.type == 'audio') {
              resource.path += self.audioType;
            }

            xhr.open('GET', resource.path + '.b64', false);
            xhr.send();

            if (xhr.readyState == 4 && xhr.status == 200) {
              content = xhr.responseText;
              objectStore.add({
                name : resource.name, 
                content : content
              });
              callback1(resource, progress, self.DOWNLOADED);
            } else {
              callback2(resource);

              return;
            }
          } else {
            content = request.result.content;
            callback1(resource, progress, self.LOADED);
          }

          var mime;

          if (resource.type != 'audio') {
            mime = (resource.type == 'jpg') ? 'image/jpeg' : 'image/png' ;
            self.get[ resource.name ] = new Image;
          } else {
            mime = (self.audioType == '.mp3') ? 'audio/mp3' : 'audio/ogg' ;
            self.get[ resource.name ] = new Audio;
          }

          self.get[ resource.name ].src = 'data:' + mime + ';base64,' + content;
          i++;

          if (i == total) {
            db.close();
            callback3();
          } else {
            loadResource();
          }
        }
      }

      var db = evt.target.result;
      var objectStore = db.transaction(TABLE_NAME, 'readwrite').objectStore(TABLE_NAME);
      var xhr = (typeof XMLHttpRequest != 'undefined') ? new XMLHttpRequest : new ActiveXObject('Microsoft.XMLHTTP') ;
      var total = resources.length;
      var i = 0;

      loadResource();
    }
  }

  /**
   * Delete the resources database requiring the creation of a new one in the next load.
   */
  self.clearLocalCache = function () {
    indexedDB.deleteDatabase(DB_NAME);
  }

  return self;
})();

谢谢Gregor的推荐:)