這篇 Phaser vs PixiJS for making 2D games 比較 Phaser 和 PixiJS 相同和差異之處,並實際以製作 Flappy Bird 2D 遊戲為例,同時比較兩者程式碼的寫法。
這篇文章涵蓋的範圍很完整,從基本設定、建置遊戲、添加動畫及音效、打包,把兩套工具的程式碼實際寫法和結果都說明的很詳細!
*Flappy Bird,玩家必須控制小鳥的飛行,避開水管,每飛越一個水管會得到一分,碰到障礙物即遊戲結束
*文章發佈於 2022/8,可能與現況會有些差異
以下內容取自 Phaser vs PixiJS for making 2D games
文章分成以下這幾個區塊敘述:
Phaser 特色:
// Phaser 遊戲的基本架構
const config = {
type: Phaser.AUTO, // 所使用的 Render 環境,AUTO 代表會使用 WebGL,若裝置不支援會改用 Canvas
width: 800,
height: 600,
physics: {
default: "arcade",
arcade: {
gravity: { y: 500 },
debug: true,
},
},
scene: {
preload: preload,
create: create,
update: update,
},
};
// 透過 config 物件,建立一個 `Phaser.Game` 實體
const game = new Phaser.Game(config);
// 三個函式用來建立或是更新遊戲場景
function preload() {} // 載入 assets
function create() {} // 設定場景,包含建立 sprite、設定 sprite 位置、設定使用者輸入的監聽事件以及設定碰撞偵測
function update() {} // 用來更新每一個畫面,例如:移動 sprite 或是確認 sprite 位置
PixiJS 特色:
// 透過 options 建立 PIXI.Application 實體
const app = new PIXI.Application({
width: 800,
height: 600,
antialias: true,
transparent: false,
resolution: 1,
});
// 建立出來的 view append 到 HTML document 裡
document.body.appendChild(app.view);
// 類似於 Phaser 的 preload,載入所需的 assets,load 完成後會呼叫 setup 函式
app.loader.add("assets/images/cat.png").load(setup);
// 設定場景
function setup() {
state = play;
app.ticker.add((delta) => gameLoop(delta));
}
// 更新場景
function gameLoop(delta) {
// Update the current game state
state(delta);
}
// 處理遊戲的更新,例如:移動 sprite 或是確認 sprite 當下位置
function play(delta) {
// The game logic goes here
}
function end() {
}
// 增加其他會用到的函式:
// 例如:偵測碰撞
兩套工具,官網文件都很完整也都有提供範例或是教學,Phaser 有比較多遊戲的範例,PixiJS 則是比較多圖像 render 範例。因為 Phaser 屬於 framework,本身就有提供遊戲相關的函式,對開發遊戲而言,程式碼的架構較有組織化。而 Phaser 和 PixiJS 都有 Webpack 做打包的 template 可使用。
對於 Phaser:有內建三種物理引擎可選擇,Arcade Physics
、Impact Physics
、Matter.js
對於 PixiJS:開發者必須自己設定重力以及偵測物件碰撞
// Phaser 寫法
// collider 用來偵測小鳥和水管的碰撞
collider = this.physics.add.collider(bird, pipes, collisionCallback);
// 當 collision 發生時,執行 collisionCallback,用來結束遊戲
function collisionCallback() {
gameOver = true;
collider.active = false; // turn off so that the bird falls to the ground on collision
bird.setFlipY(true);
game.sound.play("crashSound");
}
// PixiJS 寫法
// 在 play() 函式中必須加上下面兩行
bird.vy += 0.25;
bird.y += bird.vy;
// 增加小鳥的速度以及更新 y 方向的位置
// 碰撞的部分必須額外再寫一個函式做判斷,或是額外再引入第三方的 physics library,例如:matter.js
假設遊戲中有牽涉到物理現象,或是複雜的物理計算,會建議使用 Phaser
兩套工具都可以做 sprite 動畫
User input 的部分,Phaser 提供較多 methods 來處理使用者的輸入,包含拖拉。使用 PixJS 的話,需要手動處理 user input。
例如,要使用方向鍵或是觸控螢幕來控制小鳥飛行
// Phaser 寫法
// 在 create() 裡增加 cursors
// createCursorKeys method 會回傳一個物件,包含上下左右的 hotkey 、空白鍵以及 shift 鍵
cursors = this.input.keyboard.createCursorKeys();
// 在 update() 裡,監聽 up 方向鍵、滑鼠點擊以及螢幕觸控,來讓小鳥往上移動避開障礙物
if (cursors.up.isDown || this.input.activePointer.isDown) {
bird.setVelocityY(-200);
如果是 PixiJS 會需要更多的程式碼,要使用 keyboard 函式來監聽特定的 input 事件,然後再去設定小鳥的速度做位置的移動。
Phaser 有 Scale Manager,可以處理 scaling、resizing 以及 alignment,也有全螢幕模式
而 PixiJS,開發者必須手動建立 Responsive 佈局。
// Phaser 寫法
// 在 config 中增加 scale 屬性
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: CONFIG_WIDTH,
height: CONFIG_HEIGHT,
},
// PixiJS 寫法
window.addEventListener("resize", resize);
resize();
function resize() {
// 取得目前螢幕尺寸
const screenWidth = Math.max(
document.documentElement.clientWidth,
window.innerWidth || 0
);
const screenHeight = Math.max(
document.documentElement.clientHeight,
window.innerHeight || 0
);
// 計算縮放比例
const scale = Math.min(
screenWidth / CONFIG_WIDTH,
screenHeight / CONFIG_HEIGHT
);
// 計算寬高
const enlargedWidth = Math.floor(scale * CONFIG_WIDTH);
const enlargedHeight = Math.floor(scale * CONFIG_HEIGHT);
// 計算置中所需的間距
const horizontalMargin = (screenWidth - enlargedWidth) / 2;
const verticalMargin = (screenHeight - enlargedHeight) / 2;
// 設定尺寸和間距
app.view.style.width = `${enlargedWidth}px`;
app.view.style.height = `${enlargedHeight}px`;
app.view.style.marginLeft = app.view.style.marginRight = `${horizontalMargin}px`;
app.view.style.marginTop = app.view.style.marginBottom = `${verticalMargin}px`;
}
PixiJS 需額外增加 PixiJS Sound library
// Phaser 寫法
// preload() 內載入音效
this.load.audio("birdSound", "./assets/sounds/woosh.mp3");
// 播放音效
game.sound.play("birdSound");
// PixiJS 寫法
// index.html 載入 PixiJS Sound library
// <script src="https://unpkg.com/@pixi/sound/dist/pixi-sound.js"></script>
.add("bird-sound", "assets/sounds/woosh.mp3")
// 播放音效
PIXI.sound.play("bird-sound");
分別使用這兩個 Webpack starter template 做打包
A Phaser 3 project template that uses Webpack 5 for bundling
Template for PixiJS using Typescript and Webpack
結果顯示,PixiJS 打包完的檔案大小約為 Phaser 的一半,如果 bundle 大小會是決定的關鍵,會建議選擇 PixiJS。
雖然要在專案建置初期就做這樣的分析比較有難度,但從同樣的遊戲需求做為出發點,從建置到打包的過程,逐一比較做分析,更能看出兩套工具的差異性,未來有新的遊戲要挑選使用的工具時,也能更快速的做選擇。
也有點類似於框架的選擇,什麽樣的需求適合什麽樣的工具,我的優先度可能會是:需求符合 > 文件 >> 討論度 >>> commit 時間 >>> 星星數。