iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 4
1

前言

今天來做載入頁面以及基本場景吧!

載入畫面

我們昨天做了一次載入多張圖,但遊戲在真實環境時,也許網路速度相當的慢,造成LOADING期時會有一段時間,那我們得顯示一些載入動畫舒緩緊張情緒,於是得要有一載入頁面。

結構

以下為目前結構,我新增了groups資料夾以及Loading.ts載入頁面,我定義groups是群組的物件,裡面包含許多objects,而本身groups並不是一個實際的物件,感覺像是組合物的容器。
還有新增config/loadingPath.ts,用意載入畫面先把自己要用到的圖片載完,才開始載遊戲內的其他畫面,不然空空的看著%數挺無趣的!

└──src
    │  App.ts
    │  Game.ts
    │
    ├─components
    │  ├─elements
    │  │      Animation.ts
    │  │      Sprite.ts
    │  │      Texture.ts
    │  │      Wrapper.ts
    │  │      WrapperContainer.ts
    │  │      WrapperContainerCenter.ts
    │  │      WrapperType.ts
    │  │
    │  ├─groups
    │  │      Loading.ts
    │  │
    │  └─objects
    │          Chip.ts
    │
    ├─config
    │      chipType.ts
    │      imagePath.ts
    │      loadingPath.ts
    │
    └─loaders
            Loader.ts

loadingPath

loadingPath.ts內容就是兩個路徑而已

loadingPath.ts

/**
 * 這邊是放loading頁面要的圖 因為要分開載入
 */
export default {
  'logo': './assets/scene/loading-logo.png',
  'bg': './assets/scene/loading-bg.jpg'
}

Loading

以下一長串就是我們的Loading頁面!
大致就是由兩張圖片,一張底圖以及一張LOGO,那載入進度是由數字顯示。

Loading.ts

import loadingPath from '@/config/loadingPath'
import Wrapper from '@/components/elements/Wrapper'
import Texture from '@/components/elements/Texture'
import WrapperContainerCenter from '@/components/elements/WrapperContainerCenter'
import * as PIXI from "pixi.js"

export default class Loading extends WrapperContainerCenter {
  private _loadingText: Wrapper
  private _loadLogo: Wrapper
  private _loadBg: Wrapper
  private _status: boolean = false

  constructor() {
    super()
    let rect = new PIXI.Graphics() // 畫一個區間撐開容器用以方便置中定位
    rect.beginFill(0xff0000)
    rect.drawRect(0, 0, 1650, 900)
    rect.endFill()
    rect.alpha = 0 // 不需要被看見只要能撐開就好
    this._centerContainer.addContainer(rect)

    // 這邊宣告三個容器像是圖層
    this._loadingText = new WrapperContainerCenter() 
    this._loadLogo = new WrapperContainerCenter()
    this._loadBg = new WrapperContainerCenter()

    // 這是背景
    this._centerContainer.addChild(this._loadBg)
    this._loadBg.addChild(new Texture(loadingPath.bg))
    this._loadBg.setSize(false, 1950, 900)
    this._loadBg.setAlpha(false, 0)
    this._loadBg.setAlpha(true, 1)
    this._loadBg.setPosition({ animation: false }, -162.5, 0)

    // 這是LOGO
    this._centerContainer.addChild(this._loadLogo)
    this._loadLogo.addChild(new Texture(loadingPath.logo))
    this._loadLogo.setAlpha(false, 0)
    this._loadLogo.setAlpha(true, 1)
    this._loadLogo.setPosition({ animation: false }, 1650 / 2 - this._loadLogo.width / 2, 900 / 2 - this._loadLogo.width / 2)
    
    // 這是載入進度
    this._centerContainer.addChild(this._loadingText)
    
    // 置中用途
    this._loadingText.setPosition({ animation: false }, 1650 / 2 - this._loadingText.width / 2, 900 - 200)

    // 讓LOGO動起來
    this.animation()
  }

