Skip to content

Latest commit

 

History

History
874 lines (713 loc) · 18.9 KB

File metadata and controls

874 lines (713 loc) · 18.9 KB

CubeCity 新手开发指南 🚀

欢迎加入 CubeCity 开发团队!这是一款基于 Three.js 和 Vue 3 的 2.5D 卡通城市模拟经营游戏。本指南将帮助你快速上手项目开发,成为一名优秀的 CubeCity 开发者。


📋 目录

  1. 项目概述
  2. 技术栈介绍
  3. 开发环境搭建
  4. 项目结构解析
  5. 核心概念理解
  6. 开发规范
  7. 常见开发任务
  8. 调试与测试
  9. 部署与发布
  10. 常见问题解答

🎯 项目概述

项目简介

CubeCity 是一款 2.5D 卡通风格的城市建设模拟游戏,玩家通过放置、管理各类建筑,经营并扩展属于自己的城市。

核心特性

  • 🏗️ 3D 城市建设:基于 Three.js 的 3D 场景渲染
  • 🎮 模拟经营:RCI & ESG 多维建筑体系
  • 💾 本地存储:自动保存游戏进度
  • 📱 响应式设计:支持桌面端和移动端
  • 🌍 国际化:支持中英文切换
  • 🎨 现代化 UI:基于 Vue 3 + Tailwind CSS

开发目标

  • 提供流畅的 3D 城市建设体验
  • 实现复杂的建筑相互作用系统
  • 构建可扩展的游戏架构
  • 保持代码的可维护性和可读性

🛠️ 技术栈介绍

核心技术

技术 版本 用途
Vue 3 3.x 前端框架,负责 UI 层
Three.js 0.172.0 3D 图形渲染引擎
Vite 5.4.0 构建工具和开发服务器
Pinia 3.0.3 状态管理
Tailwind CSS 3.4.9 CSS 框架

辅助技术

技术 用途
GSAP 动画库,用于 UI 和 3D 动画
Cannon.js 物理引擎
Mitt 事件总线,用于组件通信
Vue I18n 国际化支持
Tweakpane 调试面板

开发工具

工具 用途
ESLint 代码质量检查
Prettier 代码格式化
Playwright 端到端测试
Husky Git hooks
Commitlint 提交信息规范

🚀 开发环境搭建

1. 环境要求

  • Node.js: >= 18.0.0
  • 包管理器: pnpm (推荐) 或 npm
  • 编辑器: VS Code (推荐)
  • 浏览器: Chrome/Edge (支持 WebGL)

2. 克隆项目

git clone https://github.com/your-username/CubeCity.git
cd CubeCity

3. 安装依赖

# 使用 pnpm (推荐)
pnpm install

# 或使用 npm
npm install

4. 启动开发服务器

pnpm dev
#
npm run dev

访问 http://localhost:5173 查看项目。

5. 其他常用命令

# 构建生产版本
pnpm build

# 预览生产版本
pnpm preview

# 代码检查
pnpm lint

# 代码格式化
pnpm lint:fix

# 运行测试
pnpm test:chrome

📁 项目结构解析

CubeCity/
├── docs/                    # 项目文档
│   ├── PRD.md              # 产品需求文档
│   ├── TD.md               # 技术设计文档
│   └── 新手指南.md         # 玩家指南
├── public/                  # 静态资源
│   ├── models/             # 3D 模型文件
│   ├── textures/           # 纹理文件
│   └── fonts/              # 字体文件
├── src/                     # 源代码
│   ├── components/         # Vue 组件
│   ├── js/                 # Three.js 相关代码
│   │   ├── components/     # 3D 组件
│   │   ├── tools/          # 工具类
│   │   ├── utils/          # 工具函数
│   │   └── world/          # 世界管理
│   ├── shaders/            # GLSL 着色器
│   ├── stores/             # Pinia 状态管理
│   ├── assets/             # 资源文件
│   └── css/                # 样式文件
├── tests/                   # 测试文件
├── package.json            # 项目配置
└── vite.config.js          # Vite 配置

核心文件说明

Vue 层 (src/components/)

  • App.vue: 主应用组件
  • GameCanvas.vue: 游戏画布组件
  • BuildingSidebar.vue: 建筑选择面板
  • BuildingDetail.vue: 建筑详情面板

Three.js 层 (src/js/)

  • experience.js: 核心体验类,单例模式
  • camera.js: 相机管理
  • renderer.js: 渲染器管理
  • world.js: 世界管理
  • components/: 3D 组件
    • tiles/: 地皮系统
    • buildings/: 建筑系统
    • effects/: 特效系统

状态管理 (src/stores/)

  • useGameState.js: 游戏状态管理

🧠 核心概念理解

1. 架构设计模式

