PixiJS 是一個(gè)使用便捷且高效的2D渲染引擎——沒錯(cuò),它不是大而全的游戲引擎,而是更輕量的渲染引擎。
這也使得它更專注于做好高效的2D渲染工作,給予WebGL高效渲染,實(shí)現(xiàn)上萬對(duì)象渲染的粒子效果;同時(shí)也提供了更高的自由度,可用于做任何游戲類型的渲染層,甚至僅僅用于宣傳頁面的2D動(dòng)畫繪制。
(資料圖片僅供參考)
同時(shí),作為渲染引擎,它又比純粹的 Canvas 使用起來更為便捷,可以直接通過操作 Sprite
、Container
、Graphics
等對(duì)象的屬性完成畫面中渲染效果的更新。
這樣輕量易上手而又高效的渲染引擎,對(duì)于快速搭建輕量級(jí)的H5小游戲或者游戲 demo 來說可謂再合適不過。
而且,從2014年10月的第一個(gè)版本發(fā)布至今已過去近十年,仍然在不斷更新迭代。2022年的 PixiJS v6 開始更是提供了 TypeScript 的支持,提供了內(nèi)部對(duì)象更加方便的智能提示支持,也讓大型項(xiàng)目使用 TS 開發(fā)后更加規(guī)范和可維護(hù)。最新的 v7 更是拋棄了各種歷史包袱,更新到了現(xiàn)代化的前端項(xiàng)目生態(tài),并且改進(jìn)了一些歷史 API(比如 interactive
),提供新的更深入優(yōu)化項(xiàng)目性能的能力。
對(duì)于諸如骨骼動(dòng)畫、游戲?yàn)V鏡、物理引擎、跨平臺(tái)框架等需求,PixiJS 也有各種第三方工具、插件的支持,可擴(kuò)展性也十分優(yōu)秀。
這樣優(yōu)秀的工具,卻可能因?yàn)楣俜綀F(tuán)隊(duì)人力有限無暇顧及文檔維護(hù),或是覺得都是基本的開發(fā)概念不需要再重新寫文檔贅述,官方的文檔較為簡(jiǎn)陋,基本只是羅列 API 的參考手冊(cè)。
對(duì)于之前沒太多了解的新同學(xué)來說,上手可能要走不少彎路。
于是就想在個(gè)人學(xué)習(xí)的筆記基礎(chǔ)上,梳理一個(gè)從基礎(chǔ)概念開始的學(xué)習(xí)流程供大家參考,希望能對(duì)有需要的同學(xué)有所幫助。
首先,我們來搭建一個(gè)使用 PixiJS 渲染的游戲項(xiàng)目。
如果只是想快速體驗(yàn),可以參考官方文檔指南,在頁面內(nèi)通過 <script>
標(biāo)簽引入 PixiJS 的 dist 文件后,直接在靜態(tài)項(xiàng)目?jī)?nèi)體驗(yàn)使用 PixiJS:
<script src="https://pixijs.download/release/pixi.js"></script><script> const { Application } = window.PIXI; const app = new Application({ width: 640, height: 360, backgroundColor: 0x6495ed, }); document.body.appendChild(app.view);</script>
這一方式的優(yōu)點(diǎn)是快速可用。
但缺點(diǎn)也很明顯,沒有構(gòu)建環(huán)境的支持無法使用 TypeScript 等相關(guān)能力,也不具備 Tree Shaking 優(yōu)化項(xiàng)目產(chǎn)物大小等前端構(gòu)建項(xiàng)目的常用特性。
這一途徑則是在現(xiàn)有的前端構(gòu)建項(xiàng)目中,通過 npm/pnpm 安裝 PixiJS,再 import 需要的模塊到頁面內(nèi)進(jìn)行開發(fā)。
優(yōu)點(diǎn)是可以完整地使用所有 PixiJS 應(yīng)有的能力,以及前端構(gòu)建項(xiàng)目所具有的所有便捷特性。缺點(diǎn)是搭建最初的項(xiàng)目結(jié)構(gòu)稍微需要花一點(diǎn)時(shí)間。
推薦使用 Vite 創(chuàng)建一個(gè)基本的 Vanilla + TypeScript 項(xiàng)目,再安裝 pixi.js
和幾個(gè)常用的 PixiJS 基本子包:
$ npm create vite@latest my-pixi-demo$ cd my-pixi-demo$ npm install -S pixi.js @pixi/utils
然后清空項(xiàng)目的入口腳本(一般為 src/main.ts),修改為:
import { Application } from "pixi.js";const app = new Application({ width: 640, height: 360, backgroundColor: 0x6495ed,});document.body.appendChild(app.view as HTMLCanvasElement);
啟動(dòng)開發(fā)構(gòu)建服務(wù):
$ npm run dev
點(diǎn)擊打開出現(xiàn)的開發(fā)預(yù)覽頁面鏈接,不出意外的話,就能看到游戲的畫布出現(xiàn)在瀏覽器內(nèi)了。
剛才我們搭建完項(xiàng)目后,創(chuàng)建了一個(gè) PixiJS 提供的 Application
對(duì)象,它就是我們開發(fā)的 游戲應(yīng)用了。
只不過目前它里面空空如也,只是繪制了一個(gè)指定背景色和寬高尺寸的空畫布。
接下來我們就要往里面加入各種成員,讓它熱鬧起來。
import { Application, Graphics, Sprite, Text,} from "pixi.js";const app = new Application({ width: 640, height: 360, backgroundColor: 0x6495ed,});document.body.appendChild(app.view as HTMLCanvasElement);// 創(chuàng)建文本成員const slogan = new Text("Hello, developer!", { fill: 0xffffff, fontSize: 32,});slogan.position.set(20, 20);// 創(chuàng)建圖形成員const sloganBg = new Graphics();sloganBg.position.set(10, 10);sloganBg.lineStyle(4, 0x333333, .25);sloganBg.beginFill(0xefefef, .5);sloganBg.drawRoundedRect(0, 0, slogan.width + 20, slogan.height + 20, 10);sloganBg.endFill();app.stage.addChild(slogan, sloganBg);// 創(chuàng)建精靈成員const sprite = Sprite.from("https://hk.krimeshu.com/sprite-minion.png");sprite.anchor.set(0.5, 0.5);sprite.position.set(app.screen.width / 2, app.screen.height / 2);app.stage.addChild(sprite);
效果大致如下:
上面的例子中,除了之前提到的 Application
之外,主要有以下幾個(gè)新面孔:
Text
Graphics
Sprite
以及 Application
的幾個(gè)成員:
顯然,Text
、Graphics
和 Sprite
將會(huì)是我們之后開發(fā)游戲常用的成員類型。其中的 Text
和 Graphics
顧名思義很好理解,就是 文本和 圖形。而 Sprite
其實(shí)也是它的字面意思“精靈”,它是具有圖形材質(zhì)和一系列屬性、操作方法的成員對(duì)象,是我們?cè)谟螒蛑兄苯硬僮鞯幕A(chǔ)單元之一。
如果查看他們的 type 聲明就會(huì)發(fā)現(xiàn),它們具有這樣的繼承派生關(guān)系:
符號(hào)
->
表示繼承。
Graphics -> ContainerSprite -> ContainerText -> Sprite -> Container
可見它們都屬于一個(gè)共同的祖先類別 Container
,而 Container
又繼承于更原始的 DisplayObject
。
可推測(cè) DisplayObject
是 PixiJS 中可用于繪制的 可顯示對(duì)象,應(yīng)該是渲染底層操作的基礎(chǔ)單位。
而 Container
是在 DisplayObject
的基礎(chǔ)上具有類似 Web 節(jié)點(diǎn)性質(zhì)的樹形結(jié)構(gòu)對(duì)象。整個(gè)游戲需要繪制的成員,都以嵌套的樹形結(jié)構(gòu)最終掛載于 app.stage
這個(gè)頂級(jí) Container
之下。
實(shí)際上因?yàn)?PixiJS 沒有 CSS 的層級(jí)概念,繪制時(shí)其實(shí)就是按照遍歷整個(gè) app.stage
的樹形結(jié)構(gòu),從上到下、從前到后進(jìn)行繪制,后繪制對(duì)象覆蓋先繪制的對(duì)象的優(yōu)先級(jí)來決定層級(jí)覆蓋關(guān)系。
Graphics
、Sprite
和 Text
則是在 Container
基礎(chǔ)上,擁有更多特化后的繪制能力和操作方法的可顯示對(duì)象具體子類。將它們的實(shí)例通過 addChild
加入到游戲的 app.stage
中,就會(huì)被 PixiJS 繪制出來,最終出現(xiàn)在我們眼前了。
const text1 = new Text("...");const text2 = new Text("...");// ...app.stage.addChild( text1, text2, //...);
除了 app.stage
之外,上面還用到了 app.screen
和 app.view
這兩個(gè) Application
的屬性。
通過查看類型定義,我們發(fā)現(xiàn)前者的類型是 Rectangle
,即矩形,對(duì)其的官方定義為:
Rectangle
對(duì)象是一個(gè)由它左上角的 原點(diǎn)(x, y)
和自身 寬度width
+高度height
定義的區(qū)域。
而 app.screen
就是我們整個(gè)游戲應(yīng)用的矩形渲染區(qū)域,平時(shí)游戲中只有位于這個(gè)區(qū)域內(nèi)的可顯示對(duì)象才能被用戶在頁面上看到。
最后的 app.view
則是 PixiJS 應(yīng)用的渲染器所持有的 Canvas 畫布對(duì)象引用。
在我們的例子中,因?yàn)閯?chuàng)建 Application
時(shí)沒有傳入畫布對(duì)象,所以 PixiJS 內(nèi)部會(huì)幫我們創(chuàng)建符合指定屬性的畫布,并掛載在 app
實(shí)例的 view
屬性上。在這一切完成之后,我們最后將創(chuàng)建的 app.view
畫布通過 appendChild()
加入到頁面的 DOM 樹內(nèi)。
同樣的,我們也可以不使用自動(dòng)創(chuàng)建的畫布,而是使用頁面上已有的 Canvas 畫布對(duì)象來創(chuàng)建 Application
應(yīng)用對(duì)象:
const canvas = document.querySelector("#cvsMyGame");const app = new Application({ view: canvas, width: canvas.width, height: canvas.height,});
這個(gè)例子里,如果我們不將 canvas 的寬高傳給 Application
的構(gòu)造參數(shù),PixiJS 將會(huì)用一個(gè)默認(rèn)的尺寸創(chuàng)建游戲,并修改為 canvas 的新寬高。所以還是需要獲取后賦值傳入,稍顯啰嗦。
如果我們的游戲是面向移動(dòng)端設(shè)備開發(fā)的話,還需要增加一個(gè)分辨率參數(shù),以適配高分辨率設(shè)備的像素密度:
const app = new Application({ view: canvas, width: canvas.width, height: canvas.height, resolution: window.devicePixelRatio || 2,});
不過如果我們的游戲應(yīng)用與網(wǎng)頁視口的尺寸始終保持一致(即所謂的“全屏游戲”)的話,其實(shí)可以也不用傳入這么多參數(shù),只需要這樣配置:
const app = new Application({ view: canvas, resizeTo: window, autoDensity: true,});
通過 resizeTo
屬性指定應(yīng)用畫布跟隨網(wǎng)頁窗口尺寸,還可以在用戶屏幕旋轉(zhuǎn)、調(diào)整窗口尺寸后由 PixiJS 自動(dòng)調(diào)整畫布尺寸,以適配用戶設(shè)備的最新畫面狀態(tài)。
——不過頁面內(nèi)的成員坐標(biāo)和尺寸并不會(huì)按新舊尺寸的比例進(jìn)行調(diào)整更新,畢竟實(shí)際游戲場(chǎng)景的成員數(shù)可能相當(dāng)多,而且不同成員的定位適配策略通常并不相同,還是需要在檢測(cè)到對(duì)應(yīng) resize
事件后自行調(diào)整。
這次我們創(chuàng)建了一個(gè)基本的 PixiJS 游戲應(yīng)用,并對(duì)一些基礎(chǔ)概念進(jìn)行了說明。
但這個(gè)基本 demo 中還是有不少東西沒有說清楚,并且這個(gè)應(yīng)用的代碼也沒有合理組織,之后我們將在這個(gè)基礎(chǔ)上繼續(xù)補(bǔ)充和完善。
如果有什么紕漏與謬誤歡迎指出~
關(guān)鍵詞: