Files
CodeLikeDemo/03-技术架构关键路径.md
2026-03-27 18:21:29 +08:00

133 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🏗️ 《Project Vibe: Ship It!》技术架构与关键路径文档
## 一、 技术栈选型 (Tech Stack)
*强烈建议在 Prompt 开头向 AI 明确这些技术栈,防止它自由发挥引入不兼容的库。*
* **前端框架:** React 18 + Vite (纯前端单页应用,无需复杂后端)。
* **开发语言:** TypeScript (*极其重要!严格的类型定义是 VibeCoding 不翻车的绝对保障*)。
* **状态管理:** **Zustand** (*极其推荐!比 Redux 轻量,比 Context 性能好,非常适合做游戏的状态机更新*)。
* **样式方案:** Tailwind CSS (*方便 AI 快速生成好看的 UI 卡片*)。
* **拖拽与动画:** `framer-motion` (处理卡牌平滑移动和翻转动画) + 原生 Pointer Events (处理自由桌面坐标 x,y**不要用**基于网格的 `dnd-kit`,因为我们要的是类似操作系统的无网格自由堆叠)。
---
## 二、 核心数据模型 (Data Models - TypeScript Interfaces)
*这是整个系统的灵魂。第一步请先让 AI 生成并完善这些类型定义文件 `types.ts`。有了它AI 后续写的逻辑就不会乱。*
```typescript
// 1. 卡牌基础模型
export interface Card {
id: string; // 唯一实例ID (uuid)
templateId: string; // 对应配置表中的模板ID (如 'dirty_code', 'developer')
name: string;
type: 'resource' | 'actor' | 'module' | 'crisis' | 'warning';
tags: string[]; // 用于危机判定 (如 ['need_test', 'human'])
position: { x: number; y: number }; // 桌面绝对坐标
status: 'idle' | 'dragging' | 'processing' | 'locked';
processEndTime?: number; // 如果在合成中/危机倒计时中,记录结束的绝对时间戳
}
// 2. 合成配方模型 (静态配置)
export interface Recipe {
id: string;
inputs: string[]; // 需要堆叠在一起的 templateId 列表
timeMs: number; // 合成所需时间 (毫秒)
outputs: Array<{ templateId: string; chance: number }>; // 产物及概率
}
// 3. 全局状态模型 (Zustand Store)
export interface GameState {
// --- 资源状态 ---
funds: number; // 项目资金
sprintTimeRemaining: number; // 当前 Sprint 倒计时
sprintNumber: number; // 当前处于第几个 Sprint
// --- 实体状态 ---
cards: Card[]; // 桌面上的所有卡牌
// --- 核心方法 (Actions) ---
moveCard: (id: string, x: number, y: number) => void;
checkCraftingOverlap: (cardId: string) => void; // 碰撞检测核心逻辑
tick: () => void; // 游戏主循环 (Game Loop),每帧/每秒更新状态
triggerCrisis: (crisisData: any) => void; // LLM 返回后触发危机
}
```
---
## 三、 系统架构设计图 (单向数据流)
游戏的架构必须是**数据驱动**的。UI 层只负责渲染 `cards` 数组和捕获鼠标事件。
```text
[ 玩家操作 (拖拽/松开) ]
[ 操作意图 (Pointer Events) ] ──发起请求──> [ LLM 危机生成器 (异步 Hook) ]
│ │ (返回JSON/超时Fallback)
▼ ▼
[ 碰撞检测算法 (Overlap Check) ] [ 触发预警卡/危机卡生成 ]
│ │
▼ (匹配 Recipe) │
[ 状态机引擎 (Zustand Store) ] <─────────────────┘
├─ 更新卡牌坐标 (x, y)
├─ 锁定卡牌状态 (status: 'processing')
└─ 结算资金/时间 (funds, sprintTime)
▼ (响应式数据)
[ React 渲染层 (UI Components) ]
├─ <DesktopBoard /> (渲染桌面)
├─ <CardItem /> (渲染实体卡、倒计时进度条)
└─ <HudPanel /> (渲染顶部资金与时间)
```
---
## 四、 VibeCoding 关键开发路径 (Critical Paths)
千万不要让 AI “一口气写一个游戏”。请严格按照以下 4 个阶段向 AI 提需求 (Prompting),每完成一个阶段就运行测试,确保无误后再进入下一阶段。
### 🚩 Path 1: 静态桌面与全局状态 (The Dumb Board)
* **目标:** 能在屏幕上看到几张卡牌,资金数值能够显示。
* **AI 任务:**
1. 初始化 Vite + React + TS 项目,安装 Zustand 和 Tailwind。
2. 创建 `types.ts`(使用上面提供的模型)。
3. 创建 `useGameStore.ts` (Zustand),初始化几个假数据卡牌 (Mock Data)。
4. 编写 `<DesktopBoard>``<CardItem>` 组件。卡牌通过绝对定位 `absolute` 渲染在对应的 `x, y` 坐标上。
### 🚩 Path 2: 自由拖拽与物理碰撞 (Drag & Collision)
* **目标:** 卡牌可以被鼠标拖拽,松开鼠标时,能判断两张卡是否重叠。
* **AI 任务:**
1.`<CardItem>` 添加 `onPointerDown`, `onPointerMove`, `onPointerUp` 事件。
2. 拖拽时,调用 Store 的 `moveCard` 更新坐标。
3. **核心算法要求:** 松开鼠标时,写一个 `checkOverlap(cardA, cardB)` 函数(通过判断两个矩形的边界 bounding box 是否相交)。如果重叠率超过 50%,则判定为“堆叠成功”。
### 🚩 Path 3: 核心主循环与合成引擎 (Game Loop & Crafting)
* **目标:** 两张配方卡重叠 -> 出现进度条 -> 读条结束产出新卡。
* **AI 任务:**
1. 创建一个静态的 `recipes.json`(包含 2-3 个基础配方,如 开发+脏代码=模块)。
2. 在 Store 中创建一个 `requestAnimationFrame``setInterval(..., 100)` 驱动的 `tick()` 函数(这就是游戏的心跳)。
3. **合成逻辑:** 当卡牌重叠,匹配 JSON 成功后,将这两张卡的 status 改为 `processing`,并设置 `processEndTime = Date.now() + timeMs`
4. **心跳检测:** `tick()` 函数不断检查当前时间是否超过 `processEndTime`。如果超过,删除旧卡,在原坐标 `push` 新产物卡。
### 🚩 Path 4: LLM 异步危机总线 (The AI Director)
* **目标:** 每隔一段时间,根据桌面状态调用大模型,生成危机并处理延迟。
* **AI 任务:**
1. 编写一个独立的服务 `llmService.ts`
2. 编写 `useCrisisDirector` 自定义 Hook。内部设置一个定时器如每 30 秒触发一次)。
3. **时序逻辑编写:**
* 触发时:立刻在 Store 中推入一张 status 为 `processing` 的【预警卡】。
* 发起 `fetch` 请求调用 OpenAI/DeepSeek API附带当前桌面状态。
* 使用 `Promise.race` 实现 5 秒超时控制。
* 请求 Resolve (或超时 Fallback) 时将那张【预警卡】的数据替换为真实的【危机卡】JSON 数据,并开始倒计时。
---
## 💡 给开发者的 VibeCoding 避坑指南 (Tips)
1. 这是一个自由摆放的桌面,没有列表,只需要纯粹的 x,y 坐标更新和基于宽高的 AABB (Axis-Aligned Bounding Box) 碰撞检测。”
2. 在 Zustand store 里统一管理所有状态。**不要**在 `<CardItem>` 组件内部用 `useState` 去管理倒计时!所有的进度条渲染都应该基于 `processEndTime - Date.now()` 实时计算得出,这样即使组件卸载/重新渲染,进度也不会丢失。
3. **Mock First** 在写 LLM 接入时,用 `setTimeout` 模拟一个假的大模型返回。把游戏跑通了,最后再填入真实的 API Key 和 `fetch` 请求。