[phaser3入門探坑]使用phaser3製做山寨馬里奧

前言

phaser是一個優秀的前端canvas庫,封裝了不少底層的實現,能夠用來製做遊戲,h5場景等。今年1月新發布了phaser3,到今天爲止已經更新到了3.30。html

聲明

本遊戲來自於phaser小站的官方教程,加入了一些我的的註釋,本文旨在幫助各位觀衆老爺快速上手。

各位看官也能夠直接移步phaser官網查看教程前端

小貼士

每個步驟後面都貼了代碼,若是由於個人寫做方式讓您難以接受,能夠直接到每一個步驟後面複製代碼git

準備工做

你須要一份phaser3.js
也可能須要一份文檔
若是有一份隨時查閱的範例固然更好
一個本地服務器
一份包含素材的空[項目]()github

開始製做

這裏是爲觀衆老爺們準備的github倉庫,有咱們須要的素材和腳本文件canvas

git clone https://github.com/YexChen/canvas_game.git

製做基本的遊戲場景

打開咱們的項目文件夾,修改index.html瀏覽器

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/phaser.min.js"></script>
  <style>
    body{

    }
  </style>
</head>
<body>
  <script>
    //生成配置文件
    const config = {
      //初始化遊戲類型
      type : Phaser.AUTO,
      width : 800,
      height : 600,
      //物理引擎
      physics : {
        default : 'arcade',
        arcade : {
          //重力設置
          gravity : {y : 300},
          debug : false
        }
      },
      //場景設置
      scene : {
        preload,
        create,
        update
      }
    }
    //初始化遊戲
    let game = new Phaser.Game(config)
    //遊戲主要函數
    function preload(){

    }

    function create(){

    }

    function update(){

    }
  </script>
</body>
</html>

而後在命令行運行http-server,打開瀏覽器,效果是否是出來了呢?
空白的遊戲界面服務器

在上面的腳本中,咱們定義了三個函數,preload,create,update,分別表明遊戲中的預加載,初始化函數,更新函數。

加載主要素材

在preload函數中加入如下代碼段:ide

this.load.image("sky","./img/sky.png")
      this.load.image("star","./img/star.png")
      this.load.image("ground","./img/platform.png")
      this.load.image("bomb","./img/bomb.png")
      this.load.spritesheet("dude","./img/dude.png",{frameWidth:32,frameHeight:48})

spritesheet是精靈圖的加載方式,frameWidth是每幀的寬度,frameHeight是幀的高度,有興趣的朋友們能夠量一量函數

加載主要場景

摸了這麼久的魚,也該看點成果了吧,咱們來製做主場景:
在create函數中加入:動畫

this.add.image(400,300,"sky")

保存,刷新,咱們的界面上是否是出現了一片藍天呢?
圖片描述

this.add.image(offsetX,offSetY,imagename)
有興趣的朋友們能夠調下參數,試一下(0,0,'sky')是在哪一個地方的

讓咱們來繼續添加場景吧,緊跟着上一句輸入如下代碼:

platforms = this.physics.add.staticGroup()
      platforms.create(400,568,"ground").setScale(2,2).refreshBody()
      platforms.create(600,400,"ground")
      platforms.create(0,300,"ground")
      platforms.create(600,200,"ground")
      platforms.create(0,100,"ground")
create(x,y,imagename)
selScale(x,y):把圖片縮放x,y倍,若是不設置y的話就按x的倍數縮放
這個refreshbody你們能夠去掉,後面會有驚喜的

圖片描述

這樣場景就繪製出來了,各位看官也能夠本身設置參數,製做屬於本身的遊戲場景

圖片描述

儘可能不要作出這種反人類設計就行。。emmm,你的遊戲你作主咯

可能內容多,你們可能會打錯地方,發一下完整的代碼段:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/phaser.min.js"></script>
  <style>
    body{

    }
  </style>
