# 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** 是圖片下方顯示的數字 ![](https://i.imgur.com/PvG7ctS.png) 4. 開啟 Options -> Export Options * Bit depth 預設 8, 但必需選擇 **<font color=red>32</font>**, 才能保持色彩和透明度 * Textures 預設 tga, 改選擇 **<font color=red>png</font>** * Font description 目前測試 **Text**, **XML** 都能在 pixi 正常讀取 ![](https://i.imgur.com/GGhK1p5.png) 5. 輸出字型 Options -> Save bitmap font as... 6. 使用文字編輯器開啟剛剛輸出的 .fnt 7. 修改字型名稱 **<font color=red>info face="字型名稱"</font>** * 此欄位是在 pixi 使用的字型名稱 * BMFont只能選系統已存在的字型, 預設選 Arial, 無法自定義名稱 ![](https://i.imgur.com/AafOp8b.png) 8. pixi 使用方式 ```javascript const fnt = loader.resources["assets/test.fnt"].bitmapFont; const txt = new PIXI.BitmapText("0123456789", { fontName: "字型名稱" }); this.stage.addChild(txt); ``` ![](https://i.imgur.com/3HDrHbG.png) ::: --- ## 碰撞偵測 :::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`