Phaser -- HTML 遊戲框架

第 2 章    平台跳躍

(1) 遊戲規格

∗ 小精靈在移動的平台上跳躍

phaserLogo

▸ 視窗尺寸 800x600 ,背景為天空、雲彩、及樹叢

▸ 有兩種平台:標準平台及冰平台,其中冰平台沒有摩擦力

▸ 小精靈可左右移動及跳躍,可站在地面或平台

(2) 建立專案目錄及初始檔案

∗ 遊戲專案

▸ 建立專案目錄結構,遊戲名稱:jump

games/ 
   jump/
      index.html
      assets/
      js/
         main.js 

▸ 下載資產 assets02.zip

▸ 建立 index.html 及初始 main.js

index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="gameDiv"></div>
<script src="../phaser/phaser.min.js"></script>
<script src="js/main.js"></script>
</body>
</html>
main.js
var main = {

  preload: function() {
  },

  create: function() {
  },

  update: function() {
  },

};

var game = new Phaser.Game(800, 600, Phaser.AUTO, 'gameDiv');
game.state.add('main', main);
game.state.start('main');
✶ 遊戲視窗 800x600

▸ 接下來修改 main.js 開始建構遊戲

(3) 載入遊戲資產

∗ 在 preload 方法中載入資產:背景、標準平台、冰平台、及精靈表

  preload: function() {
    game.load.image('background', 'assets/background.png');
    game.load.image('platform', 'assets/platform.png');
    game.load.image('ice-platform', 'assets/ice-platform.png');
    game.load.spritesheet('dude', 'assets/dude.png', 32, 48); 
  },

▸ 影像資產:背景、標準平台、冰平台之資產鍵 (Asset key) 分別為 background, platform, 及 ice-platform,之後的程式將利用資產鍵來取用此資產

▸ 精靈表 (Sprite sheet):一系列的影像組成一張大影像,每張影像是就是動畫 (Animation) 裡的一幀畫面 (Frame),影像的尺寸均相同

✶ 如果每個畫面都是一個影像檔,會大量耗費記憶體,而將所有畫面組成一張影像則會讓記憶體的耗損減少
✶ 使用小精靈影像,資產鍵為 dude ,並設定每幀畫面的寬高分別為 32 及 48
elf

(4) 加入背景及平台

∗ 遊戲世界 (Game world) 及精靈 (Sprite)

▸ 遊戲專案裡的所有物件都存在遊戲世界裡,遊戲世界沒有固定尺寸,中心點為 (0, 0) 並往各方向延伸到無限遠

▸為方便起見,Phaser將 (0, 0) 設在視窗左上角,但這可以透過 Camera 改變

▸ 遊戲世界可透過 game.world 存取,並有許多方法及屬性可供使用

▸ 可利用 game.add.sprite() 指令在遊戲世界裡加入 Phaser.Sprite 物件 (精靈物件)

∗ 將背景及 4 個平台加入遊戲世界:在 create 方法加入以下內容

  create: function() { 
    game.physics.startSystem(Phaser.Physics.ARCADE);
  
    game.add.image(0, 0, 'background');
  
    this.platforms = game.add.group();
    this.platforms.enableBody = true;
  
    var platform = this.platforms.create(0, 64, 'ice-platform');
    platform.body.friction.x = 0;
    this.platforms.create(200, 180, 'platform');
    platform = this.platforms.create(400, 296, 'ice-platform');
    platform.body.friction.x = 0;
    this.platforms.create(600, 412, 'platform');
  
    this.platforms.setAll('body.immovable', true);
    this.platforms.setAll('body.velocity.x', 100); 
  },

game.physics.startSystem():首先啟用遊戲的物理系統 (本遊戲需要物理現象)

▸ Phaser 提供三種物理系統:Arcade、Ninja、及 P2 ( 參考 Phaser 文件 )