</head>
<body>
  <script>
    //生成配置文件
    const config = {
      //初始化遊戲類型
      type : Phaser.AUTO,
      width : 800,
      height : 600,
      //物理引擎
      physics : {
        default : 'arcade',
        arcade : {
          //重力設置
          gravity : {y : 300},
          debug : false
        }
      },
      //場景設置
      scene : {
        preload,
        create,
        update
      }
    }
    //初始化遊戲
    let game = new Phaser.Game(config)
    //遊戲主要函數
    function preload()
    {
      this.load.image("sky","./img/sky.png")
      this.load.image("star","./img/star.png")
      this.load.image("ground","./img/platform.png")
      this.load.image("bomb","./img/bomb.png")
      this.load.spritesheet("dude","./img/dude.png",{frameWidth:32,frameHeight:48})
    }

    function create()
    {
      this.add.image(400,300,"sky")

      platforms = this.physics.add.staticGroup()

      platforms.create(400,568,"ground").setScale(2,2).refreshBody()

      platforms.create(600,400,"ground")
      platforms.create(0,300,"ground")
      platforms.create(600,200,"ground")
      platforms.create(0,100,"ground")

    }

    function update()
    {

    }
  </script>
</body>
</html>

人物的製做

什麼都有了,主角怎麼能少呢?
緊接着上一行,寫下代碼:
player = this.physics.add.sprite(100,450,'dude')

刷新一下,是否是看到咱們的男主角生成出來。。而後又入土爲安了呢?
聰明的你應該會想到:是缺乏了碰撞函數,那麼,讓咱們來添加碰撞函數吧,緊接着添加:

player.setBounce(0.2)
      player.setCollideWorldBounds(true)

刷新頁面,哇塞
圖片描述

咱們的男主真入土爲安了!

嗯,這不是咱們想要的結果,至少不是個人。。咱們好像忘記給障礙物添加碰撞了,咱們來加一下吧:

this.physics.add.collider(player,platforms)
你們還記得哪一個refreshbody嗎?若是你當時刪掉了它,那麼碰撞就仍是不會成立(話說這種東西做者去內置一個方法不就行了麼)

這裏貼出目前完整的代碼:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/phaser.min.js"></script>
  <style>
    body{

    }
  </style>
</head>
<body>
  <script>
    //生成配置文件
    const config = {
      //初始化遊戲類型
      type : Phaser.AUTO,
      width : 800,
      height : 600,
      //物理引擎
      physics : {
        default : 'arcade',
        arcade : {
          //重力設置
          gravity : {y : 300},
          debug : false
        }
      },
      //場景設置
      scene : {
        preload,
        create,
        update
      }
    }
    //初始化遊戲
    let game = new Phaser.Game(config)
    //遊戲主要函數
    function preload()
    {
      this.load.image("sky","./img/sky.png")
      this.load.image("star","./img/star.png")
      this.load.image("ground","./img/platform.png")
      this.load.image("bomb","./img/bomb.png")
      this.load.spritesheet("dude","./img/dude.png",{frameWidth:32,frameHeight:48})
    }

    function create()
    {
      this.add.image(400,300,"sky")

      platforms = this.physics.add.staticGroup()

      platforms.create(400,568,"ground").setScale(2,2).refreshBody()

      platforms.create(600,400,"ground")
      platforms.create(0,300,"ground")
      platforms.create(600,200,"ground")
      platforms.create(0,100,"ground")

      player = this.physics.add.sprite(100,450,'dude')
      player.setBounce(0.2)
      player.setCollideWorldBounds(true)

      this.physics.add.collider(player,platforms)
    }

    function update()
    {

    }
  </script>
</body>
</html>

添加動畫效果和鍵盤控制器

要是不能操控的話,那這遊戲也太佛繫了,咱們來添加一動畫效果吧
Phaser類有個anims成員,用來管理全部的動畫效果(說白了就是改變圖片嘛),接下來咱們經過代碼感覺一下,添加到上述代碼後面:

this.anims.create({
        key : 'left',
        frames : this.anims.generateFrameNumbers('dude',{start : 0,end : 3}),
        frameRate : 10,
        repeat : -1
      })

      this.anims.create({
        key : 'turn',
        frames : [{key : 'dude',frame : 4}],
        frameRate : 20
      })

      this.anims.create({
        key : 'right',
        frames : this.anims.generateFrameNumbers('dude',{
          start : 5,end : 8
        }),
        frameRate : 10,
        repeat : -1
      })

而後咱們初始化遙控器吧:

cursors = this.input.keyboard.createCursorKeys()

按下鍵盤方向鍵上下左右,誒?爲何沒反應?
咱們好像忘記在update函數中監聽鍵盤了,難怪沒反應,
在update函數中添加如下代碼:

