iT邦幫忙

2025 iThome 鐵人賽

DAY 16
1

昨天我們告別了基礎教學,正式啟動了《小女巫・啟程》的專案。根據我們的企劃,今天要完成遊戲的第一個視覺目標:讓小女巫登場,並讓她身後的背景動起來,營造出飛行的感覺。

▸ 視差捲軸(Parallax Scrolling)

在 2D 遊戲中,如何讓靜態的背景產生「深度」和「移動」的錯覺?答案就是視差捲軸

視差捲軸的原理非常簡單,它模擬了人眼觀察世界的現象:離我們越近的物體,移動速度看起來越快;離我們越遠的物體,移動速度看起來越慢

在程式碼中,我們只需要將背景拆分成多個圖層,並讓它們以不同的速度移動即可:

  • 前景 (Foreground):移動最快,讓玩家感覺離它最近(例如枯樹、城堡)。
  • 遠景 (Midground):移動次之(例如遠處的雲)。
  • 背景 (Background):移動最慢(例如最遠處的星空或深藍色夜空)。

要實現這個效果,PixiJS 提供了一個專門的類別:TilingSprite

▸ 認識 TilingSprite

TilingSprite 是一種特殊的 Sprite,它可以將一張貼圖在指定的區域內無限重複平鋪。這對需要無限捲動的背景來說是完美的工具,因為我們不需要手動管理多張背景圖的拼接和替換。

TilingSprite 有一個關鍵屬性:tilePosition
我們只需要在遊戲的更新循環中,持續減少 tilePosition.x 的值,貼圖就會向左移動,產生無限向右捲動的錯覺。

當然,我們必須確保圖片的左右兩邊是可以無縫接軌的,這個部分在我們準備圖片素材的時候就必須下點功夫,不過素材的部分我都幫各位處理好了。老實說準備素材感覺也可以開好幾篇文章來介紹,但這次的系列文章應該沒有空間可以擠下去了,也許未來有機會我再分享我自己是怎麼尋找,或製作素材的~

▸ 程式碼實作:ParallaxBackground 類別

為了讓程式碼更乾淨,我們將視差捲軸的邏輯封裝在一個獨立的 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 的改變會移動貼圖的平鋪起始點,產生移動效果。
    • 我們在 Day 10 有介紹過,使用 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() 函數中,我們先載入了所有背景圖層與小女巫的資源,然後初始化舞台。
  • 物件實例化:我們將 ParallaxBackgroundwitch 實例化,並加入舞台中。
  • 加入主循環:最後,我們使用 CG.Base2.addUpdateFunctionparallaxBackground.update(dt) 函數註冊到遊戲的主更新循環中,確保每一幀畫面繪製前,背景都會移動。

視差捲軸 預覽

點我查看範例程式碼

▸ 總結

今天小女巫終於有了背景舞台,我們完成了 《小女巫・啟程》 最重要的視覺基石:視差捲軸

透過 TilingSprite 和一個獨立的 ParallaxBackground 類別,我們成功地用三層移動速度不同的背景,營造出了小女巫正在持續向右飛行的錯覺。

然而,目前的小女巫還只是個漂亮的靜物,她無法響應我們的操作。所以明天,我們將實作「玩家操控」,為小女巫加入上下移動的功能,讓我們真正開始掌握她在遊戲世界中的命運!


上一篇
Day 15:告別基礎教學!從今天開始,我們「直接做遊戲」
系列文
用 PixiJS 寫遊戲!告別繁瑣設定,在 Code.Gamelet 打造你的第一個遊戲16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言