uniapp学习5,兼容微信小程序的俄罗斯方块游戏

tetris.vue

html 复制代码
<template>
  <view class="container">
    <!-- 开始弹窗 -->
    <view class="modal" v-if="showStartModal">
      <view class="modal-content">
        <text class="title">俄罗斯方块</text>
        <view class="speed-box">
          <text>速度:{{ speed }}</text>
          <slider :value="speed" min="1" max="10" activeColor="#42b983" @change="onSpeedChange" />
        </view>
        <button class="start-btn" @click="startGame">开始游戏</button>
      </view>
    </view>

    <!-- 游戏画布 -->
    <canvas id="game" canvas-id="game" class="game-canvas"></canvas>

    <!-- 分数 -->
    <view class="score" v-if="gameStarted">分数:{{ score }}</view>

    <!-- 控制按钮 -->
    <view class="control-group" v-if="gameStarted">
      <button @click="moveLeft">◀</button>
      <button @click="rotate">⟳</button>
      <button @click="moveRight">▶</button>
      <button @click="dropDown">▼</button>
      <button @click="pauseGame">⏸</button>
    </view>

    <!-- 暂停弹窗 -->
    <view class="modal" v-if="showPauseModal">
      <view class="modal-content">
        <text class="title">{{ gameOver ? '游戏结束' : '暂停' }}</text>
        <button @click="resumeGame" v-if="!gameOver">继续</button>
        <button @click="restartGame">重新开始</button>
        <button @click="exitGame">退出</button>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      ctx: null,
      width: 300,
      height: 600,
      cols: 10,
      rows: 20,
      block: 30,
      board: [],
      current: null,
      score: 0,
      gameStarted: false,
      gameOver: false,
      paused: false,
      showStartModal: true,
      showPauseModal: false,
      speed: 2,
      timer: null,

      shapes: [
        [[1, 1, 1, 1]],
        [[1, 1], [1, 1]],
        [[1, 1, 1], [0, 1, 0]],
        [[1, 1, 1], [1, 0, 0]],
        [[1, 1, 1], [0, 0, 1]],
        [[1, 1, 0], [0, 1, 1]],
        [[0, 1, 1], [1, 1, 0]]
      ],
      colors: [
        [0, 255, 255], [255, 255, 0], [128, 0, 128],
        [255, 165, 0], [0, 0, 255], [0, 255, 0], [255, 0, 0]
      ]
    }
  },

  onReady() {
    this.ctx = uni.createCanvasContext('game', this)
    this.drawBg()
  },

  onUnload() {
    clearInterval(this.timer)
  },

  methods: {
    onSpeedChange(e) {
      this.speed = e.detail.value
    },

    initBoard() {
      let board = []
      for (let y = 0; y < this.rows; y++) {
        board[y] = []
        for (let x = 0; x < this.cols; x++) {
          board[y][x] = 0
        }
      }
      this.board = board
    },

    newPiece() {
      let i = Math.floor(Math.random() * this.shapes.length)
      let shape = this.shapes[i]
      this.current = {
        shape,
        color: this.colors[i],
        x: Math.floor(this.cols / 2) - Math.floor(shape[0].length / 2),
        y: 0
      }
    },

    startGame() {
      this.showStartModal = false
      this.gameStarted = true
      this.gameOver = false
      this.paused = false
      this.score = 0
      this.initBoard()
      this.newPiece()
      this.startLoop()
    },

    startLoop() {
      clearInterval(this.timer)
      this.timer = setInterval(() => {
        if (this.paused || this.gameOver) return
        this.tick()
      }, 1000 / this.speed)
    },

    tick() {
      this.current.y++
      if (this.collide()) {
        this.current.y--
        this.merge()
        this.clearLines()
        this.newPiece()
        if (this.collide()) {
          this.gameOver = true
          this.showPauseModal = true
        }
      }
      this.drawAll()
    },

    collide() {
      let p = this.current
      for (let y = 0; y < p.shape.length; y++) {
        for (let x = 0; x < p.shape[y].length; x++) {
          if (p.shape[y][x]) {
            let nx = p.x + x
            let ny = p.y + y
            if (
              nx < 0 ||
              nx >= this.cols ||
              ny >= this.rows ||
              (ny >= 0 && this.board[ny][nx])
            ) return true
          }
        }
      }
      return false
    },

    merge() {
      let p = this.current
      for (let y = 0; y < p.shape.length; y++) {
        for (let x = 0; x < p.shape[y].length; x++) {
          if (p.shape[y][x]) {
            let ny = p.y + y
            let nx = p.x + x
            if (ny >= 0) this.board[ny][nx] = 1
          }
        }
      }
    },

    clearLines() {
      let lines = 0
      for (let y = this.rows - 1; y >= 0; y--) {
        if (this.board[y].every(v => v === 1)) {
          this.board.splice(y, 1)
          this.board.unshift(new Array(this.cols).fill(0))
          lines++
          y++
        }
      }
      this.score += lines * 100
    },

    drawAll() {
      this.drawBg()
      this.drawBoard()
      this.drawPiece()
      this.ctx.draw()
    },

    drawBg() {
      this.ctx.setFillStyle('#000')
      this.ctx.fillRect(0, 0, this.width, this.height)
    },

    drawBoard() {
      for (let y = 0; y < this.rows; y++) {
        for (let x = 0; x < this.cols; x++) {
          if (this.board[y][x]) {
            this.ctx.setFillStyle('#0ff')
            this.ctx.fillRect(x * this.block, y * this.block, this.block - 2, this.block - 2)
          }
        }
      }
    },

    drawPiece() {
      let p = this.current
      this.ctx.setFillStyle(`rgb(${p.color[0]},${p.color[1]},${p.color[2]})`)
      for (let y = 0; y < p.shape.length; y++) {
        for (let x = 0; x < p.shape[y].length; x++) {
          if (p.shape[y][x]) {
            let nx = p.x + x
            let ny = p.y + y
            this.ctx.fillRect(
              nx * this.block,
              ny * this.block,
              this.block - 2,
              this.block - 2
            )
          }
        }
      }
    },

    moveLeft() {
      this.current.x--
      if (this.collide()) this.current.x++
    },
    moveRight() {
      this.current.x++
      if (this.collide()) this.current.x--
    },
    rotate() {
      let shape = this.current.shape
      let rotated = shape[0].map((_, i) =>
        shape.map(row => row[i]).reverse()
      )
      let old = this.current.shape
      this.current.shape = rotated
      if (this.collide()) this.current.shape = old
    },
    dropDown() {
      while (!this.collide()) this.current.y++
      this.current.y--
    },

    pauseGame() {
      this.paused = true
      this.showPauseModal = true
    },
    resumeGame() {
      this.paused = false
      this.showPauseModal = false
    },
    restartGame() {
      clearInterval(this.timer)
      this.startGame()
      this.showPauseModal = false
    },
    exitGame() {
      clearInterval(this.timer)
      uni.navigateBack()
    }
  }
}
</script>