  // 如果載入完了Logo會放大消失
  public done(delay?: number) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        this._status = true
        this._loadLogo.setScale(true, 2, 2)
        this._loadLogo.setAlpha(true, 0)
        setTimeout(() => {
          resolve()
        }, 0)
      }, delay ? delay : 0)
    })
  }

  // update是給外部操控現在%數多少
  public update(process: number) {
    this._loadingText.removeChildren()
    this._loadingText.addContainer(new PIXI.Text(`Loading: ${process} %`, { fontFamily: 'Arial', fontSize: 24, fill: 0xff1010, align: 'center' }))
    this._loadingText.setPosition({ animation: false }, 1650 / 2 - this._loadingText.width / 2, 900 - 200)
  }

  // 持續的動畫
  private animation() {
    let _reverse = false
    let timeout = setInterval(() => {
      _reverse ? this._loadLogo.setPosition({ animation: true, during: 3 }, this._loadLogo.x, this._loadLogo.y - 50) : this._loadLogo.setPosition({ animation: true, during: 3 }, this._loadLogo.x, this._loadLogo.y + 50)
      _reverse = !_reverse
      if (this._status) { clearInterval(timeout) }
    }, 700)
  }
}

Loading結合

現在我們回到我們的Game.ts,裡面有載入的方法,我們新增了甚麼呢?

import Loader from '@/loaders/Loader'
import imagePath from '@/config/imagePath'
import Wrapper from '@/components/elements/Wrapper'
import WrapperContainer from '@/components/elements/WrapperContainer'
// 我們需要loadingPath、Loading畫面,所以引入。
import loadingPath from '@/config/loadingPath'
import Loading from '@/components/groups/Loading'

export default class Game {
  private _app: PIXI.Application
  private _game: Wrapper
  // 增加一個容器裝我們的loading頁面比較方便。
  private _loading: Wrapper

  constructor() {
    this._app = new PIXI.Application({ width: 1625, height: 900 })
    this._game = new WrapperContainer()
    // 新增一個容器給他
    this._loading = new WrapperContainer()
    // 加進去我們的game讓他可以被渲染
    this._game.addChild(this._loading)

    this._app.stage.addChild(this._game.getContainer())
    document.body.appendChild(this._app.view)


    // 調用下方我們的loadimage方法,先載我們loading畫面
    this.loadimage(loadingPath)
      .then(() => {
        // 再load所有圖片
        return this.loadimage(imagePath, 9000)
      })
      .then(() => {
        // load完呼叫stepup
        this.setup()
      })
  }

  private setup(): void {
    console.log('SETUP!')
  }

  // 我們的loadimage方法可以設定load完之後的維持秒數,本地端測試太快速,都看不到。
  private loadimage(imagePath: any, delay?: number): Promise<any> {
    return new Promise((resolve, reject) => {
      try {
        // 新增loading畫面
        let loading = new Loading()
        // 把他加進去我們上面的容器
        this._loading.addChild(loading)
        // 呼叫Loader需要幫我們載入的圖片
        Loader.load(imagePath)
          .on((e: number) => {
          // 載的進度可以透過on,得到我們載入的進度
            loading.update(e * 100)
          })
          .then(() => {
          // 完成之後就是跟loading說好了!不過要delay欣賞一下哦!
            return loading.done(delay)
          })
          // 真的好了之後就把圖層內的Sprite、Texture全部清空
          .then(() => {
            this._loading.removeChildren()
            resolve()
          })
      } catch (err) {
        reject(err)
      }
    })
  }
}

載入完之後就是這畫面,再來就會消失變一片黑。
https://ithelp.ithome.com.tw/upload/images/20190905/20109783xlhGypmTyO.png

場景

緊接著我們來載入我們的場景吧!

區塊切割

我們要來載入我們的賭桌跟賭場被景。但我們先新增大部分的區塊圖層。

└─src
    │  App.ts
    │  Game.ts
    │
    ├─components
    │  ├─elements
    │  │     ...省略
    │  │
    │  ├─groups
    │  │      Body.ts
    │  │      Casino.ts
    │  │      Loading.ts
    │  │      Navbottom.ts
    │  │      Navtop.ts
    │  │      Table.ts
    │  │
    │  └─objects
    │          Chip.ts
    │          Desk.ts
    │          Bg.ts
    ...省略

我在groups內新增了Body、Casino、Navbottom、Navtop、Table,這幾部分,Casino為主要的,他的內部有Body、Navbottom,Navtop,而Table是在body內。還新增了一Desk的物件就是那張桌子的圖片,Bg的物件就是簡單的背景圖片!

Desk.ts很簡單,就是顯示desk圖片而已

Desk.ts

import Texture from '@/components/elements/Texture'
import imagePath from '@/config/imagePath'

export default class Desk extends Texture {
    constructor (){
        super(imagePath.deskPath)
    }
}

當Desk建好了之後,我們再到groups/Table.ts看看吧!

Table.ts

