iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0
佛心分享-IT 人自學之術

那些經過腦海一瞬的關鍵字們系列 第 4

[Web Game] 基於同樣的需求做比較

  • 分享至 

  • xImage
  •  

別人這樣比較差異性

這篇 Phaser vs PixiJS for making 2D games 比較 Phaser 和 PixiJS 相同和差異之處,並實際以製作 Flappy Bird 2D 遊戲為例,同時比較兩者程式碼的寫法。

這篇文章涵蓋的範圍很完整,從基本設定、建置遊戲、添加動畫及音效、打包,把兩套工具的程式碼實際寫法和結果都說明的很詳細!

*Flappy Bird,玩家必須控制小鳥的飛行,避開水管,每飛越一個水管會得到一分,碰到障礙物即遊戲結束
*文章發佈於 2022/8,可能與現況會有些差異

以下內容取自 Phaser vs PixiJS for making 2D games

文章分成以下這幾個區塊敘述:

  • 前言:說明目的和做法
  • Phaser basics:介紹 Phaser 特色
  • PixiJS basics:介紹 PixiJS 特色
  • Documentation and ease of use:文件與範例的完整度以及好不好上手
  • Adding game physics:怎麼設定遊戲的物理世界
  • Adding animation:怎麼設定動畫
  • Adding user input:監聽使用者的操作做輸入
  • Implementing a responsive layout:Responsive 版面配置
  • Adding sounds:添加音效
  • Bundle size comparision:比較打包尺寸
  • Conclusion - which one is the best:小結

1. 基本介紹

Phaser 特色:

  • Game framework
  • 提供三套物理引擎可以選擇,方便處理速度、加速度、重力和碰撞等物理現象
// 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 特色:

  • 快速、輕量的圖像 library,用來 render 2D 圖像
  • 沒有內建的 function 可以使用 (例如:偵測碰撞),必須自己定義自己寫,或是額外引用第三方套件
// 透過 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() {
}

// 增加其他會用到的函式:
//   例如:偵測碰撞

2. 文件

兩套工具,官網文件都很完整也都有提供範例或是教學,Phaser 有比較多遊戲的範例,PixiJS 則是比較多圖像 render 範例。因為 Phaser 屬於 framework,本身就有提供遊戲相關的函式,對開發遊戲而言,程式碼的架構較有組織化。而 Phaser 和 PixiJS 都有 Webpack 做打包的 template 可使用。

3. 怎麼設定物理世界

對於 Phaser:有內建三種物理引擎可選擇,Arcade PhysicsImpact PhysicsMatter.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

4. Adding animation & user input

兩套工具都可以做 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 事件,然後再去設定小鳥的速度做位置的移動。

5. Responsive 佈局

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`;
}

6. 設定音效

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");

7. 打包

分別使用這兩個 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 時間 >>> 星星數。


上一篇
[Web Game] 選擇適合的工具
下一篇
[測試] 種下種子的地方
系列文
那些經過腦海一瞬的關鍵字們13
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言