用Phaser來製做一個html5遊戲——flappy bird (二)

上一篇教程中咱們完成了boot、preload、menu這三個state的製做,下面咱們就要進入本遊戲最核心的一個state的製做了。play這個state的代碼比較多,我不會一一進行說明,只會把一些關鍵的東西挑出來講。html

咱們點擊遊戲菜單中的開始按鈕後,首先出現的是這個畫面:html5

1

 

在第一部分的教程中,咱們已經制做了一個遊戲菜單的場景,這個頁面也跟那個差很少,只不過這個頁面去除了遊戲標題和開始按鈕,而多出了Get Ready以及提示點擊屏幕來操做遊戲的兩張圖片。在這個state裏,咱們須要啓用物理引擎,而且要可以響應鼠標點擊事件。ios

game.States.play = function(){
    this.create = function(){
        this.bg = game.add.tileSprite(0,0,game.width,game.height,'background');//背景圖,這裏先不用移動,遊戲開始後再動
        this.pipeGroup = game.add.group();//用於存放管道的組,後面會講到
        this.pipeGroup.enableBody = true;
        this.ground = game.add.tileSprite(0,game.height-112,game.width,112,'ground'); //地板,這裏先不用移動,遊戲開始後再動
        this.bird = game.add.sprite(50,150,'bird'); //
        this.bird.animations.add('fly');//添加動畫
        this.bird.animations.play('fly',12,true);//播放動畫
        this.bird.anchor.setTo(0.5, 0.5); //設置中心點
        game.physics.enable(this.bird,Phaser.Physics.ARCADE); //開啓鳥的物理系統
        this.bird.body.gravity.y = 0; //鳥的重力,未開始遊戲,先讓重力爲0,否則鳥會掉下來
        game.physics.enable(this.ground,Phaser.Physics.ARCADE);//開啓地面的物理系統
        this.ground.body.immovable = true; //讓地面在物理環境中固定不動

        this.readyText = game.add.image(game.width/2, 40, 'ready_text'); //get ready 文字
        this.playTip = game.add.image(game.width/2,300,'play_tip'); //提示點擊屏幕的圖片
        this.readyText.anchor.setTo(0.5, 0);
        this.playTip.anchor.setTo(0.5, 0);

        this.hasStarted = false; //遊戲是否已開始
        game.time.events.loop(900, this.generatePipes, this); //利用時鐘事件來循環產生管道
        game.time.events.stop(false); //先不要啓動時鐘
        game.input.onDown.addOnce(this.statrGame, this); //點擊屏幕後正式開始遊戲
    };
}

 

啓用物理系統git

默認的遊戲中的每一個對象的物理系統是關閉的,要啓用一個對象的物理系統,可使用 game.physics.enable() 方法github

enable(object, system, debug)

 

object : 要開啓物理系統的對象,能夠是單個對象,也能夠是一個包含多個對象的數組web

system : 要啓用的物理系統,默認爲 Phaser.Physics.ARCADE,Phaser目前支持三種物理引擎,分別是Arcade ,P2 以及 Ninja。api

debug : 是否開啓調試數組

只有開啓了對象的物理系統,該對象才具備物理特性,開啓了物理系統後,對象的body屬性指向該對象擁有的物理系統,全部與物理相關的屬性或方法都必須在body上進行操做。瀏覽器

 

鼠標點擊事件app

Phaser中的鼠標、鍵盤、觸摸等交互事件都統一由Input對象來處理。咱們須要鼠標點擊屏幕後進行響應,可使用Input對象的onDown屬性,該屬性指向一個Phaser.Signal對象,咱們能夠在這個對象上綁定事件,每當鼠標按鍵下,就會觸發一個onDown的信號,若是這個onDown信號對象上綁定了事件,那麼這些事件就會執行。例如:

var input = game.input; //當前遊戲的input對象
var signal = input.onDown; //鼠標按下時的 Signal對象
signal.add(function(){}); //給Signal 綁定事件處理函數
signal.add(function(){}); //再綁定一個
signal.addOnce(function(){}); //綁定一個只會執行一次的事件函數

 

時鐘對象

有時咱們須要定時或者每隔一段時間就執行一段代碼,在原生js中咱們能夠經過setTimeout和setInterval來實現。Phaser給咱們提供了功能更強大的Timer對象來實現這些功能。Timer對象主要有如下幾個方法:

loop(delay, callback, callbackContext, arguments); //以指定的時間間隔無限重複執行某一個函數,直到調用了Timer對象的stop()方法才中止

repeat(delay, repeatCount, callback, callbackContext, arguments); //讓某個函數重複執行,能夠指定重複的次數

 

 

