基於nodejs環境用Phaser來製做一個html5遊戲

最近對html5小遊戲有點感興趣,就在網上搜羅了一些教程,本身學着實現了簡單的flappybird。效果以下圖:
圖片描述圖片描述圖片描述javascript

今天就跟你們簡單介紹下個人實現,相應代碼已放置本人github,下面步驟所涉及到的資源素材皆在代碼裏面。html

一、建立node項目
遊戲主要是有js來控制實現的,是要靠服務器運行的,這邊我就選擇了node環境,因此最開始須要先建立node項目,這裏就不詳細介紹,如果不會的能夠參照如何建立nodejs項目?html5

二、添加遊戲相關空架子
建立完node項目以後,就須要咱們在這個框架結構編寫遊戲了,首先咱們須要建與遊戲編碼相關的一個空架子,目錄結構以下:java

clipboard.png

咱們須要編輯的就是views/index.ejs(至關於index.html,主頁入口),public/images(遊戲圖片),public/Javascripts(遊戲相關js,main.js就是主要編輯的js,phaser.min.js就是遊戲基於的技術實現)node

三、初始化
修改node項目主文件index.ejs,引入phaser和main.jsgit

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title> Flappy Bird Clone </title>
  <script type="text/javascript" src="/javascripts/phaser.min.js"></script>
  <script type="text/javascript" src="/javascripts/main.js"></script>
</head>

<body>
</body>
</html>

四、遊戲場景實例化
接下來就須要咱們編輯各類遊戲場景了,控制整個遊戲的流程個運行,這裏就須要在mainjs裏面實現了,依賴於phaser的技術,這裏就不仔細介紹phaser,如有不懂可查看官方文檔,或者看這裏
首先這邊設定的是四種遊戲場景:加載動畫(bootState)、預加載(preloadState)、菜單(menuState)、遊戲(mainState)github

這裏用到的就是phaser的stateweb

首先根據設計的四種場景,搭建main.js的空架子,實例化一個game,以下:服務器

//加載動畫
var bootState = {};
//預加載
var preloadState = {};
//菜單
var menuState = {};
//遊戲
var mainState = {};

//實例化遊戲
var game = new Phaser.Game(285, 490);


//把定義好的場景添加到遊戲中
game.state.add('boot', bootState);
game.state.add('preload', preloadState);
game.state.add('menu', menuState);
game.state.add('main', mainState);

//調用boot場景來啓動遊戲
game.state.start('boot');

五、加載動畫
咱們設定遊戲最開始就是加載動畫,這邊有個小問題,就是這裏的gif動畫運行不起來,這塊暫時沒有去優化,因此你們也能夠看着辦,這塊也是無關緊要,可根據本身喜愛添加,若不想要這一塊,將初始運行改成預加載的模塊便可,代碼以下:app

var bootState = {
    preload:function () {
        game.load.image('loading','/images/flappybird/loading0.gif'); //加載進度條圖片資源
    },
    create: function () {
        game.state.start('preload'); //加載完成後,調用preload場景
    }
};

六、調用preload場景
遊戲所須要用到的全部資源全都在這裏進行加載,代碼以下:

var preloadState = {
    preload : function(){
        var preloadSprite = game.add.sprite(0,0,'loading'); //建立顯示loading進度的sprite
        game.load.setPreloadSprite(preloadSprite);  //用setPreloadSprite方法來實現動態進度條的效果

        //如下爲要加載的資源
        game.load.image('background','/images/flappybird/bg_day.png'); //遊戲背景圖
        game.load.image('ground','/images/flappybird/land.png'); //地面
        game.load.image('title','/images/flappybird/title.png'); //遊戲標題
        game.load.image('bird', '/images/Flappy-bird.png');//小鳥
        game.load.image('btn','/images/flappybird/button_play.png');  //按鈕
        game.load.image('pipe', '/images/pipe.png');//管道
       
        game.load.audio('jump', '/images/jump.wav');

        game.load.image('ready_text','/images/flappybird/text_ready.png'); //get ready圖片
        game.load.image('play_tip','/images/flappybird/tutorial.png'); //玩法提示圖片
        game.load.image('game_over','/images/flappybird/text_game_over.png'); //gameover圖片
        game.load.image('score_board','/images/flappybird/score_panel.png'); //得分板
    },
    create : function(){
        game.state.start('menu'); //當以上全部資源都加載完成後就能夠進入menu遊戲菜單場景了
    }
};

