H5遊戲開發的一乘輕騎---Phaser

快速入門(普通版)

Phaser 是一款很是優秀的 HTML5 遊戲框架,致力於發展 PC 端和移動端的 HTML5 遊戲,是一款不可多得的神器。html

本篇文章將帶你快速入門遊戲的開發(移動端),適合想要了解遊戲開發或者想投入遊戲開發的人羣,毫無疑問,Phaser 是一個值得你學習的遊戲框架。jquery

特別注意

Phaser 雖然是一款很是優秀的HTML5遊戲框架,可是也有它軟肋的地方,如今請注意下面兩個 Phaser 處理不了或者處理起來很棘手的地方:git

1. 3D。若是你想學作一個很酷的3D動畫,能夠學習 Three.js github

2. 視頻(移動端)。若是你在移動端頁面上須要嵌入視頻,請使用原生HTML5標籤 video,若是須要對 video 有更好的擴展,能夠參考 Video.js web

接下來你能夠先打開咱們這篇文章所講的 案例 (服務器渣渣,可能要等很久很久哈哈哈),看完這個案例,你可能會說,這有什麼的,我用Jquery的動畫都作得出來,哈哈,之前我也對一個大神說過相似的話,而後人家很清楚地跟我說拿 Phaser 的動畫跟 Jquery 的動畫比簡直是在侮辱 Phaser。npm

好了,話就說到這裏,我我的以爲太複雜的案例反而會讓人理不清思路,便有違咱們的快速入門的宗旨,相信看完這篇文章以後,你能夠打開遊戲開發的篇章,自由的描繪喜歡的世界。canvas

代碼地址跨域

說在前面

首先,爲了能正常運行咱們的案例,你須要一個本地服務器。你可能會有疑問,爲何咱們不能直接把 index.html  拖進瀏覽器中並運行呢?瀏覽器