<style scoped>
.container {
  width: 100%;
  min-height: 100vh;
  background: #222;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20rpx;
}
.game-canvas {
  width: 300px;
  height: 600px;
  background: #000;
  border: 2px solid #fff;
}
.score {
  color: #fff;
  font-size: 32rpx;
  margin: 20rpx 0;
}
.control-group {
  display: flex;
  gap: 16rpx;
  margin-top: 20rpx;
}
.control-group button {
  width: 80rpx;
  height: 80rpx;
  border-radius: 50%;
  background: #42b983;
  color: #fff;
  font-size: 28rpx;
}
.modal {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 999;
}
.modal-content {
  background: #fff;
  padding: 40rpx;
  border-radius: 20rpx;
  text-align: center;
}
.title {
  font-size: 36rpx;
  font-weight: bold;
  margin-bottom: 30rpx;
  display: block;
}
.speed-box {
  margin: 20rpx 0;
}
.start-btn {
  background: #42b983;
  color: #fff;
  padding: 20rpx 40rpx;
  border-radius: 10rpx;
  margin-top: 20rpx;
}
</style>
相关推荐
知识分享小能手2 小时前
MongoDB入门学习教程,从入门到精通,MongoDB应用程序设计知识点梳理(9)
数据库·学习·mongodb
雷工笔记2 小时前
读书笔记《工程师进阶之路》
笔记·学习
智算菩萨2 小时前
【论文精读】通过元学习与关联规则挖掘增强人工智能在网络安全领域特征选择中的可解释性
论文阅读·人工智能·学习·web安全·论文笔记
Engineer邓祥浩2 小时前
JVM学习笔记(4) 第二部分 自动内存管理 第3章 垃圾收集器与分配策略
jvm·笔记·学习
VelinX2 小时前
【个人学习||ai提示词工程】01Prompt Engineering
学习
chase。3 小时前
【学习笔记】基于扩散模型的运动规划学习与适应
人工智能·笔记·学习
321.。3 小时前
Linux 进程控制深度解析:从创建到替换的完整指南
linux·开发语言·c++·学习
CheerWWW3 小时前
C++学习笔记——枚举、继承、虚函数、可见性
c++·笔记·学习
报错小能手5 小时前
ios开发方向——对于实习开发的app(Robopocket)讲解
开发语言·学习·ios·swift