实战

项目目录

├── images      // 图片
├── js
│   ├── libs    // 第三方库
│   └── states  // phaser 场景
├── game.js     // 游戏主入口
├── game.json
└── project.config.json

1.引入 Phaser,libs目录下的p2.js pixi.js、 phaser-split.js 和weapp-adapter.js,在game.js里引入

require('./js/libs/weapp-adapter')
window.p2 = require('./js/libs/p2')
window.PIXI = require('./js/libs/pixi')
window.Phaser = require('./js/libs/phaser-split')

2.编辑 game.js 创建游戏 ->

// 定义全局常量
window.WIDTH = 750                     // 游戏宽度
window.SCALE = WIDTH / canvas.width    // 游戏宽度/ canvas 宽度
window.HEIGHT = canvas.height * SCALE  // 游戏高度

// go: Global Object 用于在 state 之间共享数据和方法
window.go = {
  game: null,                      // 游戏实例
  userInfo: null,                  // 玩家信息
  opponentInfo: null,              // 对手信息
  common: null,                    // 公共函数
  server: null,                    // 与服务器的交互
  launchRoomId: null,              // 进入主菜单时需要加入的房间 id
  battle: null,                    // 对战状态
}

// 初始化游戏
const config = {
  width: WIDTH,             // 游戏世界宽度
  height: HEIGHT,           // 游戏世界高度
  renderer: Phaser.CANVAS,  // 渲染器,这里我们使用 canvas
  canvas: canvas            // 将游戏绘制在 adapter 为我们创建的 canvas 上
}
const game = new Phaser.Game(config)                   // 创建游戏
// 全局对象中保存一个 game 的引用
go.game = game
// 注册游戏场景
game.state.add('start', require('./js/states/start'))  // 添加 start 游戏场景
game.state.start('start') 
  • 引入 adapter 之后会自动创建一个 canvas ,我们的游戏要使用它来绘制才能正常显示。
  • 我们的视觉稿是按 750 的宽度设计的,这里我们按照宽度适应屏幕。
  • window.go 对象用来在 state 之间共享数据和方法
  • 创建场景 add,跳转场景页面 start

3.下载资源

  preload() {
    // 配置画面缩放
    this.scale.pageAlignHorizontally = true
    this.scale.pageAlignVertically = true
    this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL
    // 预加载资源
    this.load.image('bg_menu', 'images/bg_menu.png')
    this.load.image('bg_playing', 'images/bg_playing.png')
    this.load.image('bg_rank', 'images/bg_rank.png')
    this.load.image('bg_waiting', 'images/bg_waiting.png')
    this.load.image('avatar', 'images/avatar.png')
    this.load.image('avatar_unknow', 'images/avatar_unknow.png')
    this.load.image('btn', 'images/btn_menu.png')
    this.load.image('o', 'images/o.png')
    this.load.image('x', 'images/x.png')
    this.load.image('row', 'images/rank_row.png')
    this.load.image('avatars', 'images/result_avatars.png')
    this.load.image('win', 'images/result_win.png')
    this.load.image('lose', 'images/result_lose.png')
    this.load.image('draw', 'images/result_draw.png')
    this.load.image('bunting', 'images/bunting.png')
  }

