前兩天,我們介紹了如何用循環更新來處理持續性的物件移動,以及如何用補間動畫(Tween)來實現一次性的平滑移動。而今天,我們要來介紹另一種截然不同的技術——Spritesheet。
Spritesheet 就像是遊戲世界的定格動畫,它用一系列的靜態圖片,在極短的時間內快速切換,從而創造出流暢的視覺效果。
Spritesheet 是一種將多個小圖(動畫幀)打包成一張大圖的技術。你可能在許多遊戲的資源檔中看過它,它看起來就像一張包含所有角色動作的大拼圖。
素材來源:https://despairparty.itch.io/rpgmaker-spriteface
作者:Despair Party Works
這種技術之所以被廣泛使用,主要有兩個原因:
基於上述優點,Spritesheet 就算不拿來製作動畫,也常常被用來整合所有靜態圖片資源,這是一種常見的優化方式。
要在 PixiJS 上使用 Spritesheet,我們通常需要兩個檔案:
在 CG 上,Spritesheet 被稱為圖集動畫,同樣可以作為資源素材上傳至專案中,但由於需要包含兩種檔案,因此上傳時需要先壓縮成 .zip
檔案,而且壓縮檔內的結構有所規定:
Spritesheet 名稱.zip
└── Spritesheet 名稱
├── Spritesheet 名稱.png
└── Spritesheet 名稱.json
你需要將 .png
檔與 .json
檔放在一個資料夾內,然後將這個資料夾包含在 .zip
壓縮檔內,並且這四個檔案的名稱必須一模一樣,這樣才能將壓縮檔上傳至 CG 專案資源管理中。
如上圖,在 CG 的資源管理中,可以查看該 Spritesheet 的動畫資料:
素材來源: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
就是動畫版的 Sprite
,Sprite
一次只接受一個 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();
在這段程式碼中:
pixi.assets.getSpritesheet()
:這個方法能取得我們載入的 Spritesheet 資源物件。asset.animations["player"]
:一個 Spritesheet 可以包含多個動畫(例如:走路、攻擊、死亡)。asset.animations
是一個物件,其中包含了所有動畫的 Texture
陣列。new PIXI.AnimatedSprite({...})
:與 Text
相同,新版的 PixiJS 幾乎將所有能夠被 new
出來的物件,都支援了能將所有的參數(包括紋理、速度、錨點等)都放在一個物件 {}
內傳入,這樣程式碼會更清晰。而 AnimatedSprite
包含了以下幾個常用的屬性、函數:
textures
:動畫使用的 Texture
陣列。autoPlay
:布林值,設定是否在創建時自動播放動畫。animationSpeed
:控制動畫的播放速度,預設速度為 1
。loop
:布林值,設定動畫是否循環播放。play()
:開始播放動畫。stop()
:停止播放動畫。gotoAndPlay(frame)
:跳到指定的幀並開始播放。gotoAndStop(frame)
:跳到指定的幀並停止。onComplete
:動畫播放完畢時觸發的回呼函式(只在 loop
為 false
時有效)。除了製作動畫,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
物件。
上圖正是我自己幫我的音樂遊戲所畫的各種圖示,也是利用 Spritesheet 的方式來整合所有的圖示,當然實際背景不會是黑色的,但因為 iThome 好像只有淺色模式,為了介紹所以加工了一下。(作為程式語言交流的平台卻沒有深色模式太奇怪了!)
順帶一提這是我以前使用今天介紹的素材,所做的一款同名 2D 橫向卷軸遊戲,並且提供了自製關卡的功能。
以上兩個遊戲專案的原始碼都是公開的,對我實際上是怎麼使用 CG 平台 + PixiJS 來製作遊戲有興趣的人,可以去翻看看,只是這兩個專案的 PixiJS 版本都是古早版的 v5,所以可能會有不少語法與本次介紹的有所不同。
今天我們學會了遊戲開發中極為重要的視覺技術——Spritesheet。我們不僅了解了它如何提升遊戲效能與載入速度,也學會了兩種主要用法:
PIXI.AnimatedSprite
實現流暢的動態效果。有了讓畫面看起來更酷的動畫效果後,接下來缺的就只剩下聲音了,明天我們將要來介紹如何播放音效、音樂,讓我們的遊戲能夠更加生動活潑!