✶ Arcade 僅偵測AABB碰撞 (Axis-aligned bounding box,軸對齊邊框),亦即偵測兩個影像 (方形,不得旋轉) 是否重疊,由於計算速度快,因此適用於高速碰撞偵測,本遊戲使用 Arcade 系統
✶ Ninja 允許物體有磚塊 (Title) 及斜率 (Slope),亦允許旋轉,計算速度較慢
✶ P2 支援多邊形的物體碰撞,允許模擬彈性及碰撞力量等,計算速度也較慢

▸ Phaser 呈現物件的順序和程式撰寫的順序相同,因此如果天空背景應在其他物件底下,則應先加入天空精靈, 之後再加入平台及小精靈

game.add.image(0, 0, 'background'):在 (0, 0) 位置加入背景影像, 使用 background 資產,以 game.add.image() 所加入的物件無法設定動畫或物理物體的特性

game.add.group():加入一個群組 (Group),並指派給 main 的屬性 platforms,包含 4 個平台,小精靈可以跳上去

✶ 群組的功能很強,可將類似的物件組在一起,然後就像控制一個物件一般來統一控制它們,也可以測試不同群組彼此是否碰撞

this.platforms.enableBody = true;:設定平台裡的所有物件都符合物理學的物體特性

✶ 設定此特性之後,個別 platform 才有 body 屬性可使用

var platform = this.platforms.create(..., 'ice-platform'):在平台群組產生冰平台,並設定適當位置

✶ 指派給變數 platorm 的目的在於下一行需要設定該平台的物體特性 (摩擦力)

platform.body.friction.x = 0:x 方向的摩擦係數為 0 (範圍:0~1)

this.platforms.create(..., 'platform'):在平台群組產生 2 個標準平台, 並分別設定適當位置

this.platforms.setAll('body.immovable', true):平台群組裡的所有物理物體不可移動 (以免小精靈跳上去時,地面會陷落)

this.platforms.setAll('body.velocity.x', 100): 設定平台群組裡的所有物理物體的水平速度為 100

▸ 結果:向右移動的平台

phaserLogo

∗ 在 main 物件中產生變數時,應宣告為物件屬性或是區域變數?

▸ 如果該變數僅在函式內部使用,在其他地方不會用到,就宣告為區域變數

▸ 如果在其他地方會用到 (函式以外的地方),就設定為物件的屬性:在物件內部可利用 this.<varName> 方式存取,在物件外部則使用 main.<varName> 方式存取

▸ 物件屬性 (Property) 就像是附加在該物件的變數一般

(5) 加入小精靈並設定鍵盤

∗ 繼續在 create 中加入小精靈並設定其特性

  create: function() {
    ...
    this.platforms.setAll('body.velocity.x', 100);
  
    this.player = game.add.sprite(320, 400, 'dude');
    game.physics.arcade.enable(this.player);
    this.player.body.gravity.y = 300;
    this.player.body.collideWorldBounds = true;
    this.player.body.setSize(20, 32, 5, 16);
  
    this.player.animations.add('left', [0, 1, 2, 3], 10, true);
    this.player.animations.add('right', [5, 6, 7, 8], 10, true);
  },

this.player = game.add.sprite():產生小精靈並指派給 main 的屬性 player,設定適當起始位置, 使用 dude 資產

game.physics.arcade.enable():小精靈啟用 Arcade 物理特性, 會行動的精靈需要使用物理特性

this.player.body.collideWorldBounds = true:設定小精靈會碰撞遊戲世界的邊界 (即視窗邊界)

this.player.body.setSize(20, 32, 5, 16):設定物理物體特性的尺寸, 讓碰撞距離較小,看起來較真實

✶ 參數為 width, height, offsetX, offsetY:寬、高、x 位移、及 y 位移 (原尺寸為 32x48)

dude 並非以影像方式加入,而是以精靈表 (Sprite sheet) 方式加入資產, 因為 dude.png 影像包含動畫畫面 (Animation frames):

phaserLogo

▸ 共有 9 幀畫面 (每幀 32x48):4 個向左跑,1 個正面,另 4 個向右跑 (註:Phaser 支援影像翻轉, 其實不需要這麼多幀畫面)

