利用HTML5進行游戲開發,相比于其他語言例如Java、C++等,有很多不同,其中一個就是資源加載。本文主要對HTML5游戲資源加載的問題、原因以及解決方案,進行一些分析,試圖解釋以下問題:
- 如何加載不同類型的資源
- 如何進行批量加載
- 如何顯示加載的進度條
- 如何存放資源
在文章的最后,也會列舉一些游戲引擎的實現方案,供大家參考。通過此篇文章,希望可以讓讀者對于資源加載的技術有一個全面的了解。
二) 需要考慮的資源類型
一般游戲需要的資源,主要包括圖片、音頻、視頻以及二進制數據文件。如果是3D游戲,還會需要一些模型文件,例如3dmax導出的obj文件。通常的情況下,這些資源文件,少則幾十兆,多則幾個G。對于很多客戶端游戲,這個并不是特別大的問題。通常,它們可以將這些資源打在安裝包中,隨著安裝的過程,一次性的存放在本地。
但是,Web游戲面臨的情況比較復雜,主要有兩個原因:
- 因為所有的資源都放在云端的服務器上。
- 瀏覽器為了優化網頁的渲染,對于圖片、音頻等資源的加載,通常都是異步的。
大家可以回想一下,在打開某些網頁的時候,偶然也會看到,即使在網頁顯示完以后,總有一兩個圖片的位置是空白的,大約幾秒鐘以后,這些圖片往往又在不經意中顯示了出來。
除了圖片、音頻等二進制文件,還有一類比較特殊的文件,就是Javascript文件。尤其是游戲邏輯比較龐大時,Javascript文件也可能有幾百K,甚至好幾兆。如果僅是根據文件的大小,這類文件似乎可以忽略不計。但是由于瀏覽器對于Javascript文件的處理是同步的,往往這些文件會成為性能的瓶頸。
舉個例子,當瀏覽器在解析網頁的過程中,碰到了<script>標簽,它會立即轉入對<script>標簽的解析,同時阻塞的等待解析的完成。如果<script>標簽,帶有src屬性,瀏覽器同樣是阻塞的等待下載完成。所以,有時我們抱怨網絡太慢,其實是委屈了運營商,很多時候,是腳本執行占用了太長時間,阻塞了網頁的顯示。
對于Javascript腳本的加載,首先要解決下載的問題,通常是偽裝Javascript文件成資源文件,比如將Javascript中的腳本,作為整個字符串,放入一個JSON文件,充分發揮瀏覽器異步下載的能力。其次要縮短每次腳本文件解析的時間,這個最重要的就是按需“執行”,也就說要將腳本模塊化。模塊化是比較容易理解的,就是模仿面向對象的編程方法,將不同功能的函數放在不同的文件中。
但是,這樣做帶來另外一個問題,因為Javascript沒有提供類似于面向對象語言中的模塊繼承功能,例如,在Java中,Java虛擬機會自動的將該文件依賴的其他類,導入運行時環境。為了實現模塊化,也需要為Javascript模擬一套類似的功能,幸運的是,目前已經有許多成熟的類庫,例如RequireJS。因為Javascript文件的加載不屬于游戲開發的專有問題,在本文中不做詳細介紹。
三) 如何加載不同類型的資源
2.1 通過瀏覽器內置對象的回調接口,實現資源加載
對于圖片文件的加載,瀏覽器提供了方便的回調接口,比較容易實現,如下:
- var image = new Image();
- image.addEventListener(“success”, function(e) {
- // do stuff with the image
- );
- image.src = "/some/image.png";
但是比較麻煩的是,HTML并沒有提供對等的Audio、Video對象。對于Audio,雖然Web Audio API可以提供類似的功能,但是明顯學習門檻高了一些。對于Video,目前還沒有可以有效的方式,可以模擬類似的功能。對于文本、二進制等文件,更是比較麻煩。
2.2 通過Ajax請求,實現資源加載
利用Ajax對HTTP地址進行請求的能力,相信大家沒有任何質疑。但是,在Ajax請求到相關資源以后,如何將資源轉化為相應的圖片、音頻等對象,好像又產生了一些困難。
但是幸運的是,目前Ajax推出了新的標準,可以支持對二進制數據的提取。再輔助目前新的數據存儲方式,比如Blob、FileSystem等,可以輕松的解決這個問題。
利用Blob將資源轉換相應的對象,代碼片段如下,更多代碼請參考“New Trics in XHR”
- window.URL = window.URL || window.webkitURL; // Take care of vendor prefixes.var xhr = new XMLHttpRequest();
- xhr.open('GET', '/path/to/image.png', true);
- xhr.responseType = 'blob';
- xhr.onload = function(e) {
- if (this.status == 200) {
- var blob = this.response;
- var img = document.createElement('img');
- img.onload = function(e) {
- window.URL.revokeObjectURL(img.src); // Clean up after yourself.
- };
- img.src = window.URL.createObjectURL(blob);
- document.body.appendChild(img);
- ...
- }
- };
- xhr.send();
利用FileSystem,將資源轉換為相應的對象,代碼片段如下,更多完成代碼,請參考“LOADING LARGE ASSETS IN MODERN HTML5 GAMES”
- var xhr = new XMLHttpRequest();
- xhr.open('GET', url, true);
- xhr.responseType = 'arraybuffer';
- xhr.addEventListener('load', function() {
- createDir_(root, dirname_(key).split('/'), function(dir) {
- dir.getFile(basename_(key), {create: true}, function(fileEntry) {
- fileEntry.createWriter(function(writer) {
- writer.onwrite = function(e) {
- // Save this file in the path to URL lookup table.
- lookupTable[key] = fileEntry.toURL();
- callback();
- };
- writer.onerror = failCallback;
- var bb = new BlobBuilder();
- bb.append(xhr.response);
- writer.write(bb.getBlob());
- }, failCallback);
- }, failCallback);
- });
- });
- xhr.addEventListener('error', failCallback);
- xhr.send();
上面兩種方式,都是在獲得資源后,為資源生成一個URL地址對象,在將此地址賦給相關的對象。
2.3 通過創建元素的方式,獲得資源
于第二種方式,相信不少讀者擔心不同瀏覽器的兼容性,畢竟里面利用了大量的HTML5新屬性,而這些屬性,很多屬于剛剛發布的特性,每個瀏覽器支持的情況不太一樣。所以,還需要一種可以兼容所有瀏覽器的方式。通過創建元素的方式,相對比較保險。參考如下代碼:
- var res = document.createElement(“image/audio/xxx”)
- res.src = “http://www.yourdomain.com”
但是這樣的代碼,也碰到了和第一個方法同樣的問題,不是所有的元素都提供了onload函數。對于Image,處理相對簡單。但是對于Audio/Vido,只能利用canplaythrough等函數做一些大約估計。
總之,為了做好資源下載,可能不能完全依賴某一類方法,最有可能的方式是根據每種方法的優缺點,根據具體原因進行選擇。在一些比較成熟的游戲引擎和類庫的實現中,也確實是融合了這三種不同的方法。
(未完待續)
【網站聲明】本站除付費源碼經過測試外,其他素材未做測試,不保證完整性,網站上部分源碼僅限學習交流,請勿用于商業用途。如損害你的權益請聯系客服QQ:2655101040 給予處理,謝謝支持。