logo

从零开始:Vue 实现 Chrome 小恐龙游戏

作者:十万个为什么2025.10.11 17:06浏览量:0

简介:本文详解如何使用 Vue 3 实现 Chrome 浏览器离线时的小恐龙游戏,覆盖物理引擎、碰撞检测、动画控制等核心模块,提供完整代码实现与性能优化方案。

一、项目概述与技术选型

Chrome 小恐龙游戏是浏览器在离线状态下显示的经典 2D 跳跃游戏,其核心机制包含:

  • 基于 Canvas 的 2D 图形渲染
  • 物理引擎驱动的角色运动
  • 碰撞检测与障碍物生成
  • 分数计算与游戏状态管理

选择 Vue 3 实现的优势在于:

  1. 响应式系统:利用 refreactive 管理游戏状态
  2. 组合式 API:通过 setup() 函数组织逻辑代码
  3. 生命周期钩子:精准控制游戏初始化与销毁
  4. 组件化开发:将游戏元素拆分为可复用组件

项目结构建议:

  1. src/
  2. ├── components/
  3. ├── GameCanvas.vue # 主画布组件
  4. ├── Dino.vue # 恐龙角色组件
  5. ├── Obstacle.vue # 障碍物组件
  6. └── Score.vue # 分数显示组件
  7. ├── composables/
  8. ├── useGameLoop.ts # 游戏循环逻辑
  9. └── usePhysics.ts # 物理引擎
  10. └── App.vue # 主入口组件

二、核心实现步骤

1. 画布初始化与基础设置

GameCanvas.vue 中创建 Canvas 元素并设置基础参数:

  1. <template>
  2. <canvas
  3. ref="canvasRef"
  4. :width="width"
  5. :height="height"
  6. @click="handleJump"
  7. ></canvas>
  8. </template>
  9. <script setup>
  10. import { ref, onMounted } from 'vue'
  11. const canvasRef = ref(null)
  12. const width = 800
  13. const height = 300
  14. let ctx = null
  15. onMounted(() => {
  16. ctx = canvasRef.value.getContext('2d')
  17. // 初始化游戏背景
  18. ctx.fillStyle = '#f7f7f7'
  19. ctx.fillRect(0, 0, width, height)
  20. })
  21. </script>

2. 物理引擎实现

创建 usePhysics.ts 组合式函数处理重力与跳跃:

  1. import { ref } from 'vue'
  2. export function usePhysics() {
  3. const gravity = 0.6
  4. const jumpForce = -10
  5. const velocity = ref(0)
  6. const isJumping = ref(false)
  7. const applyGravity = () => {
  8. if (velocity.value < 10) { // 终端速度
  9. velocity.value += gravity
  10. }
  11. }
  12. const jump = () => {
  13. if (!isJumping.value) {
  14. velocity.value = jumpForce
  15. isJumping.value = true
  16. }
  17. }
  18. return { velocity, isJumping, applyGravity, jump }
  19. }

3. 恐龙角色动画

Dino.vue 中实现角色动画:

  1. <script setup>
  2. import { ref, watch } from 'vue'
  3. import { usePhysics } from '@/composables/usePhysics'
  4. const { velocity, isJumping, applyGravity, jump } = usePhysics()
  5. const position = ref(0)
  6. const frame = ref(0)
  7. const frames = [0, 1, 2] // 奔跑动画帧
  8. // 更新角色位置
  9. watch(velocity, (newVal) => {
  10. position.value += newVal
  11. if (position.value >= 0) {
  12. isJumping.value = false
  13. }
  14. })
  15. // 动画循环
  16. const animate = () => {
  17. frame.value = (frame.value + 0.2) % frames.length
  18. requestAnimationFrame(animate)
  19. }
  20. defineExpose({ jump })
  21. </script>

4. 障碍物生成与碰撞检测

实现障碍物类与碰撞检测逻辑:

  1. // obstacle.ts
  2. export class Obstacle {
  3. x: number
  4. width: number
  5. height: number
  6. type: 'cactus' | 'bird'
  7. constructor(type: 'cactus' | 'bird' = 'cactus') {
  8. this.type = type
  9. this.width = type === 'cactus' ? 30 : 50
  10. this.height = type === 'cactus' ? 50 : 30
  11. this.x = 800 // 初始位置在画布右侧
  12. }
  13. update(speed: number) {
  14. this.x -= speed
  15. }
  16. draw(ctx: CanvasRenderingContext2D) {
  17. ctx.fillStyle = '#333'
  18. if (this.type === 'cactus') {
  19. ctx.fillRect(this.x, 220, this.width, this.height)
  20. } else {
  21. // 鸟类障碍物绘制
  22. ctx.fillRect(this.x, 180, this.width, this.height)
  23. }
  24. }
  25. }
  26. // 碰撞检测函数
  27. export function checkCollision(dinoY: number, obstacle: Obstacle) {
  28. const dinoHeight = 60
  29. const dinoWidth = 40
  30. return (
  31. dinoX + dinoWidth > obstacle.x &&
  32. dinoX < obstacle.x + obstacle.width &&
  33. (dinoY + dinoHeight > 220 || // 地面障碍物
  34. dinoY < 180 + obstacle.height) // 空中障碍物
  35. )
  36. }

