v8 遷移指南
歡迎來到 PixiJS v8 遷移指南!本文件旨在協助您順利地將專案從 PixiJS v7 轉移至最新、最棒的 PixiJS v8。請遵循下列步驟,確保能成功地進行遷移。
目錄
1. 簡介
PixiJS v8 推出多項令人興奮的變更與改進,可大幅提升渲染器的效能。儘管我們已盡力確保遷移過程能盡可能順利,有些重大變更是無法避免的。本指南將逐步引導您完成遷移 PixiJS v7 專案至 PixiJS v8 的必要步驟。
2. 重大變更
在深入瞭解遷移流程之前,讓我們先來檢閱 PixiJS v8 中推出的重大變更。務必特別注意這些變更,因為它們可能會影響您現有的程式碼庫。
我應該升級嗎?
一般來說,答案是肯定的!但目前可能有些原因建議您暫時不要升級。請問自己以下問題
- 您的專案是否採用尚未移轉至 v8 的 Pixi 函式庫?我們正努力將我們的關鍵函式庫移轉至 v8,但不想對那些使用純 Pixi 的使用者造成阻礙。這意味著有些函式庫目前還沒有 v8 對應版本。如果是這種情況,最好暫緩升級。
已移轉
- 濾鏡
- 音效
- Gif
- 故事書
- UI
- 開啟遊戲
正在進行遷移
- React
- Spine(深奧版本)
即將移轉
- Pixi 圖層(我們可能會將其直接整合到 PixiJS v8 中,作為一項功能而非移轉它)
新的套件結構
PixiJS 自版本 5 以來,已使用個別子套件將其程式碼庫組織成較小的單位。但是,此方法會導致問題,例如不同 PixiJS 版本的安裝衝突,並造成內部快取複雜度。
在 v8 中,PixiJS 已還原為單一套件結構。雖然你仍然可以匯入 PixiJS 的特定部分,但你只需要安裝主套件。
舊的
import { Application } from '@pixi/app';
import { Sprite } from '@pixi/sprite';
新的
import { Application, Sprite } from 'pixi.js';
自訂建置
PixiJS 使用「擴充」系統來增加渲染器功能。PixiJS 預設包含許多擴充,以獲得全面的開箱即用體驗。但是,若要完全控制功能和套件大小,你可以手動匯入特定 PixiJS 組件。
// imported by default
import 'pixi.js/accessibility';
import 'pixi.js/app';
import 'pixi.js/events';
import 'pixi.js/filters';
import 'pixi.js/sprite-tiling';
import 'pixi.js/text';
import 'pixi.js/text-bitmap';
import 'pixi.js/text-html';
import 'pixi.js/graphics';
import 'pixi.js/mesh';
import 'pixi.js/sprite-nine-slice';
// not added by default, everyone needs to import these manually
import 'pixi.js/advanced-blend-modes';
import 'pixi.js/unsafe-eval';
import 'pixi.js/prepare';
import 'pixi.js/math-extras';
import 'pixi.js/dds';
import 'pixi.js/ktx';
import 'pixi.js/ktx2';
import 'pixi.js/basis';
import { Application } from 'pixi.js';
const app = new Application();
await app.init({
manageImports: false, // disable importing the above extensions
});
在初始化應用程式時,你可以停用自動匯入功能,以防止 PixiJS 自動匯入任何擴充。你需要手動匯入它們,如上所示。
還應注意的是,pixi.js/text-bitmap
也會新增 Assets
加載功能。因此,如果你想在初始化渲染器之前載入點陣字型,則需要匯入此擴充。
import 'pixi.js/text-bitmap';
import { Assets, Application } from 'pixi.js';
await Assets.load('my-font.fnt'); // If 'pixi.js/text-bitmap' is not imported, this will not load
await new Application().init();
非同步初始化
現在需要以非同步方式初始化 PixiJS。隨著 WebGPU 渲染器的引入,現在需要等待 PixiJS 才能使用
舊的
import { Application } from 'pixi.js';
const app = new Application();
// do pixi things
新的
import { Application } from 'pixi.js'
const app = new Application();
(async () => {
await app.init({
// application options
});
// do pixi things
})()
有了這項變更,這也表示現在可以將 ApplicationOptions
物件傳遞給 init
函式,而不是建構函式。
紋理調整
紋理結構已修改,以簡化 v7 中幕後變得相當混亂的部分。紋理不再知道或管理資源的載入。這需要你或資產管理員事先完成。紋理只載入已載入完成的資源。這使得管理變得容易許多,因為在建構時就可以驗證紋理,並且就此放著!BaseTexture 已不再存在。相反地,我們現在有了許多可用的 TextureSources。紋理來源結合了紋理的設定,以及如何上傳和使用該紋理。在 v8 中有下列的紋理來源
TextureSource - 你可以渲染或上傳其內容的香草紋理(主要由渲染紋理使用)。ImageSource - 包含某種類型影像資源(例如 ImageBitmap 或 HTML 影像)的紋理來源。CanvasSource - 包含畫布的畫布來源。主要用於渲染畫布或渲染到畫布(WebGPU)。VideoSource - 包含影音的紋理來源。負責更新 GPU 上的紋理,以確保它們保持同步。BufferSource - 包含緩衝區的紋理來源。隨你怎麼做!請確定你的緩衝區類型和格式相容!CompressedSource - 處理壓縮紋理的紋理來源。由 GPU 壓縮紋理格式使用。
雖然大多數時間 Assets
會傳回紋理,但你可能想要自製紋理!可隨心所欲調整!
要建立紋理來源,其簽章與 baseTexture 不同,範例
const image = new Image();
image.onload = function(){
// create a texture source
const source = new ImageSource({
resource: image,
});
// create a texture
const texture = new Texture({
source
});
}
image.src = 'myImage.png';
圖形 API 大修
圖形 API 有幾個關鍵變更。事實上,這可能是 v8 中變更最多的部分。我們已在可能的地方加入不建議使用的項目,但以下為變更簡述
- v8 要求你建構形狀,然後描邊/填滿,而不是開始填滿或描邊然後建構形狀。
Line
術語已改為Stroke
術語
舊的
// red rect
const graphics = new Graphics()
.beginFill(0xFF0000)
.drawRect(50, 50, 100, 100)
.endFill();
// blur rect with stroke
const graphics2 = new Graphics()
.lineStyle(2, 'white')
.beginFill('blue')
.circle(530, 50, 140, 100)
.endFill();
新的
// red rect
const graphics = new Graphics()
.rect(50, 50, 100, 100)
.fill(0xFF0000);
// blur rect with stroke
const graphics2 = new Graphics()
.rect(50, 50, 100, 100)
.fill('blue')
.stroke({width:2, color:'white'});
- 形狀函式已重新調整名稱。各繪圖函式已簡化為較短的名稱版本。但其參數相同
v7 API 呼叫 | v8 API 等效 |
---|---|
drawChamferRect | chamferRect |
drawCircle | circle |
drawEllipse | ellipse |
drawFilletRect | filletRect |
drawPolygon | poly |
drawRect | rect |
drawRegularPolygon | regularPoly |
drawRoundedPolygon | roundPoly |
drawRoundedRect | roundRect |
drawRoundedShape | roundShape |
drawStar | star |
- fills 函式預期有
FillStyle
選項或顏色,而非字串參數。這也取代了beginTextureFill
舊的
const rect = new Graphics()
.beginTextureFill({texture:Texture.WHITE, alpha:0.5, color:0xFF0000})
.drawRect(0, 0, 100, 100)
.endFill()
.beginFill(0xFFFF00, 0.5)
.drawRect(100, 0, 100, 100)
.endFill();
新的
const rect = new Graphics()
.rect(0, 0, 100, 100)
.fill({texture:Texture.WHITE, alpha:0.5, color:0xFF0000})
.rect(100, 0, 100, 100)
.fill({color:0xFFFF00, alpha:0.5});
- stokes 函式預期有
StrokeStyle
選項或顏色,而非字串參數。這也取代了lineTextureStyle
舊:
const rect = new Graphics()
.lineTextureStyle({texture:Texture.WHITE, width:10, color:0xFF0000})
.drawRect(0, 0, 100, 100)
.endFill()
.lineStyle(2, 0xFEEB77);
.drawRect(100, 0, 100, 100)
.endFill();
新的
const rect = new Graphics()
.rect(0, 0, 100, 100)
.stroke({texture:Texture.WHITE, width:10, color:0xFF0000})
.rect(100, 0, 100, 100)
.stroke({color:0xFEEB77, width:2});
- 現在的洞會使用新的
cut
函式。與stroke
和fill
不同,cut
會對前一個形狀產生作用。舊:
const rectAndHole = new Graphics()
.beginFill(0x00FF00)
.drawRect(0, 0, 100, 100)
.beginHole()
.drawCircle(50, 50, 20)
.endHole()
.endFill();
新的
const rectAndHole = new Graphics()
.rect(0, 0, 100, 100)
.fill(0x00FF00)
.circle(50, 50, 20)
.cut();
GraphicsGeometry
已更換為GraphicsContext
,這個變更讓Graphics
資料的分享能更有效率。
舊的
const rect = new Graphics()
.beginFill(0xFF0000)
.drawRect(50, 50, 100, 100)
.endFill();
const geometry = rect.geometry;
const secondRect = new Graphics(geometry);
新的
const context = new GraphicsContext()
.rect(50, 50, 100, 100)
.fill(0xFF0000);
const rect = new Graphics(context);
const secondRect = new Graphics(context);
著色器變更
由於我們現在必須同時容納 WebGL 與 WebGPU 著色器,它們的建構方式已經過調整以考慮到這一點。您會注意到的主要差異(這是針對一般的著色器)是,紋理不再被視為統一體(也就是說,它們不能包含在統一體群組中)。反之,我們有了資源的概念。資源可以是以下幾樣事物:
- TextureSource - 原始紋理
myTexture.source
- TextureStyle - 紋理樣式
myTexture.style
- UniformGroup - 數字型統一體集合
myUniforms = new UniformGroup({})
- BufferResource - 作為統一體群組處理的緩衝區(進階)
現在建立僅限 webgl 的著色器如下所示:
舊
const shader = PIXI.Shader.from(vertex, fragment, uniforms);
新
僅 WebGL
const shader = Shader.from({
gl: { vertex, fragment },
resources, // resource used from above including uniform groups
});
WebGL 和 WebGPU
const shader = Shader.from({
gl: { vertex, fragment },
gpu: {
vertex: {
entryPoint: 'mainVert',
source,
},
fragment: {
entryPoint: 'mainFrag',
source,
},
},
resources, // resource used from above including uniform groups
});
統一體的建構方式也有點不同。在建立它們時,您現在可以提供您希望它成為的變數類型。
舊
const uniformGroup = new UniformGroup({
uTime:1,
});
uniformGroup.uniforms.uTime = 100;
新
const uniformGroup = new UniformGroup({
uTime:{value:1, type:'f32',
});
uniformGroup.uniforms.uTime = 100; // still accessed the same!
了解這個新設定的最佳方式是仔細閱讀網格和著色器範例
濾鏡
濾鏡幾乎完全以相同的方式運作,除非您建立自訂濾鏡。如果是這種情況,則需要考慮上面提到的著色器變更。
舊
const filter = new Filter(vertex, fragment, {
uTime: 0.0,
});
新
const filter = new Filter({
glProgram: GlProgram.from({
fragment,
vertex,
}),
resources: {
timeUniforms: {
uTime: { value: 0.0, type: 'f32' },
},
},
});
如果您正在使用 社群濾鏡,請注意 @pixi/filter-*
套件不再維護 v8,不過您可以直接從 pixi-filters
套件匯入作為子模組。
*舊
import { AdjustmentFilter } from '@pixi/filter-adjustment';
*新
import { AdjustmentFilter } from 'pixi-filters/adjustment';
ParticleContainer
ParticleContainer
已在 v8 中重新設計,以容納比以前更多的粒子。有幾項主要的變更您應該知道
ParticleContainer
不再將 спрайт 視為其子項。相反地,它需要一個 Particle
類別(或實作 IParticle
介面的物件),它遵循這個介面
export interface IParticle
{
x: number;
y: number;
scaleX: number;
scaleY: number;
anchorX: number;
anchorY: number;
rotation: number;
color: number;
texture: Texture;
}
此變更的原因是 Sprite 具有許多額外的屬性和事件,在處理大量粒子時通常不需要。此方法明確移除了我們在 v7 中遇到的所有模糊不清的問題,例如「我的 Sprite 為什麼無法套用濾鏡?」或「我無法在 Sprite 中巢狀加入子項?」變得更具可預測性。此外,由於粒子的輕量級特性,這表示我們可以渲染更多粒子!
所以,沒有任何功能損失,只是 API 微調一下,就能大幅提升效能!
粒子也不會儲存在 ParticleContainer
的 children
陣列中,因為從技術上而言,粒子並非場景圖的一部分(出於效能考量)。相反地,它們儲存在名為 particleChildren
的平面清單中,這是 ParticleContainer
類級的一部分。你可以直接修改此陣列以提高速度,或使用 addParticle
和 removeParticle
方法管理你的粒子。
另一個優化是 ParticleContainer
不會計算自己的邊界,因為這樣會抵銷我們所創造出的效能提升!相反地,初始化 ParticleContainer
時,由你提供一個 boundsArea
。
舊版
const container = new ParticleContainer();
for (let i = 0; i < 100000; i++) {
const particle = new Sprite(texture);
container.addChild(particle);
}
新版
const container = new ParticleContainer();
for (let i = 0; i < 100000; i++) {
const particle = new Particle(texture);
container.addParticle(particle);
}
具有邊界區域
const container = new ParticleContainer({
boundsArea:new Rectangle(0,0,500,500)
});
其他重大變更
已移除
DisplayObject
。Container
現在是所有 PixiJS 物件的基本類別。已移除
updateTransform
,因為節點不再包含任何渲染邏輯我們認知到許多人使用此功能在每格畫面執行自訂邏輯,因此我們新增了一個可用於此目的的新
onRender
功能。舊的
class MySprite extends Sprite {
constructor() {
super();
this.updateTransform();
}
updateTransform() {
super.updateTransform();
// do custom logic
}
}新的
class MySprite extends Sprite {
constructor() {
super();
this.onRender = this._onRender.bind(this);
}
_onRender() {
// do custom logic
}
}Mipmap 產生變更
- BaseTexture 的
mipmap
屬性已更名為autoGenerateMipmaps
。 - 已調整
RenderTextures
的 Mipmap,以便開發人員負責更新其 Mipmap。產生 Mipmap 的成本可能很高,而且由於我們處理紋理採用新的反應式方法,因此我們不希望在不需要時意外產生 Mipmap。
- BaseTexture 的
const myRenderTexture = RenderTexture.create({width:100, height:100, autoGenerateMipmaps:true});
// do some rendering..
renderer.render({target:myRenderTexture, container:scene});
// now refresh mipmaps when you are ready
myRenderTexture.source.updateMipmaps();
- 由於 PixiJS 處理事物的內部方式有變,Sprite 不會再收到紋理的 UV 已修改的通知。最佳做法是不要在建立紋理 UV 後修改它們。最好準備好紋理即可(建立和儲存的成本並不高)。
- 有時,你可能想採用一種特殊技術來為 UV 做動畫。在此最後一個範例中,你將負責更新 Sprite(值得注意的是,它可能自動更新,但由於新的最佳化,這無法保證)。不過,更新來源資料(例如影片紋理)一定會立即反映出來。
const texture = await Assets.load('bunny.png');
const sprite = new Sprite(texture);
texture.frame.width = texture.frame.width/2;
texture.update();
// guarantees the texture changes will be reflected on the sprite
sprite.onViewUpdate();
// alternatively you can hooke into the sprites event
texture.on('update', ()=>{sprite.onViewUpdate});
在 Sprite 的紋理變更時增加和移除事件的行為會導致效能下降,尤其是在變換許多紋理時(想像具有大量關鍵影格紋理變換的射擊遊戲)。因此,我們現在把這項責任交給使用者。
新的容器剔除方法
在這個版本的 PixiJS 中,我們變更了容器中
cullable
屬性的運作方式。以前,剔除會在渲染迴圈中自動執行。不過,我們已經移除此邏輯,並允許使用者自行控制剔除發生的時機。有了這個改變,我們新增了一些新屬性
cullable
- 容器是否可以被剔除cullArea
- 將會使用此剔除區域,而不是容器的邊界cullableChildren
- 容器的子項是否可以被剔除。這有助於最佳化大型場景
新的
const container = new Container();
const view = new Rectangle(0, 0, 800, 600);
container.cullable = true;
container.cullArea = new Rectangle(0,0,400,400);
container.cullableChildren = false;
Culler.shared.cull(myContainer, view);
renderer.render(myContainer);還有一個
CullerPlugin
可以自動呼叫Culler.shared.cull
,如果你想模擬舊行為。import {extensions, CullerPlugin} from 'pixi.js'
extensions.add(CullerPlugin)重命名幾個網格類別
- 將
SimpleMesh
重命名為MeshSimple
- 將
SimplePlane
重命名為MeshPlane
- 重新命名
SimpleRope
->MeshRope
- 將
已移除
Assets
的淘汰函式舊的
import { Assets } from 'pixi.js';
Assets.add('bunny', 'bunny.png');新的
import { Assets } from 'pixi.js';
Assets.add({ alias: 'bunny', src: 'bunny.png' });已移除
settings
物件舊的
import { settings, BrowserAdapter } from 'pixi.js';
settings.RESOLUTION = 1;
settings.FAIL_IF_MAJOR_PERFORMANCE_CAVEAT = false;
settings.ADAPTER = BrowserAdapter;新的
import { AbstractRenderer, DOMAdapter, BrowserAdapter } from 'pixi.js';
// Can also be passed into the renderer directly e.g `autoDetectRenderer({resolution: 1})`
AbstractRenderer.defaultOptions.resolution = 1;
// Can also be passed into the renderer directly e.g `autoDetectRenderer({failIfMajorPerformanceCaveat: false})`
AbstractRenderer.defaultOptions.failIfMajorPerformanceCaveat = false;
// See below for more information about changes to the adapter
DOMAdapter.set(BrowserAdapter);套用器和網頁工作人員的變更
已移除
settings.ADAPTER
,並用DOMAdapter
取代DOMAdapter
是可以用來設定整個應用程式套用器的靜態類別- PixiJS 有兩個內建套用器:
BrowserAdapter
和WebWorkerAdapter
BrowserAdapter
是預設的套用器,並用於瀏覽器中執行時WebWorkerAdapter
用於網頁工作人員中執行時
舊的
import { settings, WebWorkerAdapter } from 'pixi.js';
settings.ADAPTER = WebWorkerAdapter;
settings.ADAPTER.createCanvas();新的
import { DOMAdapter, WebWorkerAdapter } from 'pixi.js';
DOMAdapter.set(WebWorkerAdapter);
DOMAdapter.get().createCanvas();
應用程式類型現在接受渲染器而非檢視,作者為 @Zyie,刊載於 https://github.com/pixijs/pixijs/pull/9740
這是為了讓
app.renderer
可以正確輸入舊的
const app = new Application<HTMLCanvasElement>();
新的
// WebGL or WebGPU renderer
const app = new Application<Renderer<HTMLCanvasElement>>();
// WebGL specific renderer
const app = new Application<WebGLRenderer<HTMLCanvasElement>>();
// WebGPU specific renderer
const app = new Application<WebGPURenderer<HTMLCanvasElement>>();
Texture.from
將不再從 URL 載入紋理。使用
Texture.from
時,您需要傳入一個來源,例如CanvasSource
/ImageSource
/VideoSource
,或一個資源,例如HTMLImageElement
/HTMLCanvasElement
/HTMLVideoElement
,或透過Assets.load
載入的字串。舊的
import { Texture } from 'pixi.js';
const texture = Texture.from('https://i.imgur.com/IaUrttj.png');新的
import { Assets, Texture } from 'pixi.js';
await Assets.load('https://i.imgur.com/IaUrttj.png');
const texture = Texture.from('https://i.imgur.com/IaUrttj.png');
Ticker
的回呼函式現在將傳遞Ticker
實體,而不是 delta 時間。這是為了允許進一步控制所使用時間單位的動作。舊的
Ticker.shared.add((dt)=> {
bunny.rotation += dt
});新的
Ticker.shared.add((ticker)=> {
bunny.rotation += ticker.deltaTime;
});已重新命名文字剖析器
TextFormat
->bitmapFontTextParser
XMLStringFormat
->bitmapFontXMLStringParser
XMLFormat
->bitmapFontXMLParser
預設的
eventMode
現在是passive
,而非auto
已移除
utils
。所有函式都可作為直接匯入使用。舊的
import { utils } from 'pixi.js';
utils.isMobile.any();新的
import { isMobile } from 'pixi.js';
isMobile.any();container.getBounds()
現在傳回Bounds
物件,而非Rectangle
物件。您可以改用container.getBounds().rectangle
來存取矩形。舊的
const bounds = container.getBounds();
新的
const bounds = container.getBounds().rectangle;
3. 已淘汰的功能
PixiJS v7 的某些功能已在 v8 中淘汰。儘管這些功能仍然可用,但建議您更新您的程式碼以使用新的替代方案。請參閱已淘汰的功能部分,了解如何替換這些功能。
樹葉節點不再允許子節點
只有
Containers
可以有子節點。這表示Sprite
、Mesh
、Graphics
等不再有子節點。若要複製舊有的行為,您可以建立一個
Container
,並將樹葉節點新增到其中。舊的
const sprite = new Sprite();
const spriteChild = new Sprite();
sprite.addChild(spriteChild);新的
const container = new Container();
const sprite = new Sprite();
const spriteChild = new Sprite();
container.addChild(sprite);
container.addChild(spriteChild);Application.view
已改用Application.canvas
舊的
const app = new Application({ view: document.createElement('canvas') });
document.body.appendChild(app.view);新的
const app = new Application();
await app.init({ view: document.createElement('canvas') });
document.body.appendChild(app.canvas);NineSlicePlane
已重新命名為NineSliceSprite
SCALE_MODES
已改用ScaleMode
字串SCALE_MODES.NEAREST
->'nearest'
,SCALE_MODES.LINEAR
->'linear'
,
WRAP_MODES
已改用WrapMode
字串WRAP_MODES.CLAMP
->'clamp-to-edge'
,WRAP_MODES.REPEAT
->'repeat'
,WRAP_MODES.MIRRORED_REPEAT
->'mirror-repeat'
,
DRAW_MODES
已改用Topology
字串DRAW_MODES.POINTS
->'point-list'
,DRAW_MODES.LINES
->'line-list'
,DRAW_MODES.LINE_STRIP
->'line-strip'
,DRAW_MODES.TRIANGLES
->'triangle-list'
,DRAW_MODES.TRIANGLE_STRIP
->'triangle-strip'
,
建構函式已大幅變更,可用物件取代多個參數
舊的
const blurFilter = new BlurFilter(8, 4, 1, 5);
const displacementFilter = new DisplacementFilter(sprite, 5);
const meshGeometry = new MeshGeometry(vertices, uvs, index);
const mesh = new Mesh(geometry, shader, state, drawMode);
const plane = new PlaneGeometry(width, height, segWidth, segHeight);
const nineSlicePlane = new NineSlicePlane(texture, leftWidth, topHeight, rightWidth, bottomHeight);
const tileSprite = new TileSprite(texture, width, height);
const text = new Text('Hello World', style);
const bitmapText = new BitmapText('Hello World', style);
const htmlText = new HTMLText('Hello World', style);新的
const blurFilter = new BlurFilter({
blur: 8,
quality: 4,
resolution: 1,
kernelSize: 5,
});
const displacementFilter = new DisplacementFilter({
sprite,
scale: 5,
});
const meshGeometry = new MeshGeometry({
positions: vertices,
uvs,
indices: index,
topology: 'triangle-list';
shrinkBuffersToFit: boolean;
});
const mesh = new Mesh({
geometry
shader
texture
});
const plane = new PlaneGeometry({
width,
height,
verticesX: segWidth,
verticesY: segHeight,
});
const nineSliceSprite = new NineSliceSprite({
texture,
leftWidth,
topHeight,
rightWidth,
bottomHeight,
});
const tileSprite = new TileSprite({
texture,
width,
height,
});
const text = new Text({
text: 'Hello World',
style,
});
const bitmapText = new BitmapText({
text:'Hello World',
style,
});
const htmlText = new HTMLText({
text:'Hello World',
style,
});container.name
現在是container.label