iT邦幫忙

2025 iThome 鐵人賽

DAY 4
1
Modern Web

用 PixiJS 寫遊戲!告別繁瑣設定,在 Code.Gamelet 打造你的第一個遊戲系列 第 4

Day 04:讓你的 Sprite 動起來 - 位置、縮放與旋轉

  • 分享至 

  • xImage
  •  

昨天,我們成功創建了第一個 Sprite,並將它顯示在畫面的正中央。有了這個靜態的物件後,今天,我們就要讓它活起來,學習如何控制它的位置、縮放與旋轉。

在開始之前,你可以先想想,要怎麼讓一個物件動起來呢?

答案很簡單:不斷地更新它的屬性

在 PixiJS 中,Sprite 擁有許多屬性,其中最基礎也最重要的,就是控制位置 (position)、縮放 (scale) 與旋轉 (rotation)。透過在每一幀(Frame)持續地修改這些屬性,就能創造出我們所看到的動態效果。

▸ 開始動手

首先,將你的 app.ts 程式碼清空,並貼上以下程式碼:

import pixi = CG.Pixi.pixi;

// 速度
let speed = { x: 2, y: 2 };
// 旋轉速度
let rotationSpeed: number = 0.05;

async function start() {

    // 載入資源(記得將資源別名修改成你自己專案的資源別名喔!)
    await pixi.assets.add("ironman2025_cook.圖片.女巫").load();

    // 初始化 Pixi
    await pixi.initialize({ stageWidth: 960, stageHeight: 540 });

    // 創建 Sprite
    const sprite = pixi.assets.createSprite("ironman2025_cook.圖片.女巫");
    sprite.anchor.set(0.5);
    sprite.position.set(pixi.stageWidth * 0.5, pixi.stageHeight * 0.5);

    // 加入舞台
    pixi.root.addChild(sprite);

    // 添加一個循環更新函數
    CG.Base2.addUpdateFunction(() => {

        // 更新位置
        sprite.x += speed.x;
        sprite.y += speed.y;

        // 如果 Sprite 碰到舞台左右邊界,就反轉方向
        if (sprite.x > pixi.stageWidth || sprite.x < 0) {
            speed.x *= -1;
        }
        
        if (sprite.y > pixi.stageHeight || sprite.y < 0) {
            speed.y *= -1;
        }

        // 更新旋轉
        sprite.rotation += rotationSpeed;
    });
}

start();

貼上程式碼後,點擊「試玩遊戲」,你應該會看到女巫圖片在畫面上不斷地移動並持續旋轉,這不就是螢幕保護程式嗎!?

Pixi Sprite 移動

▸ 拆解程式碼

這段程式碼主要分為兩個部分:初始化循環更新

1. 初始化

前面大部分的程式碼你應該很熟悉了,包含載入資源、初始化 Pixi、創建並設定 Sprite 物件,以及將它加入舞台。

但這次,我們在程式碼最上方定義了兩個變數:

let speed = { x: 2, y: 2 };
let rotationSpeed: number = 0.05;

這裡的 let 關鍵字表示我們宣告了兩個變數,它們的值可以在程式執行過程中被改變。speed 用來控制 Sprite 的移動速度,rotationSpeed 則用來控制它的旋轉速度。

2. 循環更新

這段程式碼是今天的重點,也是所有遊戲動畫的核心:

// 添加一個循環更新函數
CG.Base2.addUpdateFunction(() => {
    // 這裡的程式碼,會在每次畫面被更新時執行
});

CG.Base2.addUpdateFunction 是一個函數,它能將你寫的程式碼註冊到遊戲主迴圈中。這個迴圈會不斷地執行,每當畫面要更新時,就會呼叫一次你所註冊的函式。這也是為什麼我們的程式碼只需要寫一次,就能讓女巫持續移動和旋轉。

在 CG 上,比起使用 PixiJS 原生的 Ticker,我們更傾向於使用 addUpdateFunction,它與原生的 Ticker 並無太大的差異,使用方式也幾乎相同。由於本系列是建立在 CG 平台上,因此我這邊才會著重在介紹 addUpdateFunction,而非 Ticker,這樣當我們去查看 CG 其他專案的原始碼時,才不會有所斷層。

在每次更新畫面的時候,我們做了幾件事情:

sprite.x += speed.x;
sprite.y += speed.y;

sprite.xsprite.y 屬性分別代表了 sprite 的水平和垂直位置。我們讓它們持續加上 speed 的值,就可以讓物件持續移動。

sprite.xsprite.position.x 的簡寫,y 亦同。

if (sprite.x > pixi.stageWidth || sprite.x < 0) {
    speed.x *= -1;
}

if (sprite.y > pixi.stageHeight || sprite.y < 0) {
    speed.y *= -1;
}

if 判斷式會檢查小括號 () 內的條件式是否成立。這裡我們用來檢查 sprite 的位置是否超過了舞台的邊界。

  • sprite.x > pixi.stageWidth 判斷 sprite 是否移出右邊界。
  • sprite.x < 0 判斷 sprite 是否移出左邊界。

sprite 碰到任一邊界時,我們將對應 speed 的值變號(乘上 -1),從而實現反彈效果。

|| 代表「或」,只要左右兩邊的條件有一個成立,就會執行大括號 {} 內的程式碼。

sprite.rotation += rotationSpeed;

rotation 屬性用來控制 sprite 的旋轉角度。我們持續讓它的值加上 rotationSpeed,從而讓 sprite 永不停止地旋轉。

為了簡化教學,我將旋轉速度設為 0.05,但 sprite.rotation 的單位是弧度而非角度。一個圓是 Math.PI * 2 弧度,大約是 6.28。所以如果你想讓物件旋轉一圈,就必須讓 rotation 屬性累加上 Math.PI * 2。如果你習慣使用角度,可以將弧度除以 180 來進行轉換,例如 30° 就是 Math.PI / 180 * 30

點我查看範例程式碼

▸ 想想看:關於 scale 的秘密

我相信 positionscalerotation,其實大家應該都很容易就能理解了,但是關於 scale 其實還有一個小秘密。

我們知道 scalexy 預設皆為 1,代表顯示物件最原始的縮放大小,數字越大,顯示物件就越大,數字越小,顯示物件就越小,直到歸零,顯示物件看起來就像是消失了。

「但!如果數字變成負數呢?」

sprite.scale.x = -1;

你能夠想像物件的 x 軸縮放是 -1 時,他看起來會長什麼樣子嗎?這個問題,我們明天再來解答。

▸ 總結

今天的內容看起來很簡單,但這三個屬性加上「循環更新」的觀念,就是所有遊戲動畫的核心。掌握了這個概念,你就可以讓任何物件動起來,而不只是靜靜地待在畫面上。

明天,我將會介紹如何使用 Container 這個物件,作為容器,它可以讓我們把多個顯示物件放在一起作為一個群組,不過詳細的介紹就等到明天再來說明吧!


上一篇
Day 03:認識遊戲物件的靈魂 - Sprite
系列文
用 PixiJS 寫遊戲!告別繁瑣設定,在 Code.Gamelet 打造你的第一個遊戲4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言