if(cursors.left.isDown)
      {
        player.setVelocityX(-50)

        player.anims.play("left",true)
      }
      else if(cursors.right.isDown)
      {
        player.setVelocityX(50)

        player.anims.play("right",true)
      }
      else{
        player.setVelocityX(0)

        player.anims.play('turn')
      }
      
      if(cursors.up.isDown && player.body.touching.down){
        player.setVelocityY(-300)
      }

好,如今移動咱們人物,哇,走的怎麼這麼慢!各位本身改下參數吧,每一個人都有不一樣的遊戲愛好,你必定能夠找到最適合本身的配置的,固然啦,也能夠玩出滑冰模式,月球模式,鬼畜模式,神仙模式,鬼人正邪模式等等。。開發遊戲主要靠想象力對吧

貼一下咱們的代碼

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/phaser.min.js"></script>
  <style>
    body{

    }
  </style>
</head>
<body>
  <script>
    //生成配置文件
    const config = {
      //初始化遊戲類型
      type : Phaser.AUTO,
      width : 800,
      height : 600,
      //物理引擎
      physics : {
        default : 'arcade',
        arcade : {
          //重力設置
          gravity : {y : 300},
          debug : false
        }
      },
      //場景設置
      scene : {
        preload,
        create,
        update
      }
    }
    //初始化遊戲
    let game = new Phaser.Game(config)
    //遊戲主要函數
    function preload()
    {
      this.load.image("sky","./img/sky.png")
      this.load.image("star","./img/star.png")
      this.load.image("ground","./img/platform.png")
      this.load.image("bomb","./img/bomb.png")
      this.load.spritesheet("dude","./img/dude.png",{frameWidth:32,frameHeight:48})
    }

    function create()
    {
      this.add.image(400,300,"sky")

      platforms = this.physics.add.staticGroup()

      platforms.create(400,568,"ground").setScale(2,2).refreshBody()

      platforms.create(600,400,"ground")
      platforms.create(0,300,"ground")
      platforms.create(600,200,"ground")
      platforms.create(0,100,"ground")

      player = this.physics.add.sprite(100,450,'dude')
      player.setBounce(0.2)
      player.setCollideWorldBounds(true)

      this.physics.add.collider(player,platforms)

      this.anims.create({
        key : 'left',
        frames : this.anims.generateFrameNumbers('dude',{start : 0,end : 3}),
        frameRate : 10,
        repeat : -1
      })

      this.anims.create({
        key : 'turn',
        frames : [{key : 'dude',frame : 4}],
        frameRate : 20
      })

      this.anims.create({
        key : 'right',
        frames : this.anims.generateFrameNumbers('dude',{
          start : 5,end : 8
        }),
        frameRate : 10,
        repeat : -1
      })

      cursors = this.input.keyboard.createCursorKeys()
    }

    function update()
    {
      if(cursors.left.isDown)
      {
        player.setVelocityX(-50)

        player.anims.play("left",true)
      }
      else if(cursors.right.isDown)
      {
        player.setVelocityX(50)

        player.anims.play("right",true)
      }
      else{
        player.setVelocityX(0)

        player.anims.play('turn')
      }
      
      if(cursors.up.isDown && player.body.touching.down){
        player.setVelocityY(-300)
      }
    }
  </script>
</body>
</html>

早苗教你畫星星

好了,人物有了,接下來應該作點道具了吧,咱們來畫點星星,在create函數中添加代碼:

stars = this.physics.add.group({
        key : 'star',
        repeat : 11,
        setXY : {x: 20,y: 0,stepX:70}
      })

      stars.children.iterate(function(child){
        //設置一下碰撞效果
        child.setBounceY(Phaser.Math.FloatBetween(0.4,0.8))
      })

      this.physics.add.collider(stars,platforms)
咱們初始化了一些星星,添加了小小的碰撞效果,然而。。。

吃不到!!

並不能吃到星星!就像一大盤香噴噴羊蠍子在你面前你卻不能吃(我這篇博客定到晚12點發就行了)

由於沒有寫星星和男主的碰撞函數,咱們來在後面寫一行

this.physics.add.overlap(player,stars,collectStar,null,this)
overlap(obj1,obj2,overcallback,processcallback,回掉中的上下文(this))

而後在文件底部加一個函數:

function collectStar(player,star)
    {
      //讓star實體消失
      star.disableBody(true,true)
    }

好了,如今能夠正常的吃星星了

貼上目前的代碼段:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/phaser.min.js"></script>
  <style>
    body{

    }
  </style>
