# 开发小游戏

# 小游戏能力快速引导

以下可能是你在开发小游戏过程中较为常用的一些能力,完整的能力列表可以查阅 开发小游戏

# 开发小游戏示例解析

# 小游戏项目结构

├── game.js
├── game.json
game.js是游戏执行逻辑的入口文件。
game.json是游戏的配置文件,是游戏运行时的配置。

# game.json 的文件内容为一个 JSON 对象,包含以下属性:

属性 类型 必填 默认值 描述
deviceOrientation String 'portrait' 屏幕选择方向
workers String - 在 game.json 中可配置 Worker 代码放置的目录,目录下的所有 JS 代码最终将被打包成一个 JS 文件
subpackages Object Array 'portrait' 分包结构配置
iOSHighPerformance Boolean true 是否开启 iOS 高性能模式。默认开启,当前不支持关闭

# 学习贪吃蛇游戏示例

贪吃蛇小游戏的初始项目结构如下:

├── game.js
├── game.json
├── js
│   │── food.js                            // 食物类
│   │── snake.js                           // 贪吃蛇类
│   │── main.js                            // 游戏入口函数
│   │── gameInfo.js                        // 绘制游戏信息

# 初始化Canvas

// 创建画布
this.canvas = tap.createCanvas()
this.ctx = this.canvas.getContext('2d')

# 开始游戏

通过模块导入语句引入了游戏中使用的不同模块,包括 贪吃蛇、食物、游戏信息。 在初始化Main类时,我们主要做了以下工作: 创建一块画布,初始化游戏对象,绑定触摸事件,使用户可以与游戏进行交互,开始游戏。

import Snake from './snake'
import Food from './food'
import GameInfo from './gameinfo'

export default class Main {
  constructor() {
    // 创建画布
    this.canvas = tap.createCanvas()
    this.ctx = this.canvas.getContext('2d')
    
    ...

    // 初始化游戏对象
    this.snake = new Snake(this)
    this.food = new Food(this)
    this.gameInfo = new GameInfo(this)
    
    // 绑定触摸事件
    this.bindEvent()
    
    // 开始游戏循环
    this.startGameLoop()
  }
  
  // 开始游戏循环
  startGameLoop() {
    this.lastTime = Date.now()
    this.loop()
  }
}

# 游戏循环

包含了游戏的核心玩法逻辑。

loop() {
  const now = Date.now()
  const dt = now - this.lastTime
  
  // 清除画布
  this.ctx.clearRect(0, 0, this.width, this.height)
  
  // 更新游戏状态
  if (!this.gameOver && dt > this.speed) {
    this.direction = this.nextDirection
    this.snake.move(this.direction)
    
    // 检查是否吃到食物
    if (this.snake.checkEat(this.food)) {
      this.food.reset()
      this.score += 10
      
      // 每得100分加速一次
      if (this.score % 100 === 0 && this.speed > 50) {
        this.speed -= 10
      }
    }
    
    // 检查是否游戏结束
    if (this.snake.checkCollision()) {
      this.gameOver = true
      console.log('游戏结束,得分:', this.score)
    }
    
    this.lastTime = now
  }
  // 绘制游戏画面
  this.drawBackground()
  this.food.draw()
  this.snake.draw()
  this.gameInfo.draw()
  
  // 继续下一帧
  requestAnimationFrame(this.loop.bind(this))
}

# 绘制背景

绘制游戏的画面背景。

drawBackground() {
  this.ctx.fillStyle = '#f3f3f3'
  this.ctx.fillRect(0, 0, this.width, this.height)
  
  // 绘制网格
  this.ctx.strokeStyle = '#e0e0e0'
  this.ctx.lineWidth = 0.5
  
  // 绘制竖线
  for (let x = 0; x <= this.width; x += this.gridSize) {
    this.ctx.beginPath()
    this.ctx.moveTo(x, 0)
    this.ctx.lineTo(x, this.height)
    this.ctx.stroke()
  }
  
  // 绘制横线
  for (let y = 0; y <= this.height; y += this.gridSize) {
    this.ctx.beginPath()
    this.ctx.moveTo(0, y)
    this.ctx.lineTo(this.width, y)
    this.ctx.stroke()
  }
}

# 绑定触摸事件

绑定事件,使用户可以通过触摸滑动来与游戏交互。

bindEvent() {
  // 触摸开始位置
  let startX = 0
  let startY = 0
  
  tap.onTouchStart(e => {
    if (this.gameOver) {
      // 游戏结束时,点击重新开始
      this.restart()
      return
    }
    
    startX = e.touches[0].clientX
    startY = e.touches[0].clientY
  })
  
  tap.onTouchEnd(e => {
    if (this.gameOver) return
    
    const endX = e.changedTouches[0].clientX
    const endY = e.changedTouches[0].clientY
    
    const dx = endX - startX
    const dy = endY - startY
    
    // 判断滑动方向
    if (Math.abs(dx) > Math.abs(dy)) {
      // 水平方向
      if (dx > 0 && this.direction !== 'left') {
        this.nextDirection = 'right'
      } else if (dx < 0 && this.direction !== 'right') {
        this.nextDirection = 'left'
      }
    } else {
      // 垂直方向
      if (dy > 0 && this.direction !== 'up') {
        this.nextDirection = 'down'
      } else if (dy < 0 && this.direction !== 'down') {
        this.nextDirection = 'up'
      }
    }
  })
}