用Vue复现Chrome小恐龙游戏
2025.09.23 12:22浏览量:1简介:本文通过Vue3实现Chrome离线小恐龙游戏,详细讲解Canvas渲染、碰撞检测、键盘事件等核心功能,提供完整代码实现与性能优化方案。
Vue 实现 Chrome 小恐龙游戏:从原理到完整实现
Chrome 浏览器在断网时显示的隐藏小恐龙游戏,凭借其简单的操作和复古的像素风格成为经典。本文将通过 Vue 3 组合式 API 实现这一游戏,重点解析 Canvas 渲染、物理碰撞检测、键盘事件监听等核心功能,并提供完整的代码实现与性能优化方案。
一、项目初始化与基础架构
1.1 Vue3 项目搭建
使用 Vite 创建 Vue3 项目,选择 TypeScript 模板以获得类型提示支持:
npm create vite@latest vue-dino-game -- --template vue-ts
1.2 游戏组件结构
采用单文件组件结构,将游戏划分为核心组件:
src/├── components/│ ├── GameCanvas.vue # 主画布渲染│ ├── Dino.vue # 恐龙角色│ ├── Obstacle.vue # 障碍物(仙人掌/鸟)│ └── GameController.vue # 游戏控制逻辑└── composables/└── useGame.ts # 游戏状态管理
1.3 Canvas 渲染基础
在 GameCanvas.vue 中初始化画布:
<template><canvas ref="canvasRef" :width="width" :height="height"></canvas></template><script setup lang="ts">import { ref, onMounted } from 'vue'const canvasRef = ref<HTMLCanvasElement>()const width = 800const height = 300onMounted(() => {const ctx = canvasRef.value!.getContext('2d')!// 后续渲染逻辑})</script>
二、核心游戏逻辑实现
2.1 游戏状态管理
使用 Composition API 封装游戏状态:
// composables/useGame.tsimport { ref, reactive } from 'vue'export function useGame() {const gameState = reactive({isRunning: false,score: 0,speed: 5,gravity: 0.6})const dino = reactive({x: 50,y: 200,width: 40,height: 50,velocityY: 0,isJumping: false})const obstacles = ref<Array<{x: number, type: 'cactus'|'bird'}>>([])return { gameState, dino, obstacles }}
2.2 物理系统实现
重力模拟:
function applyGravity() {if (dino.y < 200) { // 地面高度dino.velocityY += gameState.gravitydino.y += dino.velocityY} else {dino.y = 200dino.velocityY = 0dino.isJumping = false}}
跳跃控制:
function jump() {if (!dino.isJumping) {dino.velocityY = -12 // 初始跳跃速度dino.isJumping = true}}
2.3 障碍物生成系统
采用定时器生成随机障碍物:
function spawnObstacle() {const types = ['cactus', 'bird'] as constconst type = types[Math.floor(Math.random() * types.length)]obstacles.value.push({x: width,type})}// 每2秒生成一个障碍物setInterval(spawnObstacle, 2000)
三、碰撞检测系统
3.1 矩形碰撞检测
实现基础的 AABB(轴对齐边界框)碰撞检测:
function checkCollision(dino: typeof dino, obstacle: {x: number, type: string}) {const obstacleWidth = obstacle.type === 'cactus' ? 30 : 50const obstacleHeight = obstacle.type === 'cactus' ? 50 : 30return (dino.x < obstacle.x + obstacleWidth &&dino.x + dino.width > obstacle.x &&dino.y < height - obstacleHeight && // 地面障碍物dino.y + dino.height > height - obstacleHeight)}
3.2 碰撞响应
检测到碰撞时停止游戏:
function updateGame() {obstacles.value.forEach(obs => {obs.x -= gameState.speedif (checkCollision(dino, obs)) {gameState.isRunning = false// 触发游戏结束逻辑}})// 移除屏幕外障碍物obstacles.value = obstacles.value.filter(obs => obs.x > -50)}
四、完整渲染循环
4.1 游戏主循环
使用 requestAnimationFrame 实现流畅动画:
let animationId: numberfunction gameLoop() {if (!gameState.isRunning) returnapplyGravity()updateGame()render()animationId = requestAnimationFrame(gameLoop)}function startGame() {gameState.isRunning = truegameState.score = 0obstacles.value = []gameLoop()}
4.2 Canvas 渲染
实现完整的渲染逻辑:
function render() {const ctx = canvasRef.value!.getContext('2d')!// 清空画布ctx.clearRect(0, 0, width, height)// 绘制地面ctx.fillStyle = '#777'ctx.fillRect(0, height - 20, width, 20)// 绘制恐龙ctx.fillStyle = '#333'ctx.fillRect(dino.x, dino.y, dino.width, dino.height)// 绘制障碍物obstacles.value.forEach(obs => {ctx.fillStyle = obs.type === 'cactus' ? '#0a0' : '#a00'if (obs.type === 'cactus') {ctx.fillRect(obs.x, height - 50, 30, 50)} else {ctx.fillRect(obs.x, height - 70, 50, 30) // 飞行鸟更高}})// 绘制分数ctx.fillStyle = '#000'ctx.font = '20px Arial'ctx.fillText(`Score: ${gameState.score}`, 10, 30)}
五、性能优化方案
5.1 对象池技术
预创建障碍物对象避免频繁内存分配:
const obstaclePool: Array<{x: number, type: string}> = []for (let i = 0; i < 10; i++) {obstaclePool.push({ x: -100, type: 'cactus' })}
5.2 节流渲染
对分数更新等非关键操作进行节流:
let lastScoreUpdate = 0function updateScore() {const now = Date.now()if (now - lastScoreUpdate > 100) { // 每100ms更新一次gameState.score += 1lastScoreUpdate = now}}
5.3 Web Workers 处理复杂计算
将碰撞检测等计算密集型任务移至 Web Worker:
// worker.tsself.onmessage = function(e) {const { dino, obstacles } = e.dataconst collision = obstacles.some(obs => checkCollision(dino, obs))self.postMessage(collision)}
六、完整实现代码
6.1 主组件整合
<template><div class="game-container"><GameCanvas ref="canvasComponent" /><div class="controls"><button @click="startGame">Start</button><button @click="jump" :disabled="!gameState.isRunning">Jump</button></div></div></template><script setup lang="ts">import { ref } from 'vue'import GameCanvas from './components/GameCanvas.vue'import { useGame } from './composables/useGame'const canvasComponent = ref()const { gameState, dino } = useGame()function jump() {// 通过ref调用子组件方法canvasComponent.value?.jump()}function startGame() {canvasComponent.value?.startGame()}</script>
6.2 完整游戏循环
在 GameCanvas.vue 中整合所有逻辑:
// 省略部分重复代码...onMounted(() => {const ctx = canvasRef.value!.getContext('2d')!// 键盘事件监听window.addEventListener('keydown', (e) => {if (e.code === 'Space') jump()})// 初始化游戏循环return () => {cancelAnimationFrame(animationId)}})// 暴露方法给父组件defineExpose({jump,startGame})
七、扩展功能建议
难度递增系统:随分数提高增加游戏速度
function updateDifficulty() {if (gameState.score % 500 === 0) {gameState.speed += 0.5}}
音效系统:添加跳跃和碰撞音效
function playSound(type: 'jump'|'hit') {const audio = new Audio(type === 'jump' ? '/jump.mp3' : '/hit.mp3')audio.play()}
响应式设计:适配不同屏幕尺寸
.game-container {max-width: 100%;aspect-ratio: 800/300;margin: 0 auto;}
八、总结与最佳实践
- 状态管理:使用 Vue 的响应式系统管理游戏状态,避免直接操作 DOM
- 性能监控:通过
performance.now()测量帧率,确保 60fps - 模块化设计:将游戏逻辑拆分为独立模块,便于维护和测试
- TypeScript 优势:利用类型系统减少运行时错误
完整实现可参考 GitHub 仓库:vue-dino-game-demo,包含详细注释和部署配置。此实现不仅复现了 Chrome 小恐龙游戏的核心玩法,还通过 Vue 的响应式特性实现了更易维护的代码结构,适合作为学习 Vue3 和游戏开发的实践项目。

发表评论
登录后可评论,请前往 登录 或 注册