當前的Timer對象咱們能夠經過 game.time.events 來獲得,在調用了Timer對象的loop或repeat方法後,還必須調用start方法來啓動。可是我使用的Phaser 2.0.4 版本,好像不調用start方法,loop方法就自動起做用了,不知道這是否是一個bug。如上面代碼中咱們用到的:

game.time.events.loop(900, this.generatePipes, this); //利用時鐘對象來重複產生管道
game.time.events.stop(false); //先讓他中止,由於即便沒調用start方法,它也會自動啓動,這應該是一個bug

 

當點擊屏幕後,就能夠正式開始遊戲了,咱們來看看點擊屏幕事件綁定的 this.startGame 函數作了什麼。

this.statrGame = function(){
    this.gameSpeed = 200; //遊戲速度
    this.gameIsOver = false; //遊戲是否已結束的標誌
    this.hasHitGround = false; //是否已碰撞到地面的標誌
    this.hasStarted = true; //遊戲是否已經開始的標誌
    this.score = 0; //初始得分
    this.bg.autoScroll(-(this.gameSpeed/10),0); //讓背景開始移動
    this.ground.autoScroll(-this.gameSpeed,0); //讓地面開始移動
    this.bird.body.gravity.y = 1150; //給鳥設一個重力
    this.readyText.destroy(); //去除 'get ready' 圖片
    this.playTip.destroy(); //去除 '玩法提示 圖片
    game.input.onDown.add(this.fly, this); //給鼠標按下事件綁定鳥的飛翔動做
    game.time.events.start(); //啓動時鐘事件,開始製造管道
}

咱們再來看下鳥的飛翔動做,它由 this.fly 函數來實現

this.fly = function(){
    this.bird.body.velocity.y = -350; //飛翔,實質上就是給鳥設一個向上的速度
    game.add.tween(this.bird).to({angle:-30}, 100, null, true, 0, 0, false); //上升時頭朝上的動畫
    this.soundFly.play(); //播放飛翔的音效
}

 

重力和速度

Phaser.Physics.Arcade.Body 對象,也就是當你是用arcade物理引擎時 sprite.body 所指向的對象,擁有不少跟物理相關的屬性和方法。其中的 gravity 對象表明重力,它有x和y兩個屬性,分別表明水平方向和垂直方向的重力。咱們可使用它的 setTo(x,y)方法來同事設置兩個方向的重力。設置了重力的物體,它的運動會受到重力的影響,與真實生活中的物理現象是一致的。而後這個body它還有一個 velocity 對象,表示物體的速度,跟重力同樣,都分水平和垂直兩個方向,也能夠用setTo(x,y)方法來設置。一旦給物體設置了合適的速度,它便能動了。

 

管道的生成

下面再來看一看管道生成函數 this.generatePipes

this.generatePipes = function(gap){ //製造一組上下的管道
    gap = gap || 100; //上下管道之間的間隙寬度
    var position = (505 - 320 - gap) + Math.floor((505 - 112 - 30 - gap - 505 + 320 + gap) * Math.random());//計算出一個上下管道之間的間隙的隨機位置
    var topPipeY = position-360; //上方管道的位置
    var bottomPipeY = position+gap; //下方管道的位置

    if(this.resetPipe(topPipeY,bottomPipeY)) return; //若是有出了邊界的管道,則重置他們,再也不製造新的管道了,達到循環利用的目的

    var topPipe = game.add.sprite(game.width, topPipeY, 'pipe', 0, this.pipeGroup); //上方的管道
    var bottomPipe = game.add.sprite(game.width, bottomPipeY, 'pipe', 1, this.pipeGroup); //下方的管道
    this.pipeGroup.setAll('checkWorldBounds',true); //邊界檢測
    this.pipeGroup.setAll('outOfBoundsKill',true); //出邊界後自動kill
    this.pipeGroup.setAll('body.velocity.x', -this.gameSpeed); //設置管道運動的速度
}

 

管道生成的思路:利用隨機數計算出上下管道的位置,而後檢查當前是否有管道已經出了邊界,若是有,則重置出了邊界的那組管道的位置,若是沒有,則生成一組新的管道,這樣就能避免內存浪費了。全部管道咱們都把它放在一個組中,便於集中管理。這裏須要掌握的是sprite對象的reset方法:

reset(x, y, health)

這個方法能重置sprite對象的位置,更重要的是,若是在一個已經被殺死了(kill)的sprite對象上執行該方法,那麼該sprite的 alive, exists, visible and renderable 等屬性都會變回爲true。在須要重複利用已經存在的sprite對象時,常常要使用該方法。看下咱們這個遊戲中是怎麼使用這個方法的:

 

