phaser3學習筆記:貪喫蛇遊戲

素材文件後續提供
示例代碼來自phaser官方例子。抄代碼的時候,做了些es6改造。

這個遊戲代碼,我沒完全理解清楚。

import Phaser from 'phaser'

// 貪喫蛇食物
class Food extends Phaser.GameObjects.Image {
  constructor(scene, x, y) {
    super(scene)
    this.setTexture('food')
    this.setPosition(x * 16, y * 16)
    this.setOrigin(0)
    this.total = 0
    scene.children.add(this)
  }

  eat() {
    this.total++
  }
}

// direction consts
const UP = 0
const DOWN = 1
const LEFT = 2
const RIGHT = 3

var Snake = new Phaser.Class({

  initialize: function Snake(scene, x, y) {
    this.headPosition = new Phaser.Geom.Point(x, y)

    this.body = scene.add.group()

    this.head = this.body.create(x * 16, y * 16, 'body')
    this.head.setOrigin(0)

    this.alive = true

    this.speed = 100

    this.moveTime = 0

    this.tail = new Phaser.Geom.Point(x, y)

    this.heading = RIGHT
    this.direction = RIGHT
  },

  update: function(time) {
    if (time >= this.moveTime) {
      return this.move(time)
    }
  },

  faceLeft: function() {
    if (this.direction === UP || this.direction === DOWN) {
      this.heading = LEFT
    }
  },

  faceRight: function() {
    if (this.direction === UP || this.direction === DOWN) {
      this.heading = RIGHT
    }
  },

  faceUp: function() {
    if (this.direction === LEFT || this.direction === RIGHT) {
      this.heading = UP
    }
  },

  faceDown: function() {
    if (this.direction === LEFT || this.direction === RIGHT) {
      this.heading = DOWN
    }
  },

  move: function(time) {
    /**
     * Based on the heading property (which is the direction the pgroup pressed)
     * we update the headPosition value accordingly.
     *
     * The Math.wrap call allow the snake to wrap around the screen, so when
     * it goes off any of the sides it re-appears on the other.
     */
    switch (this.heading) {
      case LEFT:
        this.headPosition.x = Phaser.Math.Wrap(this.headPosition.x - 1, 0, 40)
        break
      case RIGHT:
        this.headPosition.x = Phaser.Math.Wrap(this.headPosition.x + 1, 0, 40)
        break
      case UP:
        this.headPosition.y = Phaser.Math.Wrap(this.headPosition.y - 1, 0, 30)
        break
      case DOWN:
        this.headPosition.y = Phaser.Math.Wrap(this.headPosition.y + 1, 0, 30)
        break
    }

    this.direction = this.heading

    //  Update the body segments and place the last coordinate into this.tail
    Phaser.Actions.ShiftPosition(this.body.getChildren(), this.headPosition.x * 16, this.headPosition.y * 16, 1, this.tail)

    //  Check to see if any of the body pieces have the same x/y as the head
    //  If they do, the head ran into the body

    var hitBody = Phaser.Actions.GetFirst(this.body.getChildren(), { x: this.head.x, y: this.head.y }, 1)

    if (hitBody) {
      this.alive = false
      return false
    } else {
      //  Update the timer ready for the next movement
      this.moveTime = time + this.speed

      return true
    }
  },

  grow: function() {
    var newPart = this.body.create(this.tail.x, this.tail.y, 'body')

    newPart.setOrigin(0)
  },

  collideWithFood: function(food) {
    if (this.head.x === food.x && this.head.y === food.y) {
      this.grow()

      food.eat()

      //  For every 5 items of food eaten we'll increase the snake speed a little
      if (this.speed > 20 && food.total % 5 === 0) {
        this.speed -= 5
      }

      return true
    } else {
      return false
    }
  },

  updateGrid: function(grid) {
    //  Remove all body pieces from valid positions list
    this.body.children.each(function(segment) {

      let bx = segment.x / 16
      let by = segment.y / 16

      grid[by][bx] = false

    })

    return grid
  }

})

class GameScene extends Phaser.Scene {
  constructor() {
    super({ key: 'GameScene' })
  }

  preload() {
    this.load.image('food', 'assets/games/snake/food.png')
    this.load.image('body', 'assets/games/snake/body.png')
  }

  create() {
    this.food = new Food(this, 3, 4)
    this.snake = new Snake(this, 8, 8)
    // Create our keyboard controls
    this.cursors = this.input.keyboard.createCursorKeys()
  }

  update(time) {
    if (!this.snake.alive) {
      return
    }
    /**
     * Check which key is pressed, and then change the direction the snake
     * is heading based on that. The checks ensure you don't double-back
     * on yourself, for example if you're moving to the right and you press
     * the LEFT cursor, it ignores it, because the only valid directions you
     * can move in at that time is up and down.
     */
    if (this.cursors.left.isDown) {
      this.snake.faceLeft()
    } else if (this.cursors.right.isDown) {
      this.snake.faceRight()
    } else if (this.cursors.up.isDown) {
      this.snake.faceUp()
    } else if (this.cursors.down.isDown) {
      this.snake.faceDown()
    }

    if (this.snake.update(time)) {
      //  If the snake updated, we need to check for collision against food
      if (this.snake.collideWithFood(this.food)) {
        this.repositionFood()
      }
    }
  }

  /**
   * We can place the food anywhere in our 40x30 grid
   * *except* on-top of the snake, so we need
   * to filter those out of the possible food locations.
   * If there aren't any locations left, they've won!
   *
   * @method repositionFood
   * @return {boolean} true if the food was placed, otherwise false
   */
  repositionFood() {
    //  First create an array that assumes all positions
    //  are valid for the new piece of food

    //  A Grid we'll use to reposition the food each time it's eaten
    let testGrid = []

    for (let y = 0; y < 30; y++) {
      testGrid[y] = []
      for (let x = 0; x < 40; x++) {
        testGrid[y][x] = true
      }
    }

    this.snake.updateGrid(testGrid)

    //  Purge out false positions
    let validLocations = []

    for (let y = 0; y < 30; y++) {
      for (let x = 0; x < 40; x++) {
        if (testGrid[y][x] === true) {
          //  Is this position valid for food? If so, add it here ...
          validLocations.push({ x: x, y: y })
        }
      }
    }

    if (validLocations.length > 0) {
      //  Use the RNG to pick a random food position
      let pos = Phaser.Math.RND.pick(validLocations)
      //  And place it
      this.food.setPosition(pos.x * 16, pos.y * 16)
      return true
    } else {
      return false
    }
  }
}

export function SnakeInit(ele) {
  return new Phaser.Game({
    type: Phaser.AUTO,
    width: 600,
    height: 480,
    backgroundColor: '#bfcc00',
    parent: ele,
    scene: [GameScene]
  })
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章