昨天我們告別了基礎教學,正式啟動了《小女巫・啟程》的專案。根據我們的企劃,今天要完成遊戲的第一個視覺目標:讓小女巫登場,並讓她身後的背景動起來,營造出飛行的感覺。
在 2D 遊戲中,如何讓靜態的背景產生「深度」和「移動」的錯覺?答案就是視差捲軸。
視差捲軸的原理非常簡單,它模擬了人眼觀察世界的現象:離我們越近的物體,移動速度看起來越快;離我們越遠的物體,移動速度看起來越慢。
在程式碼中,我們只需要將背景拆分成多個圖層,並讓它們以不同的速度移動即可:
要實現這個效果,PixiJS 提供了一個專門的類別:TilingSprite。
TilingSprite
TilingSprite
是一種特殊的 Sprite
,它可以將一張貼圖在指定的區域內無限重複平鋪。這對需要無限捲動的背景來說是完美的工具,因為我們不需要手動管理多張背景圖的拼接和替換。
TilingSprite
有一個關鍵屬性:tilePosition
。
我們只需要在遊戲的更新循環中,持續減少 tilePosition.x
的值,貼圖就會向左移動,產生無限向右捲動的錯覺。
當然,我們必須確保圖片的左右兩邊是可以無縫接軌的,這個部分在我們準備圖片素材的時候就必須下點功夫,不過素材的部分我都幫各位處理好了。老實說準備素材感覺也可以開好幾篇文章來介紹,但這次的系列文章應該沒有空間可以擠下去了,也許未來有機會我再分享我自己是怎麼尋找,或製作素材的~
為了讓程式碼更乾淨,我們將視差捲軸的邏輯封裝在一個獨立的 ParallaxBackground
類別中。
一、核心邏輯:ParallaxBackground
類別
這個類別繼承自 PIXI.Container
,用於管理所有背景圖層。
class ParallaxBackground extends PIXI.Container {
// 儲存三個 TilingSprite 實例
private _foreground: PIXI.TilingSprite;
private _midground: PIXI.TilingSprite;
private _background: PIXI.TilingSprite;
// 三個背景個別的移動速度(像素/毫秒),前景移動最快
private _foregroundScrollSpeed = 0.15;
private _midgroundScrollSpeed = 0.05;
private _backgroundScrollSpeed = 0.01;
constructor() {
super();
// 最遠層次的背景(最低層)
this._background = this._createTilingSprite("ironman2025_cook.圖片.背景_天空");
this.addChild(this._background);
// 中間層次的遠景/遠山
this._midground = this._createTilingSprite("ironman2025_cook.圖片.遠景");
this.addChild(this._midground);
// 最前景的枯樹/城堡(最高層,移動最快)
this._foreground = this._createTilingSprite("ironman2025_cook.圖片.前景");
this.addChild(this._foreground);
}
/**
* 快速創建一個填滿畫面的 TilingSprite。
* @param alias - 圖片資源別名
*/
private _createTilingSprite(alias: string): PIXI.TilingSprite {
const texture = pixi.assets.getTexture(alias);
// 設定 TilingSprite 寬高為整個舞台
return new PIXI.TilingSprite({
texture: texture,
width: pixi.stageWidth,
height: pixi.stageHeight,
});
}
// 遊戲更新循環中會呼叫此函數,進行背景捲動
update(dt: number): void {
// 讓 tilePosition.x 減去速度 * dt,實現向左的捲動效果
this._background.tilePosition.x -= this._backgroundScrollSpeed * dt;
this._midground.tilePosition.x -= this._midgroundScrollSpeed * dt;
this._foreground.tilePosition.x -= this._foregroundScrollSpeed * dt;
}
}
TilingSprite
(_background
, _midground
, _foreground
),並為它們設定了不同的 ScrollSpeed
數值。constructor
(建構子)中,我們依序將 _background
、_midground
、_foreground
加入 this
(也就是 PIXI.Container
)。在 PixiJS 中,後加入的物件會顯示在上方,確保了前景蓋在中景、中景蓋在背景之上。update(dt: number)
函數中,我們將每個圖層的 tilePosition.x
減去 速度 × dt
。
tilePosition.x
的改變會移動貼圖的平鋪起始點,產生移動效果。dt
(Delta Time)能確保即使玩家的裝置幀率不同,移動速度也會保持一致。二、專案初始化與主循環
接下來,我們需要在 start()
函數中載入資源、初始化舞台,並將這個 ParallaxBackground
實例化加入到遊戲的更新循環中。
import pixi = CG.Pixi.pixi;
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 });
// 創建視差背景實例,並加入到舞台最底層
const parallaxBackground = new ParallaxBackground();
pixi.root.addChild(parallaxBackground);
// 使用資源別名創建 Sprite 物件,並設定物件縮放、錨點、位置。
const texture = pixi.assets.getTexture("ironman2025_cook.圖片.女巫");
const witch = new PIXI.Sprite(texture);
witch.anchor.set(0.5);
// 將小女巫放置在舞台左側
witch.position.set(pixi.stageWidth * 0.25, pixi.stageHeight * 0.5);
// 將 Sprite 物件加入到舞台中。
pixi.root.addChild(witch);
// 設置更新循環函數,每一幀都呼叫背景的 update 函數
CG.Base2.addUpdateFunction((dt: number) => {
parallaxBackground.update(dt);
});
}
start();
這邊都跟之前的基礎介紹一樣的模式。
start()
函數中,我們先載入了所有背景圖層與小女巫的資源,然後初始化舞台。ParallaxBackground
和 witch
實例化,並加入舞台中。CG.Base2.addUpdateFunction
將 parallaxBackground.update(dt)
函數註冊到遊戲的主更新循環中,確保每一幀畫面繪製前,背景都會移動。今天小女巫終於有了背景舞台,我們完成了 《小女巫・啟程》 最重要的視覺基石:視差捲軸。
透過 TilingSprite
和一個獨立的 ParallaxBackground
類別,我們成功地用三層移動速度不同的背景,營造出了小女巫正在持續向右飛行的錯覺。
然而,目前的小女巫還只是個漂亮的靜物,她無法響應我們的操作。所以明天,我們將實作「玩家操控」,為小女巫加入上下移動的功能,讓我們真正開始掌握她在遊戲世界中的命運!