</head>
<body>
  <script>
    //生成配置文件
    const config = {
      //初始化遊戲類型
      type : Phaser.AUTO,
      width : 800,
      height : 600,
      //物理引擎
      physics : {
        default : 'arcade',
        arcade : {
          //重力設置
          gravity : {y : 300},
          debug : false
        }
      },
      //場景設置
      scene : {
        preload,
        create,
        update
      }
    }
    //初始化遊戲
    let game = new Phaser.Game(config)
    //遊戲主要函數
    function preload()
    {
      this.load.image("sky","./img/sky.png")
      this.load.image("star","./img/star.png")
      this.load.image("ground","./img/platform.png")
      this.load.image("bomb","./img/bomb.png")
      this.load.spritesheet("dude","./img/dude.png",{frameWidth:32,frameHeight:48})
    }

    function create()
    {
      this.add.image(400,300,"sky")

      platforms = this.physics.add.staticGroup()

      platforms.create(400,568,"ground").setScale(2,2).refreshBody()

      platforms.create(600,400,"ground")
      platforms.create(0,300,"ground")
      platforms.create(600,200,"ground")

      player = this.physics.add.sprite(100,450,'dude')
      player.setBounce(0.2)
      player.setCollideWorldBounds(true)

      this.physics.add.collider(player,platforms)

      this.anims.create({
        key : 'left',
        frames : this.anims.generateFrameNumbers('dude',{start : 0,end : 3}),
        frameRate : 10,
        repeat : -1
      })

      this.anims.create({
        key : 'turn',
        frames : [{key : 'dude',frame : 4}],
        frameRate : 20
      })

      this.anims.create({
        key : 'right',
        frames : this.anims.generateFrameNumbers('dude',{
          start : 5,end : 8
        }),
        frameRate : 10,
        repeat : -1
      })

      cursors = this.input.keyboard.createCursorKeys()

      stars = this.physics.add.group({
        key : 'star',
        repeat : 11,
        setXY : {x: 20,y: 0,stepX:70}
      })

      stars.children.iterate(function(child){
        //設置一下碰撞效果
        child.setBounceY(Phaser.Math.FloatBetween(0.4,0.8))
      })

      this.physics.add.collider(stars,platforms)
      this.physics.add.overlap(player,stars,collectStar,null,this)
    }

    function update()
    {
      if(cursors.left.isDown)
      {
        player.setVelocityX(-200)

        player.anims.play("left",true)
      }
      else if(cursors.right.isDown)
      {
        player.setVelocityX(200)

        player.anims.play("right",true)
      }
      else{
        player.setVelocityX(0)

        player.anims.play('turn')
      }

      if(cursors.up.isDown && player.body.touching.down){
        player.setVelocityY(-400)
      }
    }

    function collectStar(player,star)
    {
      //讓star實體消失
      star.disableBody(true,true)
    }

  </script>
</body>
</html>

計分系統和炸彈,以及遊戲結束

若是這個遊戲沒有計分系統和炸彈的話,那麼這個遊戲也太過於佛繫了
在preload前面加上一行:

let score = 0
let scoreText
let gameover = false

而後在create函數中尾部添加:

bombs = this.physics.add.group()
      scoreText = this.add.text(16,16,"score : 0",{fontSize: '32px',fill: "#000"})
這是一個炸彈羣組,咱們在全部星星被吃光之後使用這個羣組添加炸彈

修改collectstar函數爲:

function collectStar(player,star)
    {
      //讓star實體消失
      star.disableBody(true,true)

      score += 1000
      scoreText.setText("score :"+ score)
      if(stars.countActive(true) === 0)
      {
        stars.children.iterate(function(child)
        {
          child.enableBody(true,child.x,0,true,true)
        })
        var x = (player.x<400)?Phaser.Math.Between(400,800):Phaser.Math.Between(0,400)

        var bomb = bombs.create(x,16,'bomb')
        bomb.setBounce(true)
        bomb.setCollideWorldBounds(true)
        bomb.setVelocity(Phaser.Math.Between(-200,200),20)
        bomb.allowGravity = false
      }
    }

而後在create函數中加上碰撞:

this.physics.add.collider(bombs,platforms)
      this.physics.add.collider(player,bombs,bombbbb,null,this)

在文件尾部加上撞擊函數:

