iT邦幫忙

2025 iThome 鐵人賽

DAY 12
1

前兩天,我們介紹了如何用循環更新來處理持續性的物件移動,以及如何用補間動畫(Tween)來實現一次性的平滑移動。而今天,我們要來介紹另一種截然不同的技術——Spritesheet

Spritesheet 就像是遊戲世界的定格動畫,它用一系列的靜態圖片,在極短的時間內快速切換,從而創造出流暢的視覺效果。

▸ 什麼是 Spritesheet?

Spritesheet 是一種將多個小圖(動畫幀)打包成一張大圖的技術。你可能在許多遊戲的資源檔中看過它,它看起來就像一張包含所有角色動作的大拼圖。

RPG Maker Sprite Sheet

素材來源:https://despairparty.itch.io/rpgmaker-spriteface
作者:Despair Party Works

這種技術之所以被廣泛使用,主要有兩個原因:

  • 節省資源,載入更快:如果一個角色有 30 幀的走路動畫,我們需要載入 30 個不同的圖檔。但使用 Spritesheet 後,我們只需要載入一張圖片和一個描述檔案(通常是 .json),這大大減少了網路請求的數量,讓遊戲載入速度更快。
  • 效能更佳:在遊戲開發中,渲染圖片(綁定紋理)是一個耗費效能的步驟。如果每一幀動畫都要切換不同的圖片,會給 GPU 帶來額外的負擔。Spritesheet 讓所有動畫幀都在同一張大圖上,GPU 只需要綁定一次紋理,就能在內部快速切換顯示不同區域的圖片,從而大大提升渲染效能。

基於上述優點,Spritesheet 就算不拿來製作動畫,也常常被用來整合所有靜態圖片資源,這是一種常見的優化方式。

▸ 載入 Spritesheet

要在 PixiJS 上使用 Spritesheet,我們通常需要兩個檔案:

  • 圖片檔(.png):包含所有動畫幀的大圖。
  • 描述檔(.json):包含每一幀圖片的名稱、位置、大小等資訊。

在 CG 上,Spritesheet 被稱為圖集動畫,同樣可以作為資源素材上傳至專案中,但由於需要包含兩種檔案,因此上傳時需要先壓縮成 .zip 檔案,而且壓縮檔內的結構有所規定:

Spritesheet 名稱.zip
└── Spritesheet 名稱
    ├── Spritesheet 名稱.png
    └── Spritesheet 名稱.json

你需要將 .png 檔與 .json 檔放在一個資料夾內,然後將這個資料夾包含在 .zip 壓縮檔內,並且這四個檔案的名稱必須一模一樣,這樣才能將壓縮檔上傳至 CG 專案資源管理中。

CG 資源管理 Spritesheet

如上圖,在 CG 的資源管理中,可以查看該 Spritesheet 的動畫資料:

  • 圖幀列表:每一個 Frame(動畫幀)的名稱,也就是每一張小圖的名稱。
  • 動畫列表:每一個動畫的名稱。

素材來源:https://arks.itch.io/dungeon-platform-tileset
作者:Arks💢
如果有需要可以在 CG 載入資源時輸入 Dunjo 搜尋圖集動畫,載入由 cook1470 最新上傳的素材即可,也是本次介紹所使用的素材。

PIXI.Texture

由於之前在創建 Sprite 的時候,都是使用 pixi.assets.createSprite("資源別名"),只需要帶入專案的資源別名即可,雖然在 Day 03 有小提示關於 Texture,但沒有深入介紹到,因此這邊稍微補充一下。

Texture 是 PixiJS 中代表一張圖片紋理的物件。當我們使用 pixi.assets.createSprite() 時,它其實在背後幫我們自動完成了兩件事:首先,取得資源別名對應的 Texture;然後,用這個 Texture 來創建一個 Sprite

// 取得圖片資源別名對應的 Texture,並創建 Sprite
const texture: PIXI.Texture = pixi.assets.getTexture("ironman2025_cook.圖片.女巫");
const sprite: PIXI.Sprite = new PIXI.Sprite(texture);

如上,因此我們不太會需要操作到關於 Texture 的部分,但接下來的 AnimatedSprite 就會用到了。

PIXI.AnimatedSprite

AnimatedSprite 就是動畫版的 SpriteSprite 一次只接受一個 Texture,但它可以一次接收很多個,並藉由不斷切換不同的 Texture,從而產生動畫效果。

import pixi = CG.Pixi.pixi;