由於這與用於訪問文件的協議有關。當你在網絡上請求任何東西時,你須要使用HTTP,而服務器能夠保證你只能訪問你想要的文件。可是當你直接把 html 文件拖到瀏覽器上直接運行時,它是經過本地文件系統(file://)加載的,因爲本地文件系統沒有域的概念,沒有服務器級別的安全,只是一個原始文件系統。
安全

您真的但願JavaScript可以從文件系統中的任何地方加載文件嗎?答案固然是不行。你的計算機固然也不會贊成。

而 Phaser 須要加載資源:圖像,音頻文件,JSON數據,或者其餘Javascript文件。爲了作到這些,它就須要運行在服務器下。

若是你對如何配置一個本地服務器來運行 html 文件感到毫無頭緒,而且這也不是咱們這篇文章的重點,那我能夠介紹一個很簡單的方式,你能夠嘗試進行下列操做:

1. 打開命令行,輸入如下命令回車: npm install puer -g (若是沒有 npm 請自行百度安裝)2. puer 安裝完成後,在須要搭載在本地服務器的案例根文件夾下調用命令行,輸入如下命令回車:puer -p 9999 以下:


3.若是puer運行成功則以下並自動打開瀏覽器:


以下案例已經搭載在地址爲 http://localhost:9999/ 上了,點擊 index.html 就能夠把 Phaser 運行起來啦


開始第一步

首先建立咱們的目錄結構以下:


img文件夾下的圖片能夠自行從 案例代碼地址 上拷貝,而後你會發現,咦,GitHub上怎麼多了一張 sprites.png 圖片和一個 sprites.xml 文件,這個後面將會講到,如今咱們只須要準備現階段須要的,js/libs文件下的 jquery.min.jsphaser.min.js 請自行準備或從 案例代碼地址 上拷貝,而後其它文件咱們須要保證空白以便咱們後面迅速填充內容。

第二步

首先固然是向咱們的 index.html 快速填充內容啦,內容以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Phaser快速入門(普通版)</title>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
</head>
<style>
    html,
    body {
        width: 100%;
        height: 100%;
        font-family: "Helvetica Neue", Helvetica, STHeiTi, sans-serif;
        overflow: hidden;
        background: #fff;
        margin: 0;
        padding: 0;
    }
    .main {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
    }
    .main img {
        width: 100%;
        height: 100%;
    }
</style>
<body>
    <!--H5頁面全部動畫的canvas載體-->
    <div id="game-container"></div>
    <!--預加載背景,防止預加載的資源加載過久以至屏幕長時間空白-->
    <div class="main">
        <img src="img/bg.png" alt="">
    </div>
</body>
<script src="js/libs/jquery.min.js"></script>
<script src="js/states/utils.js"></script>
<script src="js/libs/phaser.min.js"></script>
<script src="js/states/boot.js"></script>
<script src="js/states/preload.js"></script>
<script src="js/states/state1.js"></script>
<script src="js/states/state2.js"></script>
<script src="js/app.js"></script>
</html>複製代碼

好了,內容填充完畢,在這個文件裏面首先咱們案例全部動畫都是搭載在id爲 game-container 的元素上,而後class爲 main 的元素是預加載背景,看了註釋對它的用意還不是很清楚的話能夠不用深究,後面天然就清楚了。

而後你還會發現咱們導入了一個 jquery.min.js ,你可能會有這樣的疑問,這是要幹嗎?難道在使用 Phaser 的時候還須要先導入 Jquery 嗎?答案確定不是,導入 Jquery 只是爲了在後面證實一些東西。utils.js 是封裝了一些咱們在後面常常用到的操做(滿屏效果,縮放圖片)

接下來咱們把重點放在咱們這個案例運行起來的機制,先來看看咱們的5個核心文件:

<script src="js/states/boot.js"></script>
<script src="js/states/preload.js"></script>
<script src="js/states/state1.js"></script>
<script src="js/states/state2.js"></script>
<script src="js/app.js"></script>複製代碼

這5個文件的執行順序依次是:

app.js -> boot.js -> preload.js -> state1.js -> state2.js

app.js 裏面會執行 boot.jspreload.jsstate1.jsstate2.js 這4個場景,所以 app.js 須要在導入 boot.jspreload.jsstate1.jsstate2.js 這4個文件以後導入。接下來逐一解析這5個文件。

app.js

這個文件是咱們 Phaser 執行的第一個文件,主要進行一些遊戲開發方面的配置,包括設置咱們資源的路徑,掛載元素,添加場景,啓動場景等。

具體代碼以下:

(function() {
    'use strict'

    // 設置資源目錄(項目根目錄)
    var baseURI ='../..'

    //將圖片目錄放在內存中,方便全局調用
    localStorage.baseURI = baseURI

    //設置$('#game-container')的高度等於屏幕的高度(這裏用原生js代碼書寫)
    document.getElementById('game-container').style.height = document.body.clientHeight + 'px'

    //獲取屏幕的縮放比
    var Ratio = window.devicePixelRatio

    //獲取屏幕的寬和高
    var w = document.documentElement.clientWidth || document.body.clientWidth
    var h = document.documentElement.clientHeight || document.body.clientHeight

    //由於咱們在index.html設置了禁止縮放的meta頭
    //<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    //因此當咱們將屏幕的寬和高直接傳入Phaser.Game對象時,瀏覽器會自動將canvas按屏幕的縮放比縮放,也就是當你的屏幕縮放比是2時,canvas寬高你設置成屏幕的寬高時實際看到的卻只有一半,所以相應咱們須要將畫布放大Ratio倍
    var ww = Ratio * w
    var hh = Ratio * h

    //前兩個參數是Phaser要建立的canvas元素的寬高,第三個參數是遊戲渲染的引擎,這裏讓Phaser本身識別設置便可,第四個參數是遊戲掛載在哪一個元素上
    var game = new Phaser.Game(ww, hh, Phaser.AUTO, 'game-container')

    //添加場景
    game.state.add('Boot', Boot)
    game.state.add('Preload', Preload)
    game.state.add('State1', State1)
    game.state.add('State2', State2)

    //啓動場景
    game.state.start('Boot')

})(window)複製代碼

上面的每一行代碼都已經加上相應的註釋,若是有空的話能夠看一下並理解清楚,如今咱們只需知道 app.js 只是用來配置而且啓動咱們的第一個場景(boot.js)就行。

在說咱們的場景以前,咱們先來了解什麼是場景,場景是咱們 Phaser 遊戲開發的主體,咱們全部看到的遊戲動畫都是在場景裏實現,場景能夠是一個js自定義對象,也能夠是一個函數,只要存在preload、create、update這三個方法中的任意一個,就是一個合法的場景(總共5個方法,另外兩個是init和render方法)。

init方法:一些場景的初始化代碼能夠寫在這個方法裏,最早執行。

preload方法:用來加載資源的,若是沒有init方法則它會最早執行。

create方法:初始化以及構建場景,會等到preload方法里加載的資源所有加載完成後執行。

update方法:更新函數,它會在遊戲的每一幀都執行,通常是1/60秒執行一次。

render方法:在遊戲的每一渲染週期都會調用,用來作一些自定義的渲染工做。

boot.js

這個場景不會向用戶展現,只是爲了加載下個場景(即預加載場景preload.js)所需的資源,所以資源不能加載太多,太多的話則屏幕會長時間黑屏(黑屏其實就是這個場景加載資源的過程,由於這是第一個場景,沒有上個場景幫你加載好的資源用來構建界面,因此也就沒有能夠友好顯示給用戶的界面),而這個問題咱們也在index.html 用 $('.main') 解決,即先展現 $('.main') 這個元素展示出來的界面(界面背景須要跟預加載場景背景同樣),以便這個場景加載過久有個良好的用戶體驗,而當這個場景加載完後 $('.main') 也能夠實現跟預加載場景的良好銜接。

具體代碼以下:

var Boot = function(game) {
    var baseURI = localStorage.baseURI
    this.init = function() {
        //game.device.desktop判斷是WAP端仍是PC端,true爲PC端,false爲WAP端
        if (!game.device.desktop) {
            //設置遊戲背景色
            game.stage.backgroundColor = '#282C34';
            //鼠標指針對象,因爲WAP端沒有鼠標,所以設置爲1(即爲null)
            game.input.maxPointers = 1;
            //縮放控制,這裏將畫布(canvas)拉伸至填滿父容器(即#game-container),不保持比例
            game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
            //啓用時,顯示畫布(canvas)將在父容器中水平對齊
            game.scale.pageAlignHorizontally = true;
            //啓用時,顯示畫布(canvas)將在父容器中垂直對齊
            game.scale.pageAlignVertically = true;
            //強制遊戲只能以一個方向運行,這裏設置遊戲僅能在縱向模式運行(true),沒法再橫向模式運行(false)
            game.scale.forceOrientation(false, true);
            //當forceOrientation設置只能縱向模式運行,把手機橫向擺放就會調用enterIncorrectOrientation這個方法
            game.scale.enterIncorrectOrientation.add(enterIncorrectOrientation, this);
            //當forceOrientation設置只能縱向模式運行,把手機橫向擺放就會調用enterIncorrectOrientation這個方法,從新將手機縱向擺放就會調用leaveIncorrectOrientation這個方法
            game.scale.leaveIncorrectOrientation.add(leaveIncorrectOrientation, this);
        }else{
            game.scale.pageAlignHorizontally = true
            game.scale.pageAlignVertically = true
            //防止瀏覽器失去焦點後動畫暫停,若是考慮到計算機性能能夠設置爲false
            game.stage.disableVisibilityChange = true;
        }

    }
    this.preload = function() {
        //設置圖片支持跨域請求
        game.load.crossOrigin = true
        //加載預加載界面所需的資源,能夠看到圖片跟$('.main')界面一致,第一個參數是建立sprite對象時所須要的資源標識,第二個參數是資源所在路徑
        game.load.image('start_bg', baseURI + '/img/bg.png')

    }
    this.create = function() {
        //跳轉到下一個場景
        game.state.start('Preload');
    }
    function enterIncorrectOrientation() {
        alert('請將手機縱向擺放');
    }
    function leaveIncorrectOrientation() {
        alert('已經將手機縱向擺放');
    }
}
複製代碼

上面的每一行代碼已經都加上相應的註釋,若是有空的話也能夠看一下並理解清楚,如今咱們只需知道 boot.js 只是用來設置咱們全部場景而且加載咱們的第二個場景(預加載場景preload.js)所需的資源就行。

preload.js

這個場景是預加載場景,會加載後面全部場景的資源,因爲加載的資源比較多,因此在這個場景咱們須要以一種友好的方式展示咱們資源加載的過程,用到的是上個場景已經幫咱們加載完成的 start_bg 資源,和 Phaser 自身的文本對象(顯示加載進度)。

preload.js 特別注意咱們須要加載一個幀圖片(實現動圖效果)

//加載幀圖片,第一個參數是建立sprite對象時所須要的資源標識,第二個參數是圖片所在路徑,第三個參數是標識圖片xml文件
game.load.atlasXML('sprites', baseURI + '/img/sprites.png',baseURI + '/img/sprites.xml')複製代碼

加載幀圖片咱們須要一張包含全部動做的總體圖片,還須要一個配置xml文件。那麼問題來了,咱們爲何須要兩個文件呢,爲何不直接把幀圖片作成動圖而後展現,這個問題其實很簡單,首先直接一張動圖遠遠比一張靜態圖片大的多(xml文件過小忽略不計),並且咱們也沒法在代碼中控制動圖的切換速度。

好吧,如今按咱們的想法來,若是咱們有兩張須要在 Phaser 作成動圖的圖片,好比下面:


而後咱們要怎麼把它們合成咱們想要的一張總體圖和一個配置xml文件呢?這個時候咱們就須要一個軟件來幫咱們處理了,這裏我來介紹 Shoebox :


官網地址

下載安裝完成後,咱們打開 Shoebox ,將須要合成動圖的各幀圖片一塊兒移到 Sprite Sheet ,如圖鬆手放開:


點擊 Save ,就已經生成了一張幀圖片和一個xml文件:


xml文件主要記載咱們幀圖片包含的每張動做圖片的位置和名稱信息,固然,這不是咱們關注的重點。

具體代碼以下:

var Preload = function(game) {
    var baseURI = localStorage.baseURI
    var tool = new utils()
    var w = null
    var h = null
    var start_bg = null
    var loading = null
    this.init = function() {
        //獲取畫布的寬高,即Ratio倍屏幕寬高
        w = game.width
        h = game.height

        //動畫組,方便統一處理多個對象的動畫
        group=game.add.group()

        //將$('.main')元素隱藏
        $('.main').hide()

        //因爲start_bg跟$('.mian')界面一致,用戶是看不出$('.mian')隱藏start_bg顯示這個過程的
        //建立sprite對象,第一個參數是畫布x座標(距離畫布左邊緣多遠),第二個參數是畫布y座標(距離畫布上邊緣多遠),第三個參數是構建對象的資源
        start_bg = game.add.sprite(0, 0, 'start_bg')
        //將start_bg寬高設置全屏
        tool.setFull(start_bg)

        //建立文本對象,前面兩個參數跟sprite等同,第三個參數是文本內容,第四個參數是文本樣式
        loading=game.add.text(w*.5, h*.5, '0%',
            {
                fontSize:60,
                fill:'#ffffff'
            })
        //錨點位置(相對自身),第一個參數是相對自身左移多少(.5是左移自身寬度的50%),第二個參數是相對自身上移多少(.5是上移自身高度的50%)
        loading.anchor.set(.5, .5)
        //loading最後的位置是相對畫布居中

        //loading的補間動畫,from(從怎樣的狀態轉變到默認狀態),to(從默認狀態轉變到怎樣的狀態),這裏用的是from
        //第一個參數:一個js對象,包含着須要進行動畫的屬性,{ alpha: 0 }表示透明度爲0
        //第二個參數:動畫的持續時間
        //第三個參數:動畫過程函數,默認爲勻速動畫Phaser.Easing.Linear.None
        //第四個參數:是否自動開始
        //第五個參數:動畫開始前的延遲時間,單位是毫秒
        //第六個參數:動畫重複的次數,若是須要動畫永遠循環,則把該值設爲 Number.MAX_VALUE
        //第七個參數:是否自動反轉
        game.add.tween(loading).from({ alpha: 0 }, 500, null, true, 0, 0, false)

    }
    this.preload = function() {
        game.load.crossOrigin = true

        //加載幀圖片,第一個參數是建立sprite對象時所須要的資源標識,第二個參數是圖片所在路徑,第三個參數是標識圖片xml文件
        game.load.atlasXML('sprites', baseURI + '/img/sprites.png',baseURI + '/img/sprites.xml')
        
        game.load.image('next', baseURI + '/img/next.png')
        game.load.image('img2_1', baseURI + '/img/2_1.jpg')
        game.load.image('img2_2', baseURI + '/img/2_2.jpg')

        //這個方法是文件加載過程,返回的progeress是完成的進度,0~100
        game.load.onFileComplete.add(function(progeress) {
            loading.setText(progeress + '%')
        })

        //全部文件都完成加載時會調用這個方法,咱們能夠在調用這個方法的時候跳轉到下一個場景,效果等同於在create方法執行game.state.start('State1')
        game.load.onLoadComplete.add(function() {
            game.state.start('State1')
        })

    }
    this.create = function() {
       //game.state.start('State1')    }
    this.update = function() {}
}複製代碼

上面的代碼咱們要特別注意 $('.main').hide() ,這也是咱們在前面導入 jquery.min.js 的要證實的東西,就是你能夠在 Phaser 裏面寫 Jquery 的任何代碼。

若是上面兩個場景(boot.jspreload.js)你已經徹底理解和摸透了話,那下面這兩個場景(state1.jsstate2.js)估計你可以很快速就看完。

state1.js

這個場景主要應用到 Phaser 的動圖,事件還有動畫組的運用。在這裏細心的小夥伴能夠看到咱們把原本寫在create方法的初始化以及構建場景的代碼寫在了preload方法裏,就個人經驗,只要一個場景不須要預加載資源,preload方法和create方法的效用並無差異。

具體代碼以下:

var State1 = function(game) {

    var baseURI = localStorage.baseURI

    var tool = new utils()
    var w = null
    var h = null
    
    var next=null
    var sprites=null
    var group=null

    this.preload = function() {

      w = game.width
      h = game.height

      //動畫組,方便統一處理多個對象的動畫
      group=game.add.group()

      //第四個參數是沒有加入動畫時靜態展現第幾幀圖片
      sprites=game.add.sprite(w*.5,h*.6,'sprites',0)
      tool.setSize(sprites,'width',w*.5)
      sprites.anchor.set(.5,1)
      //給sprite對象添加一個新動畫,第一個參數是動畫名稱
      sprites.animations.add('run')
      //播放動畫,第一個參數是動畫名稱,第二個參數是播放的速率,第三個參數是是否循環
      sprites.animations.play('run',4,true)
      
      
      //將start_bg加入動畫組
      group.add(sprites)

      next=game.add.sprite(w*.5,h*.8,'next')
      tool.setSize(next,'width',w*.3)
      next.anchor.set(.5,.5)
      game.add.tween(next).to({ width:next.width+20,height:next.height+20 }, 500, Phaser.Easing.Linear.In, true, 0, -1, true)

      group.add(next)

      //默認狀況下,遊戲對象不會處理任何事件,因此咱們須要讓它能夠處理事件
      next.inputEnabled = true

      //當對next對象點擊而後手指放開的時候觸發
      next.events.onInputUp.add(function() {

        //onComplete方法是補間動畫完成後的回調,咱們能夠在跳轉到下一個場景的時候作一些用戶體驗比較良好的當前場景的退場動畫(這裏的退場動畫相似淡出效果)
        game.add.tween(group).to({ alpha: 0 }, 500, Phaser.Easing.Linear.In, true, 0, 0, false).onComplete.add(function() {
            game.state.start('State2')
        })
        
      })

    }

}複製代碼

state2.js

這個場景只是爲了承接上個場景 next 按鈕的點擊跳轉。

var State2 = function(game) {
  
    var w = null
    var h = null

    var img2_1=null
    var img2_2=null

    this.preload = function() {
      w = game.width
      h = game.height

      img2_1=game.add.sprite(0,h*.5,'img2_1')
      img2_1.width=w
      img2_1.height=h*.5
      img2_1.anchor.set(0,1)
      game.add.tween(img2_1).to({ y:h }, 2000, Phaser.Easing.Linear.In, true, 500, -1, true)

      img2_2=game.add.sprite(0,h*.5,'img2_2')
      img2_2.width=w
      img2_2.height=h*.5
      game.add.tween(img2_2).to({ y:0 }, 2000, Phaser.Easing.Linear.In, true, 500, -1, true)
    }
    this.create = function() {}
}複製代碼

好了,state2.js 就不作過多的贅言了,看到了這裏相信你早已知道這個場景是怎樣的一種動畫效果了。

煮了一天的雞湯

好了,到這裏咱們就好好結束吧,Phaser 還有太多有趣的地方等着你去發掘,若是想要了解更多能夠直接去 官網 ,裏面有不少有趣的小例子。

在這裏特別鳴謝眯眼貓的黎世燦大神帶我瘋狂入坑,而且對這篇文章的技術支持。

在這裏祝賀你們雙12快樂,事業進步!

相關文章
相關標籤/搜索