this.resetPipe = function(topPipeY,bottomPipeY){//重置出了邊界的管道,作到回收利用
    var i = 0;
    this.pipeGroup.forEachDead(function(pipe){ //對組調用forEachDead方法來獲取那些已經出了邊界,也就是「死亡」了的對象
        if(pipe.y<=0){ //是上方的管道
            pipe.reset(game.width, topPipeY); //重置到初始位置
            pipe.hasScored = false; //重置爲未得分
        }else{//是下方的管道
            pipe.reset(game.width, bottomPipeY); //重置到初始位置
        }
        pipe.body.velocity.x = -this.gameSpeed; //設置管道速度
        i++;
    }, this);
    return i == 2; //若是 i==2 表明有一組管道已經出了邊界,能夠回收這組管道了
}

 

 

碰撞檢測

好了,管道和鳥都已經有了,並且它們都能動了,接下來就是,實現鳥撞到管道或地面後遊戲結束的功能了。

在Arcade物理引擎中,碰撞檢測主要用到兩個函數,一個是collide,還有一個是overlap

collide方法與overlap的區別在於collide會影響兩個要檢測的對象之間的物理狀態,好比使用collide函數去檢測兩個物體,若是物體碰撞了,那麼這兩個物體之間就會有力的相互做用,可能其中一個會被另外一個彈開,或者兩個之間相互彈開。但若是使用overlap方法的話,則只會檢測兩個物體是否已經碰撞了,或者說已經重疊了,並不會產生物理做用,顯然,若是隻須要知道兩個物體是否已經重疊了的話,overlap性能會更好。

碰撞檢測能夠單個對象與單個對象進行檢測、單個對象與組進行檢測、組與組進行檢測。collide方法必須在每一幀中都進行調用,才能產生碰撞後的物理做用。

this.update = function(){ //每一幀中都要執行的代碼能夠寫在update方法中
    if(!this.hasStarted) return; //遊戲未開始,先不執行任何東西
    game.physics.arcade.collide(this.bird,this.ground, this.hitGround, null, this); //檢測與地面的碰撞
    game.physics.arcade.overlap(this.bird, this.pipeGroup, this.hitPipe, null, this); //檢測與管道的碰撞
    if(this.bird.angle < 90) this.bird.angle += 2.5; //降低時鳥的頭朝下的動畫
    this.pipeGroup.forEachExists(this.checkScore,this); //分數檢測和更新
}

 

 

分數管理

當鳥飛過一組管道後,就得1分。飛過一組管道,是指這組管道已經在鳥的左邊的,因此能夠經過管道的x座標來判斷是否已經得分。

this.checkScore = function(pipe){//負責分數的檢測和更新,pipe表示待檢測的管道
    //pipe.hasScored 屬性用來標識該管道是否已經得過度
    //pipe.y<0是指一組管道中的上面那個管道,一組管道中咱們只須要檢測一個就好了
    //當管道的x座標 加上管道的寬度小於鳥的x座標的時候,就表示已經飛過了管道,能夠得分了
    if(!pipe.hasScored && pipe.y<=0 && pipe.x<=this.bird.x-17-54){
        pipe.hasScored = true; //標識爲已經得過度
        this.scoreText.text = ++this.score; //更新分數的顯示
        this.soundScore.play(); //得分的音效
        return true; 
    }
    return false;
}

 

只須要在每一幀中對每個管道都調用一次該函數,就能夠了。

 

聲音的播放

在Phaser中播放一段聲音很簡單,只須要事先加載好聲音資源。而後調用play方法播放就好了。

首先使用 game.load.audio() 來加載聲音資源。咱們以本遊戲中得分時播放的聲音爲例,在state的preload方法中預先加載聲音資源

game.load.audio('score_sound', 'assets/score.wav');//得分的音效

 

而後經過 game.add.sound() 來獲得一個sound對象

this.soundScore = game.add.sound('score_sound');

 

sound對象有許多方法用來控制聲音的播放暫停等,要播放聲音,只須要調用它的play方法便可。

this.soundScore.play(); //播放聲音

 

好了,把這些組合起來,就能作出咱們的flappy bird遊戲了。我所說的這些都只是些皮毛,是想讓你們對用Pharse來作遊戲有個最初步的印象,也許你還有許多不明白的地方,pharse是個功能很強大的html5遊戲框架,想要掌握它,仍是必須多看文檔,多看官方給出的例子,而後本身動手去實踐,一步一步一點一滴的去學習,去積累。蘋果新發布的ios8中對webgl的支持已經大大增強了,不管是在safari瀏覽器仍是webView運行html5遊戲,性能都至關好,這也是移動設備發展的一個趨勢,因此掌握一個html5遊戲框架,不管是自娛自樂,或是對本身能力的提高,甚至是找工做,都是有必定的益處的。

 

第一部分教程:

用Phaser來製做一個html5遊戲——flappy bird (一)

相關文章
相關標籤/搜索