將 Flash 遊戲轉換為 HTML5 時需要了解什麼?
已發表: 2022-03-10隨著 HTML5 使用的興起,許多公司開始重做他們最流行的標題,以擺脫過時的 Flash 並使他們的產品符合最新的行業標準。 這種變化在賭博/賭場和娛樂行業尤為明顯,並且已經發生了幾年,因此已經轉換了相當多的遊戲選擇。
不幸的是,在瀏覽 Internet 時,您經常會偶然發現看似倉促的工作示例,這會導致最終產品的情人質量。 這就是為什麼遊戲開發人員最好花一些時間來熟悉 Flash 到 HTML5 轉換的主題,並了解在開始工作之前要避免哪些錯誤。
選擇 JavaScript 而不是 Flash 的原因之一,除了明顯的技術問題外,還有一個事實是,將游戲設計從 SWF 更改為 JavaScript 可以產生更好的用戶體驗,從而使其具有現代感。 但是怎麼做呢? 您是否需要專門的 JavaScript 遊戲轉換器來擺脫這種過時的技術? 好吧,Flash 到 HTML5 的轉換可能是小菜一碟——這裡是如何處理它。
推薦閱讀: HTML5遊戲設計原理
如何提升 HTML5 遊戲體驗
將游戲轉換到另一個平台是改進遊戲、解決問題和增加受眾的絕佳機會。 以下是一些可以輕鬆完成且值得考慮的事情:
支持移動設備
從 Flash 轉換為 JavaScript 可以覆蓋更廣泛的受眾(移動設備用戶); 對觸摸屏控制的支持通常也需要在遊戲中實現。 幸運的是,Android 和 iOS 設備現在都支持 WebGL,因此通常可以輕鬆實現 30 或 60 FPS 的渲染。 在許多情況下,60 FPS 不會造成任何問題,隨著移動設備的性能越來越高,問題只會隨著時間的推移而改善。- 提高性能
在比較 ActionScript 和 JavaScript 時,後者比第一個更快。 除此之外,轉換遊戲是重新審視遊戲代碼中使用的算法的好時機。 通過 JavaScript 遊戲開發,您可以優化它們或完全去除原始開發人員留下的未使用代碼。 - 修復錯誤並改進遊戲玩法
讓新開發人員查看遊戲的源代碼有助於修復已知錯誤或發現新的和非常罕見的錯誤。 這將使玩遊戲對玩家的刺激性降低,這將使他們在您的網站上花費更多時間並鼓勵嘗試您的其他遊戲。 - 添加網絡分析
除了跟踪流量之外,網絡分析還可用於收集有關玩家在遊戲中的行為方式以及他們在遊戲過程中卡在哪裡的知識。 - 添加本地化
這將增加觀眾人數,並且對於來自其他國家的孩子玩您的遊戲很重要。 或者你的遊戲不是英文的,你想支持那種語言?
為什麼跳過遊戲內 UI 的 HTML 和 CSS 會提高遊戲性能
當談到 JavaScript 遊戲開發時,將 HTML 和 CSS 用於遊戲中的按鈕、小部件和其他 GUI 元素可能很誘人。 我的建議是在這裡要小心。 這是違反直覺的,但實際上利用 DOM 元素在復雜遊戲中的性能較差,而這在移動設備上更重要。 如果您想在所有平台上實現恆定的 60 FPS,則可能需要從 HTML 和 CSS 中退出。
通過使用常規圖像( Phaser.Image
類),利用.crop
屬性進行修剪,利用Phaser.Text
類進行簡單操作,可以在 Phaser 中輕鬆實現非交互式 GUI 元素,例如生命值條、彈藥條或分數計數器文本標籤。
可以使用內置的Phaser.Button
類來實現按鈕和復選框等交互元素。 其他更複雜的元素可以由不同的簡單類型組成,如組、圖像、按鈕和文本標籤。
注意:每次實例化 Phaser.Text 或 PIXI.Text 對象時,都會創建一個新紋理來渲染文本。 這個額外的紋理會破壞頂點批處理,所以注意不要有太多。
如何確保已加載自定義字體
如果要使用自定義矢量字體(例如 TTF 或 OTF)渲染文本,則需要確保在渲染任何文本之前瀏覽器已經加載了該字體。 Phaser v2 沒有為此提供解決方案,但可以使用另一個庫:Web Font Loader。
假設您有一個字體文件並在您的頁面中包含 Web Font Loader,那麼下面是一個如何加載字體的簡單示例:
製作一個將由 Web Font Loader 加載的簡單 CSS 文件(您不需要將其包含在 HTML 中):
@font-face { // This name you will use in JS font-family: 'Gunplay'; // URL to the font file, can be relative or absolute src: url('../fonts/gunplay.ttf') format('truetype'); font-weight: 400; }
現在定義一個名為WebFontConfig
的全局變量。 像這樣簡單的事情通常就足夠了:
var WebFontConfig = { 'classes': false, 'timeout': 0, 'active': function() { // The font has successfully loaded... }, 'custom': { 'families': ['Gunplay'], // URL to the previously mentioned CSS 'urls': ['styles/fonts.css'] } };
到此結束,請記住將您的代碼放在上面顯示的“活動”回調中。 就是這樣!
如何讓用戶更容易保存遊戲
要在 ActionScript 中持久存儲本地數據,您將使用 SharedObject 類。 在 JavaScript 中,簡單的替換是 localStorage API,它允許存儲字符串以供以後檢索,在頁面重新加載時倖存。
保存數據非常簡單:
var progress = 15; localStorage.setItem('myGame.progress', progress);
請注意,在上面的示例中, progress
變量是一個數字,將被轉換為字符串。
加載也很簡單,但請記住,檢索到的值將是字符串,如果它們不存在,則為null
。
var progress = parseInt(localStorage.getItem('myGame.progress')) || 0;
在這裡,我們確保返回值是一個數字。 如果不存在,則將 0 分配給progress
變量。
您還可以存儲和檢索更複雜的結構,例如 JSON:
var stats = {'goals': 13, 'wins': 7, 'losses': 3, 'draws': 1}; localStorage.setItem('myGame.stats', JSON.stringify(stats)); … var stats = JSON.parse(localStorage.getItem('myGame.stats')) || {};
在某些情況下 localStorage 對像不可用。 例如,當使用file://
協議或在私有窗口中加載頁面時。 您可以使用 try 和 catch 語句來確保您的代碼將繼續工作並使用默認值,如下例所示:
try { var progress = localStorage.getItem('myGame.progress'); } catch (exception) { // localStorage not available, use default values }
要記住的另一件事是,存儲的數據是按域保存的,而不是按 URL 保存的。 因此,如果存在多個遊戲託管在單個域上的風險,那麼保存時最好使用前綴(命名空間)。 在上面的示例中, 'myGame.'
是這樣的前綴,您通常希望將其替換為遊戲名稱。
注意:如果您的遊戲嵌入在 iframe 中,則 localStorage 不會在 iOS 上持續存在。 在這種情況下,您需要將數據存儲在父 iframe 中。
如何利用替換默認片段著色器
當 Phaser 和 PixiJS 渲染你的精靈時,它們使用一個簡單的內部片段著色器。 它沒有很多功能,因為它是為速度量身定制的。 但是,您可以根據需要替換該著色器。 例如,您可以利用它來檢查過度繪製或支持更多的渲染功能。
下面是如何為 Phaser v2 提供您自己的默認片段著色器的示例:
function preload() { this.load.shader('filename.frag', 'shaders/filename.frag'); } function create() { var renderer = this.renderer; var batch = renderer.spriteBatch; batch.defaultShader = new PIXI.AbstractFilter(this.cache.getShader('filename.frag')); batch.setContext(renderer.gl); }
注意:重要的是要記住默認著色器用於所有精靈以及渲染到紋理時。 另外,請記住,對所有遊戲中的精靈使用複雜的著色器會大大降低渲染性能。
如何使用默認著色器更改著色方法
自定義默認著色器可用於替換 Phaser 和 PixiJS 中的默認著色方法。
Phaser 和 PixiJS 中的著色通過將紋理像素乘以給定顏色來工作。 乘法總是會使顏色變暗,這顯然不是問題; 它與 Flash 著色完全不同。 對於我們的一款遊戲,我們需要實現類似於 Flash 的著色,並決定可以使用自定義默認著色器。 以下是此類片段著色器的示例:
// Specific tint variant, similar to the Flash tinting that adds // to the color and does not multiply. A negative of a color // must be supplied for this shader to work properly, ie set // sprite.tint to 0 to turn whole sprite to white. precision lowp float; varying vec2 vTextureCoord; varying vec4 vColor; uniform sampler2D uSampler; void main(void) { vec4 f = texture2D(uSampler, vTextureCoord); float a = clamp(vColor.a, 0.00001, 1.0); gl_FragColor.rgb = f.rgb * vColor.a + clamp(1.0 - vColor.rgb/a, 0.0, 1.0) * vColor.a * fa; gl_FragColor.a = fa * vColor.a; }
此著色器通過向色調添加基色來使像素變亮。 為此,您需要提供所需顏色的底片。 因此,為了獲得白色,您需要設置:
sprite.tint = 0x000000; // This colors the sprite to white Sprite.tint = 0x00ffff; // This gives red
我們遊戲中的結果如下所示(注意坦克在被擊中時會閃爍白色):
如何檢查透支以檢測填充率問題
也可以利用替換默認著色器來幫助調試。 下面我解釋瞭如何使用這樣的著色器檢測過度繪製。
當屏幕上的許多或所有像素被多次渲染時,就會發生過度繪製。 例如,許多對象佔據相同的位置並在另一個上渲染。 GPU每秒可以渲染多少像素被描述為填充率。 現代桌面 GPU 對於通常的 2D 用途具有過高的填充率,但移動 GPU 的速度要慢得多。
有一種簡單的方法可以找出屏幕上每個像素被寫入多少次,方法是將 PixiJS 和 Phaser 中的默認全局片段著色器替換為這個:
void main(void) { gl_FragColor.rgb += 1.0 / 7.0; }
此著色器使正在處理的像素變亮。 數字 7.0 表示需要多少次寫入才能將像素變為白色; 您可以根據自己的喜好調整此數字。 換句話說,屏幕上較亮的像素被寫入了幾次,而白色像素至少被寫入了 7 次。
此著色器還有助於查找由於某種原因仍被渲染的“不可見”對象和周圍有過多透明區域需要剝離的精靈(GPU 仍需要處理紋理中的透明像素)。
左圖顯示了玩家如何看待遊戲,而右圖顯示了將過度繪製著色器應用於同一場景的效果。
為什麼物理引擎是你的朋友
物理引擎是負責模擬物理體(通常是剛體動力學)及其碰撞的中間件。 物理引擎模擬 2D 或 3D 空間,但不能同時模擬兩者。 典型的物理引擎將提供:
- 通過設置速度、加速度、關節和馬達來移動物體;
- 檢測各種形狀類型之間的碰撞;
- 計算碰撞響應,即兩個對像在碰撞時應該如何反應。
在 Merixstudio,我們是 Box2D 物理引擎的忠實擁護者,並曾多次使用它。 有一個 Phaser 插件可以很好地用於此目的。 Box2D 也用於 Unity 遊戲引擎和 GameMaker Studio 2。
雖然物理引擎會加速您的開發,但您必須付出代價:降低運行時性能。 檢測碰撞和計算響應是一項 CPU 密集型任務。 您可能會被限制在手機場景中使用幾十個動態對象,或者面臨性能下降以及幀速率降低到 60 FPS 以下的情況。
圖像的左側部分是遊戲場景,而右側顯示了相同的場景,頂部顯示了 Phaser 物理調試疊加層。
如何從.fla文件中導出聲音
如果您在.fla文件中有 Flash 遊戲音效,則無法從 GUI 導出它們(至少在 Adobe Animate CC 2017 中不能),因為缺少用於此目的的菜單選項。 但是還有另一種解決方案——一個專門的腳本可以做到這一點:
function normalizeFilename(name) { // Converts a camelCase name to snake_case name return name.replace(/([AZ])/g, '_$1').replace(/^_/, '').toLowerCase(); } function displayPath(path) { // Makes the file path more readable return unescape(path).replace('file:///', '').replace('|', ':'); } fl.outputPanel.clear(); if (fl.getDocumentDOM().library.getSelectedItems().length > 0) // Get only selected items var library = fl.getDocumentDOM().library.getSelectedItems(); else // Get all items var library = fl.getDocumentDOM().library.items; // Ask user for the export destination directory var root = fl.browseForFolderURL('Select a folder.'); var errors = 0; for (var i = 0; i < library.length; i++) { var item = library[i]; if (item.itemType !== 'sound') continue; var path = root + '/'; if (item.originalCompressionType === 'RAW') path += normalizeFilename(item.name.split('.')[0]) + '.wav'; else path += normalizeFilename(item.name); var success = item.exportToFile(path); if (!success) errors += 1; fl.trace(displayPath(path) + ': ' + (success ? 'OK' : 'Error')); } fl.trace(errors + ' error(s)');
如何使用腳本導出聲音文件:
- 將上面的代碼另存為.jsfl文件在您的計算機上;
- 使用 Adobe Animate 打開一個.fla文件;
- 從頂部菜單中選擇“命令”→“運行命令”,然後在打開的對話框中選擇腳本;
- 現在彈出另一個對話框文件,用於選擇導出目標目錄。
並做了! 您現在應該在指定目錄中有 WAV 文件。 剩下要做的就是將它們轉換為例如 MP3、OGG 或 AAC。
如何在 Flash 中使用 MP3 到 HTML5 轉換
好的舊 MP3 格式又回來了,因為一些專利已經過期,現在每個瀏覽器都可以解碼和播放 MP3。 這使得開發更容易一些,因為最終不需要準備兩種單獨的音頻格式。 例如,以前您需要 OGG 和 AAC 文件,而現在 MP3 就足夠了。
儘管如此,關於 MP3,您需要記住兩件重要的事情:
- MP3 需要在加載後解碼,這可能很耗時,尤其是在移動設備上。 如果您在所有資源加載後看到暫停,則可能意味著 MP3 正在解碼;
- 無縫播放循環播放的 MP3 有點問題。 解決方案是使用 mp3loop,您可以在 Compu Phase 發布的文章中閱讀有關它的內容。
那麼,為什麼要將 Flash 轉換為 JavaScript?
如您所見,如果您知道該怎麼做,Flash 到 JavaScript 的轉換並非不可能。 有了知識和技能,您就可以停止使用 Flash 並享受用 JavaScript 創建的流暢、有趣的遊戲。 不要試圖修復 Flash - 在每個人都被迫這樣做之前擺脫它!
想了解更多?
在本文中,我主要關注 Phaser v2。 但是,現在有一個更新版本的 Phaser 可用,我強烈建議您檢查一下,因為它引入了許多新鮮、酷炫的功能,例如多相機、場景、tilemaps 或 Matter.js 物理引擎。
如果你足夠勇敢並且想在瀏覽器中創造真正非凡的東西,那麼 WebGL 是從頭開始學習的正確選擇。 與各種遊戲構建框架或工具相比,它的抽象級別較低,但即使您從事 2D 遊戲或演示,也可以實現更高的性能和質量。 在學習 WebGL 基礎知識時可能會發現許多有用的網站是 WebGL Fundamentals(使用交互式演示)。 除此之外,要了解有關 WebGL 功能採用率的更多信息,請查看 WebGL Stats。
永遠記住,沒有太多的知識——尤其是在遊戲開發方面!