iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 3
2
Modern Web

三十天路邊賭場上線了!系列 第 3

DAY03 產出Sprite圖、載入圖片

前言

昨天我們已經準備好基本的物件了,今天我們來載入我們圖檔吧!其實圖片載完、基本的動畫反應製作,遊戲介面差不多就好了。

Sprite圖

俗稱雪碧圖,就是一張png內含很多圖片,可以用來節省請求資源,或是動畫圖。以下是我們從淘寶買來的一張籌碼圖,沒錯他沒有切好spritesheet,也許cocos有相關功能,但現階段使用pixijs也沒有特別的開發工具,就自己手切囉!
https://ithelp.ithome.com.tw/upload/images/20190904/20109783EbmXI540kb.png

就是這樣,把他切下來後輸出
https://ithelp.ithome.com.tw/upload/images/20190904/20109783j1eZYLHoUf.png

然後呢把這幾張圖片丟到 Texturepacker
https://ithelp.ithome.com.tw/upload/images/20190904/201097831zHaUcqHvJ.png
右上角儲存spritesheet.json和spritesheet.png
https://ithelp.ithome.com.tw/upload/images/20190904/201097834m2tOESS4F.png
就會有這些Spritesheet,我們就可以把這些導入到我們的pixijs盡情使用了!其他圖也是這樣切出來的!

載入Pixijs

首先我們先建立以下結構,新增objects資料夾並新增一ts檔案Chip.ts。config資料夾為chiptype.ts以及imagePath.ts

└─src
    │  App.ts
    ├─components
    │  ├─elements
    │  │      Animation.ts
    │  │      Sprite.ts
    │  │      Texture.ts
    │  │      Wrapper.ts
    │  │      WrapperContainer.ts
    │  │      WrapperContainerCenter.ts
    │  │      WrapperType.ts
    │  └─objects
    │          Chip.ts
    └─config
            chipType.ts
            imagePath.ts

chipType.ts 為定義我們籌碼的規範,有那些籌碼且代表的值為多少,

chipType.ts

export default {
    '1000': { type: 'chip1000', value: 1000 },
    '10000': { type: 'chip1m', value: 10000 },
    '100000': { type: 'chip10m', value: 100000 },
    '1000000': { type: 'chip100m', value: 1000000 },
    '5000000': { type: 'chip500m', value: 5000000 },
    '10000000': { type: 'chip1000m', value: 10000000 }
}

imagePath.ts 為我們圖的位置,統整圖檔位置,比較好找圖也比較好輸入。

imagePath.ts

export default {
    'chipPath': './assets/chips/spritesheet.json',
}

Chip.ts 為我們籌碼的object,透過extends直接繼承Sprite elements,所以這object可以直接使用super來取得Sprite的constructor。以下為Sprite輸入Sprite圖的方法,透過建構子我們把targetsheet(整個Sprite圖),target(我要的那張圖),透過pixijs loader載進來,不過在使用前期其實還要先把圖片載到快取,不過這在後半段才會提到。

Sprite.ts