import Wrapper from '@/components/elements/Wrapper'
import WrapperContainerCenter from '@/components/elements/WrapperContainerCenter'
import Desk from '@/components/objects/Desk'

export default class Table extends WrapperContainerCenter {
    private _desk: Wrapper
    constructor() {
        super()
        this._desk = new Desk()
        this.addChild(this._desk)
    }
}

就是很簡單的把desk掛到Table的容器上而已。Table又掛在Body上!

Body.ts

import Wrapper from '@/components/elements/Wrapper'
import WrapperContainerCenter from '@/components/elements/WrapperContainerCenter'
import Table from '@/components/groups/Table'

export default class Body extends WrapperContainerCenter {
  private _table: Wrapper
  constructor() {
    super()
    let rect = new PIXI.Graphics()
    rect.drawRect(0, 0, 1650, 900)
    rect.alpha = 0
    this._centerContainer.addContainer(rect)
    this._table = new Table()
    this._table.setPosition({animation: false}, this.width / 2 - 10, this.height / 2 + 60)
    // 因為桌子圖片大小緣故,透過調整比例撐滿整個寬
    this._table.setScale(false, 1.2, 1.2)

    this._centerContainer.addChild(this._table)
  }
}

Body是掛在groups/Casino上!

Casino.ts
import Wrapper from '@/components/elements/Wrapper'
import WrapperContainerCenter from '@/components/elements/WrapperContainerCenter'
import Bg from '@/components/objects/Bg'
import Body from '@/components/groups/Body'
import Navtop from '@/components/groups/Navtop'
import Navbottom from '@/components/groups/Navbottom'

export default class Casino extends WrapperContainerCenter {
  private _Body: Wrapper
  private _Navtop: Wrapper
  private _Navbottom: Wrapper
  private _bg: Wrapper

  constructor() {
    super()
    this._bg = new WrapperContainerCenter()
    this._Body = new WrapperContainerCenter()
    this._Navtop = new WrapperContainerCenter()
    this._Navbottom = new WrapperContainerCenter()

    this._bg.addChild(new Bg())
    this._Body.addChild(new Body())
    this._Body.addChild(new Navtop())
    this._Body.addChild(new Navbottom())

    this._centerContainer.addChild(this._bg)
    this._centerContainer.addChild(this._Body)
    this._centerContainer.addChild(this._Navtop)
    this._centerContainer.addChild(this._Navbottom)
  }
}

那當我們全部掛好掛滿之後,我們要到我們的Game.ts把我們setup的完成改成導入到Casino裡面!

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 loadingPath from '@/config/loadingPath'
import Casino from '@/components/groups/Casino'
import Loading from '@/components/groups/Loading'

export default class Game {
  private _app: PIXI.Application
  private _game: Wrapper
  private _loading: Wrapper

  constructor() {
    this._app = new PIXI.Application({ width: 1625, height: 900 })
    this._game = new WrapperContainer()

    this._loading = new WrapperContainer()
    this._game.addChild(this._loading)

    this._app.stage.addChild(this._game.getContainer())
    document.body.appendChild(this._app.view)


    // 先load載入頁面
    this.loadimage(loadingPath)
      .then(() => {
        // 再load所有圖片
        return this.loadimage(imagePath, 0)
      })
      .then(() => {
        this.setup()
      })
  }

  private setup(): void {
    // 引入並加進去我們的_game畫面!
    let casino = new Casino()
    this._game.addContainer(casino.getContainer())
  }

  private loadimage(imagePath: any, delay?: number): Promise<any> {
    return new Promise((resolve, reject) => {
      try {
        let loading = new Loading()
        this._loading.addChild(loading)
        Loader.load(imagePath)
          .on((e: number) => {
            loading.update(e * 100)
          })
          .then(() => {
            return loading.done(delay)
          })
          .then(() => {
            this._loading.removeChildren()
            resolve()
          })
      } catch (err) {
        reject(err)
      }
    })
  }
}

完成!
https://ithelp.ithome.com.tw/upload/images/20190905/20109783fuYWBb9V47.png

結尾

其實中間遇到滿多奇怪的地方,因為其實都已經直接參考過往的寫的,結果還是有些狀況是意料之外,看來其中還有不太了解或掌握的pixijs小問題。

連結

Github
Pixijs


上一篇
DAY03 產出Sprite圖、載入圖片
下一篇
DAY05 賭桌感應區域、繪製互動區域
系列文
三十天路邊賭場上線了!30

尚未有邦友留言

立即登入留言