单例模式 (Experience)

// src/js/experience.js
export default class Experience {
  constructor() {
    if (Experience.instance) {
      return Experience.instance
    }
    Experience.instance = this

    // 初始化核心组件
    this.scene = new THREE.Scene()
    this.camera = new Camera()
    this.renderer = new Renderer()
    // ...
  }
}

组件化设计

  • Vue 组件: 负责 UI 交互和状态展示
  • Three.js 组件: 负责 3D 场景渲染和逻辑
  • 状态管理: Pinia 统一管理游戏状态

2. 通信机制

Vue ↔ Three.js 通信

// 使用 mitt 事件总线
import emitter from './utils/event-bus.js'

// Vue 组件发送事件
emitter.emit('ui:building-selected', { type: 'house' })

// Three.js 组件监听事件
emitter.on('ui:building-selected', (data) => {
  // 处理建筑选择逻辑
})

状态同步

// 使用 Pinia 进行状态管理
import { useGameState } from '@/stores/useGameState.js'

const gameState = useGameState()
gameState.setMode('build')
gameState.setSelectedBuilding({ type: 'house', level: 1 })

3. 资源管理

资源加载

// src/js/sources.js - 定义资源
export default [
  {
    name: 'house',
    type: 'gltfModel',
    path: 'models/house_level1.glb'
  }
]

// 使用资源
const houseModel = this.experience.resources.items.house

着色器管理

// src/shaders/ - GLSL 着色器
import fragmentShader from '@/shaders/fragment.glsl'
import vertexShader from '@/shaders/vertex.glsl'

const material = new THREE.ShaderMaterial({
  vertexShader,
  fragmentShader,
  uniforms: {
    time: { value: 0 }
  }
})

📝 开发规范

1. 代码风格

JavaScript/TypeScript

  • 使用 ES6+ 语法
  • 类名使用大驼峰命名
  • 方法名使用小驼峰命名
  • 常量使用大写下划线
  • 必须添加中文注释

Vue 组件

  • 组件名使用大驼峰命名
  • 文件名与组件名一致
  • 使用 Composition API
  • 样式使用 Tailwind CSS

Three.js 组件

export default class YourComponent {
  constructor() {
    // 获取 Experience 单例实例
    this.experience = new Experience()

    // 获取需要的核心组件
    this.scene = this.experience.scene
    this.resources = this.experience.resources
    this.debug = this.experience.debug

    // 初始化组件
    this.init()
  }

  init() {
    // 组件初始化逻辑
  }

  update() {
    // 更新逻辑
  }

  resize() {
    // 响应式调整
  }
}

2. 文件组织

组件文件结构

src/js/components/
├── tiles/           # 地皮相关组件
├── buildings/       # 建筑相关组件
├── effects/         # 特效组件
└── ui/             # UI 相关组件

工具文件结构

src/js/utils/
├── debug.js        # 调试工具
├── event-bus.js    # 事件总线
├── resources.js    # 资源管理
└── sizes.js        # 尺寸管理

3. 命名规范

文件命名

  • 组件文件: PascalCase.js
  • 工具文件: kebab-case.js
  • 着色器文件: kebab-case.glsl

变量命名

// 常量
const BUILDING_TYPES = ['house', 'factory', 'shop']

// 类名
class BuildingFactory {}

// 方法名
function createBuilding() {}

// 私有属性
this._privateProperty = value

4. 注释规范

中文注释要求

/**
 * 建筑工厂类
 * 负责创建和管理不同类型的建筑实例
 */
export default class BuildingFactory {
  /**
   * 创建建筑实例
   * @param {string} type - 建筑类型
   * @param {number} level - 建筑等级
   * @returns {Building} 建筑实例
   */
  createBuilding(type, level) {
    // 根据类型创建对应建筑
    switch (type) {
      case 'house':
        return new House(level)
      case 'factory':
        return new Factory(level)
      default:
        throw new Error(`未知建筑类型: ${type}`)
    }
  }
}

🔧 常见开发任务

1. 添加新建筑类型

步骤 1: 定义建筑数据

// src/constants/constants.js
export const BUILDING_DATA = {
  newBuilding: {
    name: { zh: '新建筑', en: 'New Building' },
    type: 'newBuilding',
    icon: '🏢',
    category: 'commercial',
    levels: {
      1: {
        displayName: { zh: '基础新建筑', en: 'Basic New Building' },
        cost: 500,
        coinOutput: 25,
        powerUsage: 10,
        pollution: 2,
        upgradeCost: 1000,
        nextLevel: 2,
        visible: true
      }
    }
  }
}

步骤 2: 创建 3D 组件

// src/js/components/buildings/new-building.js
import Building from './building.js'