七、進入菜單場景
加載完資源就開始進入遊戲的菜單頁面了,從這裏開始就是整個遊戲的展現主體了。
菜單有四個部分:背景地板標題組(包含標題和小鳥)和按鈕(遊戲開始入口)
實現效果如圖:
圖片描述

實現原理:
(1)背景和地面:咱們看到這兩個東西是會動的,地面移動動的速度快一些,背景圖慢一些,在Phaser中有專門的東西來處理這種效果,叫作TileSprite,什麼是TileSprite呢?TileSprite本質上仍是一個sprite對象,不過這個sprite的貼圖是能夠移動的,而且會自動平鋪來彌補移動後的空缺,因此咱們的素材圖片要是平鋪後看不出有縫隙,就能夠拿來當作TileSprite的移動貼圖了。TileSprite的貼圖既能夠水平移動也能夠垂直移動,或者二者同時移動,咱們只須要調用TileSprite對象的autoScroll(x,y)方法就可使它的貼圖動起來了,其中x是水平方向的速度,y是垂直方向的速度。

var menuState = {
    create:function () {
        game.add.tileSprite(0,0,game.width,game.height,'background').autoScroll(-10,0); //背景圖
        game.add.tileSprite(0,game.height-80,game.width,112,'ground').autoScroll(-100,0); //地板
    }
};

(2)標題組:咱們能夠看到,標題組有標題文字和小鳥,這二者仍是動態的,一塊兒上下移動,這裏用到的就是Phaser.Group,也就是組。組至關於一個父容器,咱們能夠把許多對象放進一個組裏,而後就可使用組提供的方法對這些對象進行一個批量或是總體的操做。咱們把標題文字和小鳥裝進這個組裏面,對這個組進行一個總體的操做。

var menuState = {
    create:function () {
        ...
        
        var titleGroup = game.add.group(); //建立存放標題的組
        titleGroup.create(0,0,'title'); //標題
        var bird = titleGroup.create(190, 10, 'bird'); //添加bird到組裏
        bird.animations.add('fly'); //添加動畫
        bird.animations.play('fly',12,true); //播放動畫
        titleGroup.x = 35;
        titleGroup.y = 100;
        game.add.tween(titleGroup).to({ y:120 },1000,null,true,0,Number.MAX_VALUE,true); //標題的補間動畫,對這個組添加一個tween動畫,讓它不停的上下移動
    }
};

(3)按鈕:按鈕就是給一張圖片,而後添加點擊事件,點擊就開始遊戲的場景

var menuState = {
    create:function () {
        ...
        
        ...
        
        var btn = game.add.button(game.width/2,game.height/2,'btn',function(){//按鈕
            //點擊開始main遊戲場景
            game.state.start('main');
        });
        btn.anchor.setTo(0.5,0.5);
    }
};

八、遊戲場景
這裏就是整個遊戲的重頭戲了,從這裏開始就是操做玩遊戲的整個過程,都在mainState裏面完成,至於每一個遊戲的小部分,依靠的是裏面各個函數體來控制完成

首先,遊戲準備部分,遊戲還沒開始前給出個簡易遊戲教程指示,這裏總體都是靜態的:
(1)初始化背景
(2)建立用於存放管道的組
(3)開啓地面的物理系統,可是先保持地面不移動
(4)開啓小鳥的物理系統,可是先保持小鳥不移動
(5)標題頭
(6)教程示意圖,添加點擊事件,點擊開始遊戲

var mainState = {
    create: function() {
        this.background = 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-80,game.width,112,'ground'); //地板
        game.physics.enable(this.ground,Phaser.Physics.ARCADE);//開啓地面的物理系統
        this.ground.body.immovable = true; //讓地面在物理環境中固定不動

        this.bird = game.add.sprite(50,150,'bird'); //鳥
        game.physics.enable(this.bird,Phaser.Physics.ARCADE); //開啓鳥的物理系統
        this.bird.body.gravity.y = 0; //鳥的重力,未開始遊戲,先讓重力爲0,否則鳥會掉下來

        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);
        //game.time.events.stop(false); //先不要啓動時鐘
        game.input.onDown.addOnce(this.startGame, this); //點擊屏幕後正式開始遊戲
    },
}

而後,開始玩遊戲
(1)從startGame函數開始,玩遊戲的主題函數。讓背景和地面開始移動,開啓小鳥的重力系統,經過點擊屏幕讓小鳥起飛,去除準備階段的標題頭和玩法提示圖片,管道開始移動,而後顯示左上角的得分狀況。經過update更新遊戲狀況

