# PixiJS 研究
1. 官方Github: https://github.com/pixijs/pixijs
2. Demo參考: https://pixijs.io/examples/#/demos-basic/container.js
3. 官方教學文件:https://github.com/kittykatattack/learningPixi
英文版使用 Pixi v5.3.10,中文版使用 Pixi v4.5.5
4. PixiJs Api: https://api.pixijs.io/index.html
5. TypeScript PixiJS: https://github.com/pixijs/pixi-typescript
:::spoiler ~~現成 typescript+pixijs 的 github 專案 (測試中)~~
https://github.com/GuillaumeDesforges/pixijs-typescript-starter
下載後使用 vscode 開啟,在終端機輸入
```
yarn install
```
安裝相關的 package 後直接執行就能跑起來
不過此專案的 package 版本並不是最新的,可以使用指令
```
yarn upgrade --latest
```
更新全部 package 的版本,但是目前測試升級後專案無法成功執行起來...
:::
6. webpack設定+簡易範例: https://jameskiefer.com/posts/getting-started-with-pixi.js-and-webpack/
7. anchor預設為(0,0): https://api.pixijs.io/@pixi/sprite/PIXI/Sprite.html#anchor
8. 免費好用的spritesheet軟體 [Free Texture Packer](http://free-tex-packer.com)
---
### 載入SpriteSheet
:::success
```javascript=
const loader = new PIXI.Loader();
loader.add("相對路徑/檔名.json")
.load((loader: PIXI.Loader, resources: PIXI.utils.Dict<PIXI.LoaderResource>)=>{
const sheet = loader.resources["相對路徑/檔名.json"].spritesheet;
const img = new PIXI.Sprite(sheet.textures["sprite名稱.副檔名"])
});
```
:::
:::success
切九宮格 [文件](https://pixijs.download/dev/docs/PIXI.NineSlicePlane.html)
```javascript=
const img = new PIXI.NineSlicePlane(sheet.textures["sprite名稱.副檔名"], 30, 20, 30, 20);
```
:::
---
### 事件
:::success
使用 [PIXI.utils.EventEmitter](https://api.pixijs.io/@pixi/utils/PIXI/utils/EventEmitter.html), 基於 [EventEmitter3](https://github.com/primus/eventemitter3)
#### 簡易範例
:::spoiler
```javascript=
import * as PIXI from "pixi.js";
export enum CustomEvent {
atlasLoaded = "atladLoaded"
}
export class EventDispatcher extends PIXI.utils.EventEmitter {
static private inst: EventDispatcher;
public static get instance(): EventDispatcher {
return this.inst;
}
public static create() {
if (this.inst == null) this.inst = new EventDispatcher();
}
public static destroy() {
if (this.inst != null) this.inst.removeAllListeners();
this.inst = null;
}
public static addListener(event: CustomEvent, callback: PIXI.utils.EventEmitter.ListenerFn, thisObj: any) {
this.inst.on(event, callback, thisObj);
}
public static removeListener(event: CustomEvent, callback: PIXI.utils.EventEmitter.ListenerFn, thisObj: any) {
this.inst.off(event, callback, thisObj);
}
public static dispatch(event: CustomEvent, data?: any) {
if (data == null) this.inst.emit(event);
else this.inst.emit(event, data);
}
}
```
<br>
註冊和接收
```javascript=
import * as PIXI from "pixi.js";
import { EventDispatcher, CustomEvent } from "./EventDispatcher";
...略
EventDispatcher.addListener(CustomEvent.atlasLoaded, this.onAtlasLoaded, this);
...略
onAtlasLoaded(...args: Array<any>){
console.log(`ATLAS LOADED: ${args[0]}`); //ATLAS LOADED: hi
}
```
<br>
觸發事件
```javascript=
EventDispatcher.dispatch(CustomEvent.atlasLoaded, "hi");
```
:::
---
## Bitmap font 製作
:::success
推薦使用 [BMFont](https://www.angelcode.com/products/bmfont/)
<br>
:::spoiler
1. 開啟 Edit -> Open Image Manager
2. 開啟 (Image -> Import image)
3. 設定素材對應的字元ID
以設定 **數字0** 為例, 滑鼠移到 **數字0** 的位置, **ID** 是圖片下方顯示的數字

4. 開啟 Options -> Export Options
* Bit depth 預設 8, 但必需選擇 **<font color=red>32</font>**, 才能保持色彩和透明度
* Textures 預設 tga, 改選擇 **<font color=red>png</font>**
* Font description 目前測試 **Text**, **XML** 都能在 pixi 正常讀取

5. 輸出字型 Options -> Save bitmap font as...
6. 使用文字編輯器開啟剛剛輸出的 .fnt
7. 修改字型名稱 **<font color=red>info face="字型名稱"</font>**
* 此欄位是在 pixi 使用的字型名稱
* BMFont只能選系統已存在的字型, 預設選 Arial, 無法自定義名稱

8. pixi 使用方式
```javascript
const fnt = loader.resources["assets/test.fnt"].bitmapFont;
const txt = new PIXI.BitmapText("0123456789", {
fontName: "字型名稱"
});
this.stage.addChild(txt);
```

:::
---
## 碰撞偵測
:::success
矩形檢查 - [參考來源](https://hsiangfeng.github.io/javascript/20200518/1745009932/)
:::spoiler
```javascript=
boxesIntersect(a: PIXI物件類型, b: PIXI物件類型) {
var ab = a.getBounds();
var bb = b.getBounds();
return ab.x + ab.width > bb.x && ab.x < bb.x + bb.width && ab.y + ab.height > bb.y && ab.y < bb.y + bb.height;
}
...
const isHitted = this.boxesIntersect(物件A, 物件B);
```
:::
---
### Graphics簡易顏色設定
:::success
Graphics圖形繪製時填入白色, 再設定 tint 來染色
:::spoiler
```javascript=
const shape = new PIXI.Graphics();
shape.beginFill(0xffffff, 0.5);
shape.drawCircle(0, 0, 50);
this.stage.addChild(shape);
shape.tint = 0xff0000;
```
:::
---
## 套用外部shader
:::success
:::spoiler
以載入fragment shader為例
```javascript=
const loader = new PIXI.Loader();
loader.add("相對路徑/檔名.frag").load((loader, resources) => {
const frag = loader.resources["相對路徑/檔名.frag"].data;
const filter = new PIXI.Filter(null, frag);
某sprite.filters = [filter];
});
```
更新 shader uniform 變數
```glsl
uniform float u_time;
```
```javascript=
const data = {
u_time: 0
}
const filter = new PIXI.Filter(null, frag, data);
...
//每個frame更新 data.u_time.value
filter.uniforms.u_time = performance.now() * 0.001;
```
[type 名稱參考來源](https://github.com/pixijs/pixijs/blob/364c2457c6693d6e3ab68786539c53d566503e7b/src/core/renderers/webgl/shaders/Shader.js#L232-L463)
<br>
:::warning
WebGL 2.0 還未普及, 也還不是各家瀏覽器預設啟用的功能
目前先以 WebGL 1.0 (OpenGL ES 2.0, GLSL 1.x) 為主
:::
---
## Render texture
:::success
擷取畫面+裁切特定範圍
:::spoiler
```typescript=
export class Main extends PIXI.Application {
...
const param: PIXI.IBaseTextureOptions = {
width: this.stage.width,
height: this.stage.height
};
...
const screenTex = PIXI.RenderTexture.create(param);
this.renderer.render(this.stage, { renderTexture: screenTex });
const newTex = new PIXI.Texture(screenTex.baseTexture, new PIXI.Rectangle(100, 100, 300, 300));
const sprite = new PIXI.Sprite(newTex);
this.stage.addChild(sprite);
}
```
:::
---
## 骨骼動畫
### spine
:::success
安裝 [pixi-spine](https://github.com/pixijs/spine)
:::spoiler
```nodejs
npm install pixi-spine
```
```typescript=
import { Spine } from "pixi-spine"
...
test(){
const loader = new PIXI.Loader();
loader.add("res名稱", "res相對路徑.json")
.load((loader, resources) => {
const anim = new Spine(loader.res名稱.spineData);
this.stage.addChild(anim);
if(anim.state.hasAnimation("動畫名稱")){
anim.state.setAnimation(0, "動畫名稱", loop);
}
});
}
```
:::
### spine 疑難排解
:::danger
* 無法載入 .atlas 檔案
:::success
:::spoiler
主要發生在 IIS server, 參考 [IIS server設定](https://hackmd.io/oDM9hVv7TpGRPHGN6SLGng)
* 需要新增 MIME 類型
```
.atlas
text/plain
```
:::
:::danger
* Uncaught TypeError: Cannot read properties of undefined (reading 'substr') at detectSpineVersion (loader-uni.es.js:77)
:::success
:::spoiler
此問題主要是透過 dragonBones editor 匯出 spine 格式檔案, 缺少版號資訊
1. 使用文字編輯器開啟 spine 的 json
2. <font color=red>skeleton</font> 欄位底下新增 key 並且填上 spine 版號
```json=
"spine":"3.3"
```
:::
---
## 待確認項目
1. 物件實體沒有 uid, 需要透過第三方套件來產生 ?
2. 如何透過 console 呼叫物件實體 ?
---
## 疑難排解
:::danger
透過 PIXI.Loader 下載資源出現錯誤
Cannot add resources while the loader is running
<br>
:::success
:::spoiler
每個 Loader 同時只能下載一個資源
需要清除 Loader cache 或額外建立新的 Loader 來下載
:::
:::danger
其他相依 pixi 的套件, 無法辨識 PIXI
:::success
:::spoiler
修改 webpack.config.js
```nodejs
const webpack = require("webpack");
const config = {
plugin: [
new webpack.ProvidePlugin({
PIXI: "pixi.js"
}),
]
}
```
:::
---
## performance
* [ParticleContainer](http://pixijs.download/dev/docs/PIXI.ParticleContainer.html)
* [Pixi Performance Tips](https://gist.github.com/GoodBoyDigital/5aea403f8528fb89c4f7639d6a51353f)
* [Inside PixiJS: The ultimate scene graph optimization](https://javascript.plainenglish.io/inside-pixijs-the-ultimate-scene-graph-optimization-35b62bb2153)
#### 延伸閱讀
* [GPU Accelerated Compositing in Chrome](https://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome)
* [Rendering Performance](https://developers.google.com/web/fundamentals/performance/rendering)
* [如何優化像素管道的 Paint 和 Composite](https://cythilya.github.io/2018/07/20/compositing-and-painting/)
---
###### tags: `PixiJS`