4.生命周期里来处理游戏场景,跳转其他场景等

    this.game.add.image(0, 0, 'bg_menu');
    // 添加“开始游戏”按钮
    const startBtn = addStartBtn((userInfo) => {
      // 销毁开始按钮
      startBtn.destroy()
      // 将玩家信息存入 global object
      go.userInfo = userInfo
      // 预加载玩家头像,微信头像为空则不加载
      if (go.userInfo.avatarUrl !== '') {
        this.load.image(go.userInfo.avatarUrl, go.userInfo.avatarUrl)
        // 在 preload 生命周期函数以外进行的资源加载必须手动开始加载
        this.load.start()
        go.game.state.start('menu')
      }
      

5.游戏逻辑部分从created开始看起,基础差,需要自行了解下js游戏逻辑

// 超级人工智能对弈策略。。。
const AI_STRAGETAGE = [
  [1, 1],
  [0, 0], [0, 2], [2, 0], [2, 2],
  [0, 1], [1, 0], [1, 2], [2, 1],
]

let cd             // 倒计时
let board          // 棋盘
let currentPlayer  // 当前玩家
let intervalId     // 倒计时定时器 Id ,用于清理倒计时定时器
let lastTimestamp  // 用于计算倒计时
let renderCD       // 渲染倒计时
let setPiece       // 落子

// 游戏结束
function over(result) {
  // 清理倒计时定时器
  clearInterval(intervalId)
  // 调用 go.common.showResult 显示结果层
  go.common.showResult({
    result,
    // start 场景中,我们把玩家的基本信息存到了 go.userInfo 中
    meName: go.userInfo.nickName,
    // 新注册的微信用户头像地址为空字符串,遇到这种情况,我们提供一个默认头像
    meAvatar: go.userInfo.avatarUrl || 'avatar_unknow',
    opponentName: '电脑',
    opponentAvatar: 'avatar_unknow',
    // 结果层 UI 中有一个“回到首页”按钮,这里可以设置它的点击回调
    callback: () => {
      // 点击后回到主菜单场景
      go.game.state.start('menu')
    }
  })
}

/**
 * 落子,并返回游戏是否结束
 */
function placePiece(row, col) {
  // 玩家落子
  board[row][col] = currentPlayer
  setPiece(row, col, currentPlayer)
  // 检查游戏结果
  if (checkOver()) return true
  // 双方换手
  currentPlayer = 1 - currentPlayer
  return false
}

/**
 * 重设游戏
 */
function reset() {
  // 重设棋盘,0 是自己, 1是对手,-1是空
  board = [
    [-1, -1, -1],
    [-1, -1, -1],
    [-1, -1, -1],
  ]

  // 随机选择先手玩家
  currentPlayer = Math.round(Math.random())

  // 倒计时(每人 60 秒)
  cd = [60000, 60000]
  lastTimestamp = Date.now()
  intervalId = setInterval(() => {
    // 定时更新倒计时
    const current = Date.now()
    const delta = current - lastTimestamp
    lastTimestamp = current
    cd[currentPlayer] = cd[currentPlayer] - delta
    renderCD(cd[0], cd[1])

    // 时间到,当前执子玩家判负
    cd[0] <= 0 && over('lose')
    cd[1] <= 0 && over('win')
  }, 500)
}

/**
 * 检查游戏结果
 */
function checkOver() {
  // 调用 go.common.checkWin 判断是否形成胜局
  if (go.common.checkWin(board)) {
    // 若形成胜局且当前玩家执子,则获胜
    if (currentPlayer === 0) over('win')
    // 否则失败
    else over('lose')
    return true
  // 调用 go.common.checkDraw 判断是否形成平局
  } else if (go.common.checkDraw(board)) {
    over('draw')
    return true
  }
  return false
}

class Practice extends Phaser.State {
  create() {
    // 画背景
    this.add.image(0, 0, 'bg_playing')

    // 重设游戏
    reset()

    // 调用 go.common.addBattleInfo 绘制游戏信息
    // 该函数会绘制游戏信息,并返回一个用于更新倒计时的函数
    renderCD = go.common.addBattleInfo({
      meAvatar: go.userInfo.avatarUrl || 'avatar_unknow',
      meName: go.userInfo.nickName,
      opponentAvatar: 'avatar_unknow',
      opponentName: '电脑',
    })
    // 传入玩家及对手的倒计时,进行更新
    renderCD(cd[0], cd[1])

    // 调用 go.common.addPieces 画棋盘
    // 该函数接受一个函数作为棋子被点击后的回调函数,传入 row col 值
    // 并返回一个用于落子的函数
    setPiece = go.common.addPieces((row, col) => {
      // 判断有没有轮到玩家落子
      if (currentPlayer !== 0) return

      // 玩家落子
      const isOver = placePiece(row, col)
      if (isOver) return

      // 电脑从落子列表里找一个空的位置
      const availableCoord = AI_STRAGETAGE.find(coord => board[coord[0]][coord[1]] === -1)
      // 装作在努力计算,给玩家留点面子
      setTimeout(() => {
        // 落子
        const isOver = placePiece(availableCoord[0], availableCoord[1])
        // 游戏没有结束的话,通知玩家落子
        !isOver && go.common.alertYourTurn()
      }, 500 + 1000 * Math.random())
    })

    // 若随机到电脑先下,固定下中间
    if (currentPlayer === 1) {
      placePiece(1, 1)
    } else {
      go.common.alertYourTurn()
    }
  }
}

module.exports = Practice
上次更新: 1/17/2019, 10:40:39 AM