var mainState = {
    create: function() {
        ...
    },

    startGame: function () {
        this.gameSpeed = 200; //遊戲速度
        this.background.autoScroll(-(this.gameSpeed/10),0); //讓背景開始移動
        this.ground.autoScroll(-this.gameSpeed,0); //讓地面開始移動
        // 小鳥
        game.physics.arcade.enable(this.bird);
        this.bird.body.gravity.y = 1000;
        this.readyText.destroy(); //去除 'get ready' 圖片
        this.playTip.destroy(); //去除 '玩法提示 圖片
        var spaceKey = game.input.keyboard.addKey(
            Phaser.Keyboard.SPACEBAR);
        spaceKey.onDown.add(this.jump, this);

        //管道
        this.pipes = game.add.group();
        //定時器,每1.5秒調用addRowOfPipes
        this.timer = game.time.events.loop(1500, this.addRowOfPipes, this);

        //得分:顯示左上角的分數
        this.score = 0;
        this.labelScore = game.add.text(20, 20, "0",
            { font: "30px Arial", fill: "#ffffff" });

        // 改變小鳥旋轉的中心點
        this.bird.anchor.setTo(-0.2, 0.5);

        //聲音
        this.jumpSound = game.add.audio('jump');
    },
    /**
     * 最後update方法是更新函數,它會在遊戲的每一幀都執行,以此來創造一個動態的遊戲
     */
    update: function() {
        // 小鳥超過範圍區域則從新開始遊戲
        if (this.bird.y < 0 || this.bird.y > 490)
            this.restartGame();

        //小鳥慢慢向下旋轉,直到某個點
        if (this.bird.angle < 20)
            this.bird.angle += 1;

        //當鳥死亡時,從新開始遊戲
        //game.physics.arcade.overlap(this.bird, this.pipes, this.restartGame, null, this);
        //當鳥死亡時,從屏幕上掉下來
        game.physics.arcade.overlap(this.bird, this.pipes, this.hitPipe, null, this);
    },

(2)jump函數:點擊屏幕控制小鳥飛翔狀態

jump: function() {
        //在死亡時不可以讓鳥跳躍
        if (this.bird.alive == false)
            return;
        // 點擊屏幕小鳥的重力系數變爲負數,即可起飛
        this.bird.body.velocity.y = -350;

        //小鳥往上跳的時候,它向上旋轉
        game.add.tween(this.bird).to({angle: -20}, 100).start();
        
        //小鳥起飛的音效
        this.jumpSound.play();
    },

(3)管道組:創建管道和管道組,經過startGame添加定時器控制管道定時生成和移動。而後這邊還有個分數計算,這裏設定就是每生成一個管道就加一分。

addOnePipe: function(x, y) {
        var pipe = game.add.sprite(x, y, 'pipe');
        this.pipes.add(pipe);
        game.physics.arcade.enable(pipe);
        pipe.body.velocity.x = -200;

        pipe.checkWorldBounds = true;
        pipe.outOfBoundsKill = true;
    },

    addRowOfPipes: function() {
        // Randomly pick a number between 1 and 5
        // This will be the hole position
        var hole = Math.floor(Math.random() * 5) + 1;

        // Add the 6 pipes
        // With one big hole at position 'hole' and 'hole + 1'
        for (var i = 0; i < 8; i++)
            if (i != hole && i != hole + 1)
                this.addOnePipe(285, i * 60 + 10);

        //每建立一個新管子就把分數加1
        this.score += 1;
        this.labelScore.text = this.score;
    },

(3)接下來,就是判斷小鳥撞擊管道的時候死亡了

hitPipe: function() {
        // If the bird has already hit a pipe, do nothing
        // It means the bird is already falling off the screen
        if (this.bird.alive == false)
            return;

        // Set the alive property of the bird to false
        this.bird.alive = false;

        // Prevent new pipes from appearing
        game.time.events.remove(this.timer);

        // Go through all the pipes, and stop their movement
        this.pipes.forEach(function(p){
            p.body.velocity.x = 0;
        }, this);
    }

(4)最後,就是當小鳥各類死亡後,就是遊戲結束了,這時候須要啓動從新開始遊戲的設定。設定跳轉菜單頁面。

// 從新開始遊戲
    restartGame: function() {
        game.state.start('menu');
    },

到這裏就結束啦,有興趣就去github上fork個人代碼也行哦!以爲不錯的請賞賜一個贊!!

相關文章
相關標籤/搜索