...省略
constructor(targetsheet: string, target: string) {
    let id: any = PIXI.loader.resources[targetsheet].textures
...省略

以下為Chip.ts檔案

Chip.ts

import Sprite from '@/components/elements/Sprite'
import chipType from '@/config/chipType'
import imagePath from '@/config/imagePath'

export default class Chip extends Sprite {
    public value: keyof typeof chipType
    constructor (value: keyof typeof chipType){
        super(imagePath.chipPath, `${chipType[value].type}`)
        this.value = value
    }
    public setInteractive(interactive: boolean): void{
        this._container.interactive = interactive
        this._container.buttonMode = interactive
    }
}

這樣還不行顯示,為什麼呢?

載入圖片

其實PIXIJS在使用Texture cache有許多載入方法,總之就是要先載進去快取,這樣我們就可以使用PIXI.loader.resources輕鬆取的我們的圖片囉!以下是目前架構。

└─src
    │  App.ts // 入口處
    │  Game.ts // Game!
    │
    ├─components
    │  ├─elements
    │  │      Animation.ts
    │  │      Sprite.ts
    │  │      Texture.ts
    │  │      Wrapper.ts
    │  │      WrapperContainer.ts
    │  │      WrapperContainerCenter.ts
    │  │      WrapperType.ts
    │  │
    │  └─objects
    │          Chip.ts
    │
    ├─config
    │      chipType.ts
    │      imagePath.ts
    │
    └─loaders
            Loader.ts // loader幫我們load所有圖片

我們新增一個Loader.ts協助我們大量load我們要的圖片。

以下程式碼就是我們load的方法,且可透過on的方法得到我們現在載入進度,所以有些遊戲進入很慢,載入場景就是這個部分囉,可能載入還包含同步資料等等。

Loader.ts

import * as PIXI from "pixi.js"
/**
 * Loader 把設定檔的srcs傳進來自動全部load
 */
class Loader {
  private onChange: Function = () => { }
  public load(srcs: any) {
    let on = (onChange: Function): Promise<any> => {
      this.onChange = onChange
      return then()
    }

    let then = (): Promise<any> => {
      return new Promise(async (resolve, reject) => {
        try {
          let res = []
          let totalResource = Object.keys(srcs).length
          let loadedResource = 0
          for (let i in srcs) {
            res.push(await this.loaderAdd(srcs[i]))
            loadedResource++
            // 這部分就可以把我們的載入進度往外傳遞
            this.onChange(loadedResource / totalResource)
          }
          resolve(res)
        } catch (err) {
          reject(err)
        }
      })
    }
    return {
      then: then,
      on: on
    }
  }

  private loaderAdd(path: string) {
    return new Promise((resolve, reject) => {
    // 這邊是主要PIXI.loader載入圖片進cache的地方
      PIXI.loader
        .add(path)
        .load(resolve)
        .onError.add(reject)
    })
  }
}

export default new Loader()

好我們有這個loader之後,我們新增的Game.ts就是我們開始載入的地方!

Game.ts

import Loader from '@/loaders/Loader'
import imagePath from '@/config/imagePath'
import Wrapper from '@/components/elements/Wrapper'
import WrapperContainer from '@/components/elements/WrapperContainer'
import Chip from '@/components/objects/Chip'

export default class Game {
  private _app: PIXI.Application // 一定需要PIXI.Application
  private _game: Wrapper // game的容器,用來裝我們遊戲內容

  constructor() {
    this._app = new PIXI.Application({ width: 1625, height: 900 }) // 場景寬高
    this._game = new WrapperContainer() // 新增容器
    this._app.stage.addChild(this._game.getContainer()) // 這部分就是要取得容器內的container並丟給app state,這部分是pixijs原本就要這麼做的!所以才得做一個getContainer取得pixijs原生方法。
    document.body.appendChild(this._app.view) // 掛到document body上!

    // 我們要load的是所有圖片路徑
    Loader.load(imagePath)
      .on((e: number) => {
        console.log(e) // 這邊會console.log出我們的進度(百分比)
      })
      .then(() => {
        this.done() // 完成執行done method
      })
      .catch(err => {
        console.warn(err) // 出錯警告
      })
  }

  public done () {
    let chip = new Chip('100000') // 新增籌碼
    this._game.addChild(chip) // 掛到容器上
  }
}

好了!Load圖片可以用了嗎?來確定一下!
https://ithelp.ithome.com.tw/upload/images/20190904/20109783ZrmvxsJnkY.png
沒錯!好了!已經成功顯示出我們要的圖片了!

可以在css針對canvas進行調整把他調成固定寬高比例。直接加在index.html上就好!

<style>
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }

    html,
    body {
      width: 100%;
      height: 100%;
      background: black;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    canvas {
      margin: 0 auto;
      max-width: 100%;
      max-height: 100%;
      widows: auto;
      height: auto;
    }
  </style>

最後加一點有趣的,讓籌碼亂飛吧!這是Game.ts,done的部分,讓他done之後隨機加入籌碼,而且位置不固定。

...以上省略
  public done() {
    let _chipType: Array<keyof typeof chipType> = ['1000', '10000', '100000', '1000000', '10000000', '5000000']

    // 噴射
    setInterval(() => {
      let chip = new Chip(_chipType[Math.ceil(Math.random() * 6)])
  
      chip.setPosition({ animation: true, during: Math.ceil(Math.random() * 6) },
        Math.ceil(Math.random() * 1625),
        Math.ceil(Math.random() * 900))

      this._game.addChild(chip)
    }, 100)
  }
...以下省略

就可以產出一堆籌碼囉!

https://i.imgur.com/ikFypYe.mp4

https://ithelp.ithome.com.tw/upload/images/20190904/20109783V6vvuy65zh.png

結尾

明天補上載入畫面!另外直接去Github看程式碼也是好選擇哦!按個星星吧!

連結

Github repositories


上一篇
DAY02 遊戲專案基礎初始化
下一篇
DAY04 載入畫面、基本場景
系列文
三十天路邊賭場上線了!30

1 則留言

0
阿展展展
iT邦好手 1 級 ‧ 2019-11-15 06:33:23

好可愛的籌碼XDDD

我要留言

立即登入留言