function bombbbb()
  {
    this.physics.pause()

    //塗色,我以爲綠綠的比較好看
    player.setTint(0x00ff00)

    player.anims.play("turn")

    gameover = true
  }

至此,咱們的遊戲就大功告成啦。。誒,好像個人人物還能動?這個問題就留給你們本身解決了哈哈
圖片描述
完整代碼:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="js/phaser.min.js"></script>
  <style>
    body{

    }
  </style>
</head>
<body>
  <script>
    //生成配置文件
    const config = {
      //初始化遊戲類型
      type : Phaser.AUTO,
      width : 800,
      height : 600,
      //物理引擎
      physics : {
        default : 'arcade',
        arcade : {
          //重力設置
          gravity : {y : 300},
          debug : false
        }
      },
      //場景設置
      scene : {
        preload,
        create,
        update
      }
    }
    //初始化遊戲
    let game = new Phaser.Game(config)
    let score = 0
    let scoreText
    let gameover = false
    //遊戲主要函數
    function preload()
    {
      this.load.image("sky","./img/sky.png")
      this.load.image("star","./img/star.png")
      this.load.image("ground","./img/platform.png")
      this.load.image("bomb","./img/bomb.png")
      this.load.spritesheet("dude","./img/dude.png",{frameWidth:32,frameHeight:48})
    }

    function create()
    {
      this.add.image(400,300,"sky")

      platforms = this.physics.add.staticGroup()

      platforms.create(400,568,"ground").setScale(2,2).refreshBody()

      platforms.create(600,400,"ground")
      platforms.create(0,300,"ground")
      platforms.create(600,200,"ground")

      player = this.physics.add.sprite(100,450,'dude')
      player.setBounce(0.2)
      player.setCollideWorldBounds(true)

      this.physics.add.collider(player,platforms)

      this.anims.create({
        key : 'left',
        frames : this.anims.generateFrameNumbers('dude',{start : 0,end : 3}),
        frameRate : 10,
        repeat : -1
      })

      this.anims.create({
        key : 'turn',
        frames : [{key : 'dude',frame : 4}],
        frameRate : 20
      })

      this.anims.create({
        key : 'right',
        frames : this.anims.generateFrameNumbers('dude',{
          start : 5,end : 8
        }),
        frameRate : 10,
        repeat : -1
      })

      cursors = this.input.keyboard.createCursorKeys()

      stars = this.physics.add.group({
        key : 'star',
        repeat : 11,
        setXY : {x: 20,y: 0,stepX:70}
      })

      stars.children.iterate(function(child){
        //設置一下碰撞效果
        child.setBounceY(Phaser.Math.FloatBetween(0.4,0.8))
      })

      this.physics.add.collider(stars,platforms)
      this.physics.add.overlap(player,stars,collectStar,null,this)
      bombs = this.physics.add.group()
      scoreText = this.add.text(16,16,"score : 0",{fontSize: '32px',fill: "#000"})
      this.physics.add.collider(bombs,platforms)
      this.physics.add.collider(player,bombs,bombbbb,null,this)
    }

    function update()
    {
      if(cursors.left.isDown)
      {
        player.setVelocityX(-200)

        player.anims.play("left",true)
      }
      else if(cursors.right.isDown)
      {
        player.setVelocityX(200)

        player.anims.play("right",true)
      }
      else{
        player.setVelocityX(0)

        player.anims.play('turn')
      }

      if(cursors.up.isDown && player.body.touching.down){
        player.setVelocityY(-400)
      }
    }

    function collectStar(player,star)
    {
      //讓star實體消失
      star.disableBody(true,true)

      score += 1000
      scoreText.setText("score :"+ score)
      if(stars.countActive(true) === 0)
      {
        stars.children.iterate(function(child)
        {
          child.enableBody(true,child.x,0,true,true)
        })
        var x = (player.x<400)?Phaser.Math.Between(400,800):Phaser.Math.Between(0,400)

        var bomb = bombs.create(x,16,'bomb')
        bomb.setBounce(true)
        bomb.setCollideWorldBounds(true)
        bomb.setVelocity(Phaser.Math.Between(-200,200),20)
        bomb.allowGravity = false
      }
    }

  function bombbbb()
  {
    this.physics.pause()

    //塗色,我以爲綠綠的比較好看
    player.setTint(0x00ff00)

    player.anims.play("turn")

    gameover = true
  }
  </script>
</body>
</html>
相關文章
相關標籤/搜索