export default class NewBuilding extends Building {
  constructor(level = 1) {
    super('newBuilding', level)

    // 加载模型
    this.model = this.resources.items.newBuilding
    this.scene.add(this.model)
  }

  // 重写特定方法
  getCoinOutput() {
    return this.buildingData.coinOutput * this.level
  }
}

步骤 3: 添加模型资源

// src/js/sources.js
export default [
  // ... 其他资源
  {
    name: 'newBuilding',
    type: 'gltfModel',
    path: 'models/new-building.glb'
  }
]

步骤 4: 更新 UI 组件

<!-- src/components/BuildingSidebar.vue -->
<template>
  <div class="building-card" @click="selectBuilding('newBuilding')">
    <div class="building-icon">
      🏢
    </div>
    <div class="building-name">
      {{ t('newBuilding.name') }}
    </div>
    <div class="building-cost">
      {{ getBuildingCost('newBuilding') }}
    </div>
  </div>
</template>

2. 实现建筑相互作用

步骤 1: 定义相互作用规则

// src/constants/building-interactions.js
export const BUILDING_INTERACTIONS = {
  house: {
    park: {
      distance: 1,
      effect: 'maxPopulation',
      multiplier: 0.1, // +10%
      description: '相邻公园增加人口容量'
    },
    factory: {
      distance: 1,
      effect: 'maxPopulation',
      multiplier: -0.15, // -15%
      description: '相邻工厂减少人口容量'
    }
  }
}

步骤 2: 实现相互作用逻辑

// src/js/components/buildings/building.js
export default class Building {
  calculateInteractions() {
    const neighbors = this.getNeighborTiles()
    let bonus = 1.0

    neighbors.forEach((tile) => {
      if (tile.building) {
        const interaction = BUILDING_INTERACTIONS[this.type]?.[tile.building.type]
        if (interaction) {
          bonus += interaction.multiplier
        }
      }
    })

    return Math.max(0.5, bonus) // 最低 50% 效率
  }
}

3. 添加新特效

步骤 1: 创建着色器

// src/shaders/effects/new-effect/fragment.glsl
uniform float time;
uniform vec3 color;

varying vec2 vUv;

void main() {
  vec2 uv = vUv;
  float wave = sin(uv.x * 10.0 + time) * 0.5 + 0.5;
  gl_FragColor = vec4(color * wave, 1.0);
}

步骤 2: 创建特效组件

// src/js/components/effects/new-effect.js
import fragmentShader from '@/shaders/effects/new-effect/fragment.glsl'
import vertexShader from '@/shaders/effects/new-effect/vertex.glsl'

export default class NewEffect {
  constructor() {
    this.material = new THREE.ShaderMaterial({
      vertexShader,
      fragmentShader,
      uniforms: {
        time: { value: 0 },
        color: { value: new THREE.Color(0x00FF00) }
      },
      transparent: true
    })
  }

  update() {
    this.material.uniforms.time.value += 0.01
  }
}

4. 添加新 UI 功能

步骤 1: 创建 Vue 组件

<!-- src/components/NewFeature.vue -->
<script setup>
import { ref } from 'vue'

const props = defineProps({
  title: {
    type: String,
    required: true
  }
})
</script>

<template>
  <div class="new-feature">
    <h3>{{ title }}</h3>
    <div class="content">
      <slot />
    </div>
  </div>
</template>

<style scoped>
.new-feature {
  @apply p-4 bg-white rounded-lg shadow-md;
}
</style>

步骤 2: 集成到主应用

<!-- src/App.vue -->
<script setup>
import NewFeature from '@/components/NewFeature.vue'
</script>

<template>
  <div id="app">
    <!-- 现有内容 -->
    <NewFeature title="新功能" />
  </div>
</template>

🐛 调试与测试

1. 调试工具

浏览器开发者工具

  • Console: 查看日志和错误
  • Network: 检查资源加载
  • Performance: 分析性能问题
  • Application: 查看本地存储

Three.js 调试

// 启用调试面板
if (this.debug.active) {
  this.debugFolder = this.debug.ui.addFolder({
    title: '组件名称',
    expanded: true
  })

  this.debugFolder.addBinding(this.object, 'position', {
    label: '位置'
  })
}

Vue DevTools

  • 安装 Vue DevTools 浏览器扩展
  • 查看组件状态和事件
  • 调试 Pinia 状态管理

2. 性能优化

3D 场景优化

// 使用对象池
class ObjectPool {
  constructor(createFn) {
    this.pool = []
    this.createFn = createFn
  }

  get() {
    return this.pool.pop() || this.createFn()
  }

  release(obj) {
    this.pool.push(obj)
  }
}

