原本我們只打算讓小女巫上下移動,但身為一位優秀的巫師,偶爾後退拉開距離,或是加速向前衝刺都是戰鬥必備的技巧!
因此,今天我們將為小女巫加入更完整的操控:上下移動,以及能夠微幅前後調整位置的功能。為了增加遊戲的真實感,我打算設定設定:
今天會用到 Day 09 介紹的 Keyboard
功能,忘記的趕快去惡補一下!
Witch
類別為了管理小女巫的所有邏輯(移動、攻擊、血量等),我們將其封裝在一個獨立的 Witch
類別中,並繼承自 PIXI.Container
。
一、Witch
類別架構與速度設定
class Witch extends PIXI.Container {
// 儲存女巫的 Sprite 物件
private _sprite: PIXI.Sprite;
// 用於計算每幀的移動方向
private _moveDirection: PIXI.Point = new PIXI.Point(0, 0);
// 移動速度
private _speed = {
up: 0.2, // 垂直移動速度
down: 0.2,
back: 0.1, // 向後微退速度(最慢)
front: 0.15 // 向前微移速度
}
constructor() {
super();
// 取得小女巫的紋理,並設定錨點置中
const texture = pixi.assets.getSpritesheet("ironman2025_cook.圖集動畫.角色").textures["witch"];
const spirte = this._sprite = new PIXI.Sprite({
texture: texture,
anchor: 0.5
});
this.addChild(spirte);
}
// ... (update 函數在下一段)
}
PIXI.Point
:PIXI.Point
是 PixiJS 中用來表示 二維座標 或 向量(Vector) 的實用類別。在這裡,我們使用它來儲存和計算小女巫在 X 軸(水平) 和 Y 軸(垂直) 上的移動方向和距離。使用 dir.set(0, 0)
可以快速將向量歸零,方便每一幀重新計算移動量。_speed
:我們在這裡定義了四個方向的速度。透過設定不同的數值,實現了「垂直移動速度一致」、「向前微移」和「向後微退最慢」的效果,增強了遊戲的操作手感。二、update()
函數:移動與邊界限制
所有遊戲物件的邏輯都在 update(dt: number)
函數中執行,確保移動與幀率無關。
// 承接上段的 Witch 類別
update(dt: number): void {
// 1. 偵測鍵盤輸入
const isUp = keyboard.isDown(Key.W);
const isDown = keyboard.isDown(Key.S);
const isBack = keyboard.isDown(Key.A);
const isFront = keyboard.isDown(Key.D);
// 將屬性暫存成區域常數方便使用
const speed = this._speed;
// 重設移動速度,每一幀都會根據按鍵狀態重新計算
const dir = this._moveDirection.set(0, 0);
// 2. 依據按鍵計算移動向量(使用 dt 確保不同裝置之間的畫面同步)
if (isUp) dir.y -= speed.up * dt;
if (isDown) dir.y += speed.down * dt;
if (isBack) dir.x -= speed.back * dt;
if (isFront) dir.x += speed.front * dt;
// 3. 更新位置
this.x += dir.x;
this.y += dir.y;
// 4. 邊界限制(Stage Boundaries)
const width = pixi.stageWidth;
const height = pixi.stageHeight;
// 計算物件的「半寬/半高」,因為錨點設為 0.5,所以取 Sprite 的寬高即可
const halfWidth = this._sprite.width * this.scale.x * 0.5;
const halfHeight = this._sprite.height * this.scale.y * 0.5;
// 限制水平移動範圍(避免超出左右邊界)
this.x = Math.max(halfWidth, Math.min(this.x, width - halfWidth));
// 限制垂直移動範圍(避免超出上下邊界)
this.y = Math.max(halfHeight, Math.min(this.y, height - halfHeight));
}
// ...
dir.x
和 dir.y
來累加不同按鍵的移動量,然後一次性將結果加到 this.x
和 this.y
上。Math.max()
和 Math.min()
兩個函數來夾住主角的位置:
Math.max(最小值, ...)
:確保主角位置不會小於最小值(例如:不會從左邊界外飛入)。Math.min(..., 最大值)
:確保主角位置不會大於最大值(例如:不會從右邊界飛出)。最後,我們調整 start()
函數,除了背景之外,也要記得呼叫小女巫的 update()
。
import pixi = CG.Pixi.pixi;
import keyboard = CG.Base2.keyboard; // 記得檢查是否正確引用鍵盤相關功能
import Key = CG.Base2.keyboards.Key;
async function start() {
// 載入所有資源(包含小女巫的圖集)
await pixi.assets
.add("ironman2025_cook.圖片.背景_天空")
.add("ironman2025_cook.圖片.遠景")
.add("ironman2025_cook.圖片.前景")
.add("ironman2025_cook.圖集動畫.角色") // 新增女巫圖集資源
.load();
// 初始化 Pixi。
await pixi.initialize({ stageWidth: 960, stageHeight: 540 });
// 創建視差背景實例 (ParallaxBackground class 沿用 Day 16 的程式碼)
const parallaxBackground = new ParallaxBackground();
pixi.root.addChild(parallaxBackground);
// 創建小女巫實例
const witch = new Witch();
// 將小女巫放置在舞台左側
witch.position.set(pixi.stageWidth * 0.25, pixi.stageHeight * 0.5);
// 將小女巫加入到舞台中。
pixi.root.addChild(witch);
// 設置更新循環函數
CG.Base2.addUpdateFunction((dt: number) => {
parallaxBackground.update(dt);
witch.update(dt); // 這裡要記得呼叫喔!不然小女巫就動不了了
});
}
start();
// ... (ParallaxBackground class 程式碼)
// ... (Witch class 程式碼)
我們今天成功為小女巫加入了完整的二維移動操控,並透過 Math.min/max
實現了邊界限制。現在小女巫已經準備好在廣大的夜空中自由飛行了!
不過,一位巫師在戰鬥中只會閃躲可不行。所以明天,我們要為小女巫裝上火力,實作她的核心戰鬥機制——自動攻擊,發射她的第一顆魔法彈!