原本我們只打算讓小女巫上下移動,但身為一位優秀的巫師,偶爾後退拉開距離,或是加速向前衝刺都是戰鬥必備的技巧!
因此,今天我們將為小女巫加入更完整的操控:上下移動,以及能夠微幅前後調整位置的功能。為了增加遊戲的真實感,我打算設定設定:
今天會用到 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 實現了邊界限制。現在小女巫已經準備好在廣大的夜空中自由飛行了!
不過,一位巫師在戰鬥中只會閃躲可不行。所以明天,我們要為小女巫裝上火力,實作她的核心戰鬥機制——自動攻擊,發射她的第一顆魔法彈!