// 使用 LOD (Level of Detail)
const lod = new THREE.LOD()
lod.addLevel(highDetailMesh, 0)
lod.addLevel(mediumDetailMesh, 50)
lod.addLevel(lowDetailMesh, 100)

内存管理

// 正确释放资源
dispose() {
  // 释放几何体
  this.geometry.dispose()

  // 释放材质
  this.material.dispose()

  // 从场景中移除
  this.scene.remove(this.mesh)

  // 清空纹理
  this.texture.dispose()
}

3. 测试

单元测试

import BuildingCard from '@/components/BuildingCard.vue'
import { mount } from '@vue/test-utils'
// tests/components.test.js
import { describe, expect, it } from 'vitest'

describe('BuildingCard', () => {
  it('显示正确的建筑信息', () => {
    const wrapper = mount(BuildingCard, {
      props: {
        building: {
          name: '住宅',
          cost: 300
        }
      }
    })

    expect(wrapper.text()).toContain('住宅')
    expect(wrapper.text()).toContain('300')
  })
})

端到端测试

// tests/e2e/game.test.js
import { expect, test } from '@playwright/test'

test('建筑放置功能', async ({ page }) => {
  await page.goto('/')

  // 选择建筑
  await page.click('[data-testid="building-house"]')

  // 点击地皮放置
  await page.click('[data-testid="tile-0-0"]')

  // 验证建筑已放置
  await expect(page.locator('[data-testid="building-instance"]')).toBeVisible()
})

🚀 部署与发布

1. 构建生产版本

# 构建
pnpm build

# 预览构建结果
pnpm preview

2. 部署配置

// vite.config.js
export default defineConfig({
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    sourcemap: false,
    minify: 'terser',
    rollupOptions: {
      output: {
        manualChunks: {
          three: ['three'],
          vendor: ['vue', 'pinia']
        }
      }
    }
  }
})

3. 性能优化

  • 启用 Gzip 压缩
  • 使用 CDN 加速
  • 配置缓存策略
  • 优化图片和模型资源

❓ 常见问题解答

Q1: 如何快速理解项目架构?

A: 建议按以下顺序学习:

  1. 阅读 docs/PRD.md 了解产品需求
  2. 查看 docs/TD.md 理解技术设计
  3. src/js/experience.js 开始,理解核心架构
  4. 查看 src/components/App.vue 了解 UI 结构

Q2: 如何添加新的建筑类型?

A: 参考 添加新建筑类型 章节,需要:

  1. constants.js 中定义建筑数据
  2. 创建对应的 3D 组件
  3. 添加模型资源
  4. 更新 UI 组件

Q3: 如何调试 3D 场景问题?

A: 使用以下方法:

  1. 启用调试面板查看对象属性
  2. 使用浏览器开发者工具的 Console
  3. 检查 Three.js 的 WebGL 错误
  4. 使用 console.log 输出关键数据

Q4: 如何处理性能问题?

A: 常见优化方法:

  1. 使用对象池减少内存分配
  2. 实现 LOD 系统
  3. 合理使用纹理压缩
  4. 优化渲染循环

Q5: 如何实现国际化?

A: 使用 Vue I18n:

  1. src/assets/i18n/ 中添加语言文件
  2. 使用 $t() 函数包装文本
  3. 通过 useI18n() 切换语言

Q6: 如何提交代码?

A: 遵循项目规范:

  1. 使用 git add 暂存更改
  2. 使用 git commit 提交,遵循 commitizen 规范
  3. 提交信息格式:feat: 添加新建筑类型 (#issue)

📚 学习资源

官方文档

推荐阅读

社区资源


🤝 贡献指南

开发流程

  1. Fork 项目到个人仓库
  2. 创建功能分支:git checkout -b feature/new-feature
  3. 提交更改:git commit -m "feat: 添加新功能"
  4. 推送到分支:git push origin feature/new-feature
  5. 创建 Pull Request

代码审查

  • 所有代码变更需要经过审查
  • 确保代码符合项目规范
  • 添加必要的测试用例
  • 更新相关文档

问题反馈

  • 使用 GitHub Issues 报告 Bug
  • 提供详细的复现步骤
  • 包含环境信息和错误日志
  • 建议解决方案

📞 联系方式


🎉 结语

恭喜你完成了 CubeCity 新手开发指南的学习!现在你已经具备了开始开发的基础知识。

记住:

  • 🎯 保持学习心态:技术栈在不断发展,持续学习新知识
  • 🤝 团队协作:与团队成员保持良好的沟通和协作
  • 📝 文档优先:及时更新文档,帮助其他开发者
  • 🐛 质量第一:注重代码质量和用户体验
  • 🚀 勇于创新:在现有基础上提出改进建议

祝你在 CubeCity 项目中开发愉快,创造出优秀的作品!🌟


最后更新:2024年12月