5. 游戏主循环实现

创建 useGameLoop.ts 管理游戏状态:

  1. import { ref, onUnmounted } from 'vue'
  2. import { Obstacle } from './obstacle'
  3. export function useGameLoop() {
  4. const obstacles = ref<Obstacle[]>([])
  5. const score = ref(0)
  6. const gameSpeed = ref(5)
  7. const isRunning = ref(false)
  8. let animationId: number
  9. const generateObstacle = () => {
  10. const types = ['cactus', 'cactus', 'bird'] as const
  11. const type = types[Math.floor(Math.random() * types.length)]
  12. obstacles.value.push(new Obstacle(type))
  13. }
  14. const update = () => {
  15. obstacles.value.forEach(obs => {
  16. obs.update(gameSpeed.value)
  17. if (obs.x < -obs.width) {
  18. obstacles.value = obstacles.value.filter(o => o !== obs)
  19. score.value++
  20. }
  21. })
  22. // 每2秒生成一个新障碍物
  23. if (Math.random() < 0.005) {
  24. generateObstacle()
  25. }
  26. }
  27. const startLoop = () => {
  28. isRunning.value = true
  29. const loop = () => {
  30. update()
  31. animationId = requestAnimationFrame(loop)
  32. }
  33. loop()
  34. }
  35. onUnmounted(() => {
  36. cancelAnimationFrame(animationId)
  37. })
  38. return { score, gameSpeed, isRunning, startLoop }
  39. }

三、性能优化方案

  1. 对象池技术:复用障碍物对象减少内存分配
    ```typescript
    const obstaclePool: Obstacle[] = []
    for (let i = 0; i < 10; i++) {
    obstaclePool.push(new Obstacle())
    }

const getObstacle = (type: ‘cactus’ | ‘bird’) => {
const obstacle = obstaclePool.find(o => !o.inUse)
if (obstacle) {
obstacle.type = type
obstacle.inUse = true
return obstacle
}
return new Obstacle(type)
}

  1. 2. **分层渲染**:将静态背景与动态元素分离
  2. ```vue
  3. <template>
  4. <canvas ref="bgCanvas" :width="width" :height="height"></canvas>
  5. <canvas ref="gameCanvas" :width="width" :height="height"></canvas>
  6. </template>
  1. 节流处理:限制游戏状态更新频率
    1. let lastUpdate = 0
    2. const throttleUpdate = (callback: () => void, limit = 16) => {
    3. const now = Date.now()
    4. if (now - lastUpdate >= limit) {
    5. callback()
    6. lastUpdate = now
    7. }
    8. }

四、完整游戏集成

App.vue 中整合所有组件:

  1. <template>
  2. <div class="game-container">
  3. <GameCanvas
  4. ref="gameCanvas"
  5. @game-over="handleGameOver"
  6. />
  7. <Score :value="score" />
  8. <button @click="startGame" :disabled="isRunning">
  9. {{ isRunning ? '游戏中' : '开始游戏' }}
  10. </button>
  11. </div>
  12. </template>
  13. <script setup>
  14. import { ref } from 'vue'
  15. import GameCanvas from './components/GameCanvas.vue'
  16. import Score from './components/Score.vue'
  17. import { useGameLoop } from './composables/useGameLoop'
  18. const { score, isRunning, startLoop } = useGameLoop()
  19. const gameCanvas = ref(null)
  20. const startGame = () => {
  21. if (gameCanvas.value) {
  22. gameCanvas.value.reset()
  23. }
  24. startLoop()
  25. }
  26. const handleGameOver = () => {
  27. isRunning.value = false
  28. }
  29. </script>

五、扩展功能建议

  1. 移动端适配:添加触摸事件支持

    1. const handleTouchStart = (e: TouchEvent) => {
    2. if (e.touches.length === 1) {
    3. const touch = e.touches[0]
    4. if (touch.clientY < height * 0.7) {
    5. // 触摸上半部分执行跳跃
    6. jump()
    7. }
    8. }
    9. }
  2. 难度递增系统:根据分数动态调整游戏速度

    1. watch(score, (newScore) => {
    2. if (newScore % 10 === 0) {
    3. gameSpeed.value += 0.5
    4. }
    5. })
  3. 音效系统:集成 Web Audio API

    1. const playJumpSound = () => {
    2. const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)()
    3. const oscillator = audioCtx.createOscillator()
    4. const gainNode = audioCtx.createGain()
    5. oscillator.connect(gainNode)
    6. gainNode.connect(audioCtx.destination)
    7. oscillator.type = 'square'
    8. oscillator.frequency.setValueAtTime(440, audioCtx.currentTime)
    9. gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime)
    10. gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.3)
    11. oscillator.start()
    12. oscillator.stop(audioCtx.currentTime + 0.3)
    13. }

该实现完整覆盖了 Chrome 小恐龙游戏的核心机制,通过 Vue 3 的组合式 API 实现了清晰的状态管理。实际开发中可根据需求进一步扩展游戏模式、添加特效系统或实现多人对战功能。完整代码仓库建议包含 TypeScript 类型定义、单元测试和构建配置文件,确保项目的可维护性。

相关文章推荐

发表评论