this.player.animations.add():設定 2 種動畫:向左走及向右走, 名稱分別為 leftright,向左走使用 0~3 畫面, 向右走使用 5~8 畫面,每秒跑 10 個畫面,最後參數 true 表示不斷重複

∗ 最後在 create 中加入鍵盤設定,使用者可以利用方向鍵操控精靈,this.cursors 包含四個方向鍵 (上下左右)

 
  create: function() {
    ...
    this.player.animations.add('right', [5, 6, 7, 8], 10, true);
  
    this.cursors = game.input.keyboard.createCursorKeys(); 
  },

(6) 導入碰撞偵測及操控功能

∗ 在 update 中導入碰撞偵測

  update: function() {
    this.platforms.forEach(this.wrapPlatform, this);
    game.physics.arcade.collide(this.player, this.platforms);
  },
  
  wrapPlatform: function(platform) {
    if (platform.x >= 800) {
      platform.x = -160;
    }
  },

};

▸ 首先讓平台能循環出現

this.platforms.forEach(this.wrapPlatform, this): 對於每個平台群組裡的物件,都呼叫 wrapPlatform 方法進一步處理
forEach() 的第一個參數是需執行的函式,第二個參數是方法執行的地方, 使用 this 即表示執行環境就在 main
# 給了 this 參數,不論 wrapPlatform 函式放在哪裡都可以存取 main 物件裡的屬性 (註:JavaScript 物件裡的函式常需要指定執行環境,如此才能存取該環境裡的屬性)
wrapPlatform():如果平台移出遊戲世界,就設定適當位置,從左邊循環
# forEach() 函式每次呼叫 wrapPlatform() 函式時, 會自動傳入群組成員,因此 wrapPlatform(): function(platform) 可接收參數 platform

game.pyhsics.arcade.collide(this.player, this.platforms):偵測小精靈是否碰撞平台 (平台群組裡的所有物件),因此小精靈墜落時會停在平台

▸ 測試:可看到循環出現的平台

∗ 接著加入移動小精靈的功能

  update: function() {
    ...
    game.physics.arcade.collide(this.player, this.platforms);
    
    this.player.body.velocity.x = 0;
    if (this.cursors.left.isDown) {
      this.player.body.velocity.x = -200;
      this.player.animations.play('left');
    }
    else if (this.cursors.right.isDown) {
      this.player.body.velocity.x = 200;
      this.player.animations.play('right');
    }
    else {
      this.player.animations.stop();
      this.player.frame = 4;
    }
    if (this.cursors.up.isDown &&
        (this.player.body.onFloor() || this.player.body.touching.down)) {
      this.player.body.velocity.y = -350;
    } 
  },

▸ 設定小精靈的動作:

.body.velocity.x = 0:設定水平初速為 0
this.cursors.left.isDown, this.cursors.right.isDown, this.cursors.up.isDown:判斷左、右、上鍵是否按下
.body.velocity.x = -200, .body.velocity.x = 200: 如果左鍵按下,速度設為 -200,如果右鍵按下,速度設為 200
.animations.play('...'):播放動畫
.animations.stop():停止動畫
.frame = 4:呈現第四幀畫面 (面向鏡頭)
.body.onFloor():物體是否在地面
.body.touching.down:物體底部是否碰觸其他物體
.velocity.y = -350:讓小精靈跳起 (垂直速度 -350)

▸ 測試

(7) 部署專案

∗ 部署 (Deploy) 到朝陽的 Web server

▸ 利用 Filezilla 連上學校 FTP 網址: http://home.cyut.edu.tw/,並登入

▸ 預設網頁目錄:public_html

▸ 建立遊戲目錄結構 (最上層目錄設為 games)

public_html/ 
   games/
      phaser/
         ...
      jump/
         ... 

▸ 上傳:phaserjump 目錄

▸ 測試:http://www.cyut.edu.tw/~<username>/games/jump/

(8) 練習

上一章       下一章