async function start() {

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

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

	// 取得 Spritesheet 資源
	const asset: PIXI.Spritesheet = pixi.assets.getSpritesheet("ironman2025_cook.圖集動畫.Dunjo");

	// 創建 PIXI.AnimatedSprite,並設定播放速度、自動播放
	const animatedSprite = new PIXI.AnimatedSprite({
		textures: asset.animations["player"], // 使用動畫紋理 player
		animationSpeed: 0.1,				  // 設定播放速度為 0.1
		autoPlay: true, 					  // 啟用自動播放
		anchor: 0.5,
		position: { x: pixi.stageWidth * 0.5, y: pixi.stageHeight * 0.5 }
	});

	// 將 animatedSprite 加入舞台顯示
	pixi.root.addChild(animatedSprite);
}

start();

在這段程式碼中:

  1. pixi.assets.getSpritesheet():這個方法能取得我們載入的 Spritesheet 資源物件。
  2. asset.animations["player"]:一個 Spritesheet 可以包含多個動畫(例如:走路、攻擊、死亡)。asset.animations 是一個物件,其中包含了所有動畫的 Texture 陣列。
  3. new PIXI.AnimatedSprite({...}):與 Text 相同,新版的 PixiJS 幾乎將所有能夠被 new 出來的物件,都支援了能將所有的參數(包括紋理、速度、錨點等)都放在一個物件 {} 內傳入,這樣程式碼會更清晰。

Dunjo Player

AnimatedSprite 包含了以下幾個常用的屬性、函數:

  • textures:動畫使用的 Texture 陣列。
  • autoPlay:布林值,設定是否在創建時自動播放動畫。
  • animationSpeed:控制動畫的播放速度,預設速度為 1
  • loop:布林值,設定動畫是否循環播放。
  • play():開始播放動畫。
  • stop():停止播放動畫。
  • gotoAndPlay(frame):跳到指定的幀並開始播放。
  • gotoAndStop(frame):跳到指定的幀並停止。
  • onComplete:動畫播放完畢時觸發的回呼函式(只在 loopfalse 時有效)。

▸ 作為靜態資源庫

除了製作動畫,Spritesheet 另一個主要用途就是作為一個靜態資源庫。當你需要使用圖集中的某個單一圖片時,可以透過它的「圖幀名稱」來單獨取得紋理。這對於管理 UI 介面的各式按鈕、圖示、或視窗背景等小圖片非常有用。

// 取得圖幀列表的 key 紋理
const texture = asset.textures["key"];
// 使用 key 紋理創建 Sprite
const key = new PIXI.Sprite({
	texture: texture,
	anchor: 0.5,
	position: { x: pixi.stageWidth * 0.5, y: pixi.stageHeight * 0.4 }
});

// 將 key 加入舞台顯示
pixi.root.addChild(key);

我們可以從已載入的 Spritesheet 資源中,單獨取出名為 "key" 的圖片紋理,並用它來創建一個靜態的 Sprite 物件。

點我查看範例程式碼

Ryvexia Icon Spritesheet

上圖正是我自己幫我的音樂遊戲所畫的各種圖示,也是利用 Spritesheet 的方式來整合所有的圖示,當然實際背景不會是黑色的,但因為 iThome 好像只有淺色模式,為了介紹所以加工了一下。(作為程式語言交流的平台卻沒有深色模式太奇怪了!)

Dunjo 遊戲預覽

順帶一提這是我以前使用今天介紹的素材,所做的一款同名 2D 橫向卷軸遊戲,並且提供了自製關卡的功能。

以上兩個遊戲專案的原始碼都是公開的,對我實際上是怎麼使用 CG 平台 + PixiJS 來製作遊戲有興趣的人,可以去翻看看,只是這兩個專案的 PixiJS 版本都是古早版的 v5,所以可能會有不少語法與本次介紹的有所不同。

▸ 總結

今天我們學會了遊戲開發中極為重要的視覺技術——Spritesheet。我們不僅了解了它如何提升遊戲效能與載入速度,也學會了兩種主要用法:

  • 製作動畫:使用 PIXI.AnimatedSprite 實現流暢的動態效果。
  • 作為靜態資源庫:從圖集中單獨取出靜態圖片,用於 UI 或其他物件。

有了讓畫面看起來更酷的動畫效果後,接下來缺的就只剩下聲音了,明天我們將要來介紹如何播放音效、音樂,讓我們的遊戲能夠更加生動活潑!


上一篇
Day 11:物件的平滑移動 - 使用補間動畫 (Tween)
系列文
用 PixiJS 寫遊戲!告別繁瑣設定,在 Code.Gamelet 打造你的第一個遊戲12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言