前幾天纔開始學習phaser,看完教程後第一件事就是想作個動畫效果,可是這纔剛踏出第一步就踩了坑,就此記錄一下。html
這裏我使用TexturePacker把事先準備好的素材生成Phaser3支持的紋理集,TexturePacker使用很是簡單,官網有教程,這裏很少贅述
經過TexturePacker得到了火焰的紋理集和一個json文件
json文件中記錄的都是關於紋理集中每一幀的位置、大小等信息,看不懂不要緊,所幸Phaser會幫咱們解析git
先在preload方法加載資源github
// 讀取assets/sprites/fire.json文件,並命名爲fire,第三個參數則是圖片的路徑 this.load.multiatlas('fire', 'assets/sprites/fire.json', 'assets/sprites')
而後在create方法中把已經加載完成的紋理添加到場景中web
// 三個參數分別是精靈(sprite)的x座標、y座標和紋理的key值,對應上方multiatlas()的第一個參數 const fire = this.add.sprite(400, 100, 'fire')
這樣就把紋理集的第一幀繪製到屏幕上了,很是輕鬆。可是一團火焰不會燃燒可不行,接下來就得讓它燃燒起來。json
建立動畫也很簡單,在create方法中加入如下代碼數組
const frames = this.anims.generateFrameNames('fire', { start: 0, end: 14 }) console.log(frames) this.anims.create({ key: 'burn', frames, repeat: -1 })
回到瀏覽器發現火焰仍是紋絲不動,不急,先看看這些個參數是什麼瀏覽器
這裏能夠看到frames參數比較特別,它是用this.anims的另外一個方法生成的,這就是我踩坑的點。
根據API文檔,這個方法的兩個參數分別是紋理的key和動畫幀名稱的配置,因而點開繼續查看第二個參數的配置屬性,能夠看到有7個屬性。ide
這是我剛看到的理解,根據文檔,start、end跟frames是互斥的,若是傳入frames,start和end就會失效。
因爲文檔中描述得比較模糊(英語渣),而且都是可選參數,我圖省事兒只寫了start和end參數,並從0寫到14,因而瀏覽器給我無情地報了個錯誤
我代碼中並無引用frame屬性的,看來是Phaser的運行時錯誤沒有很好地處理,而且這時候打印frames是一個空數組。因爲國內的Phaser資源較少,通過好一番搜索也沒有發現相關的解決方法,那咋辦呢,看源碼唄。
我在phaser/src/animations/AnimationManager.js路徑下找到了generateFrameNames方法oop
generateFrameNames: function (key, config) { var prefix = GetValue(config, 'prefix', ''); var start = GetValue(config, 'start', 0); var end = GetValue(config, 'end', 0); var suffix = GetValue(config, 'suffix', ''); var zeroPad = GetValue(config, 'zeroPad', 0); var out = GetValue(config, 'outputArray', []); var frames = GetValue(config, 'frames', false); var texture = this.textureManager.get(key); if (!texture) { return out; } var diff = (start < end) ? 1 : -1; // Adjust because we use i !== end in the for loop end += diff; var i; var frame; if (!config) { // Use every frame in the atlas? frames = texture.getFrameNames(); for (i = 0; i < frames.length; i++) { out.push({ key: key, frame: frames[i] }); } } else if (Array.isArray(frames)) { // Have they provided their own custom frame sequence array? for (i = 0; i < frames.length; i++) { frame = prefix + Pad(frames[i], zeroPad, '0', 1) + suffix; if (texture.has(frame)) { out.push({ key: key, frame: frame }); } } } else { for (i = start; i !== end; i += diff) { frame = prefix + Pad(i, zeroPad, '0', 1) + suffix; if (texture.has(frame)) { out.push({ key: key, frame: frame }); } } } return out; }
這段代碼其實不難懂,仔細揣摩一下就能看懂,返回值out是一個數組,就是前邊說的「生成動畫幀的數據」,通過一系列的判斷以後,能夠看到out中的對象的frame屬性老是prefix + xxx + suffix
,原來參數中的start,end,prefix,suffix,frames,zeroPad都是用來生成拼接的字符串的,而咱們須要經過這些參數讓它生成紋理集數據中的幀名
因而將代碼修改學習
const frames = this.anims.generateFrameNames('fire', { start: 1, end: 15, prefix: 'fire', suffix: '.png' })
這樣一來生成的frame分別是fire1.png ~ fire15.png,這時候打印的frames就會是一個15項的數組
而後在代碼最後面加一行代碼就能讓火焰燒起來了fire.play('burn')
new Phaser.Game({ type: Phaser.AUTO, width: 750, height: window.innerHeight, scene: [ { preload() { this.load.multiatlas('fire', 'assets/sprites/fire.json', 'assets/sprites') }, create() { const fire = this.add.sprite(400, 100, 'fire') const frames = this.anims.generateFrameNames('fire', { start: 1, end: 15, prefix: 'fire', suffix: '.png' }) console.log(frames) this.anims.create({ key: 'burn', frames, repeat: -1 }) fire.play('burn') } } ] })
這次踩坑是由於理解錯generateFrameNames()方法中的配置,一開始覺得start和end是每一幀對應的index,因此纔會寫成0和14,但這實際上是跟每一幀的filename對應,經過結合配置參數的前綴、後綴、補0生成,因此根據filename的不一樣傳入不一樣配置纔是正解。