Phaser3中使用紋理集建立動畫

前幾天纔開始學習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
})

回到瀏覽器發現火焰仍是紋絲不動,不急,先看看這些個參數是什麼瀏覽器

  • key: 做爲這個動畫的名稱
  • frames: 生成動畫幀的數據
  • repeat: 動畫播放次數,-1爲無限循環

這裏能夠看到frames參數比較特別,它是用this.anims的另外一個方法生成的,這就是我踩坑的點。
根據API文檔,這個方法的兩個參數分別是紋理的key和動畫幀名稱的配置,因而點開繼續查看第二個參數的配置屬性,能夠看到有7個屬性。ide

  • start: 開始
  • end: 結束
  • prefix: 前綴
  • suffix: 後綴
  • zeroPad: 補0
  • outputArray: 輸出數組,能夠把生成的動畫幀數據push到這個數組當中
  • frames: 幀編號數組

這是我剛看到的理解,根據文檔,start、end跟frames是互斥的,若是傳入frames,start和end就會失效。
因爲文檔中描述得比較模糊(英語渣),而且都是可選參數,我圖省事兒只寫了start和end參數,並從0寫到14,因而瀏覽器給我無情地報了個錯誤
Cannot read property 'frame' of undefined
我代碼中並無引用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都是用來生成拼接的字符串的,而咱們須要經過這些參數讓它生成紋理集數據中的幀名
fire.json
因而將代碼修改學習

const frames = this.anims.generateFrameNames('fire', {
  start: 1,
  end: 15,
  prefix: 'fire',
  suffix: '.png'
})

這樣一來生成的frame分別是fire1.png ~ fire15.png,這時候打印的frames就會是一個15項的數組
image.png
而後在代碼最後面加一行代碼就能讓火焰燒起來了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的不一樣傳入不一樣配置纔是正解。

相關文章
相關標籤/搜索