Files
test1/.docs/tasks/task-1ab-game-foundation.md

227 lines
7.1 KiB
Markdown
Raw 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.
# Task 1A+1B: 游戏基础框架搭建
## 任务概要
在现有 Next.js 项目中,安装 Phaser.js改造主页封面创建游戏页面实现 Phaser 游戏框架挂载 + S型地图渲染。
## 执行步骤
### Step 1: 安装 Phaser.js
```bash
pnpm add phaser
```
### Step 2: 改造 `app/page.tsx`(主页/游戏封面)
完全重写主页,展示游戏封面。注意:**必须覆盖模板原有 UI不保留任何模板样式**。
设计要求(参考 `design-system/大厂保卫战/MASTER.md`
- 背景色:`#0F0F23`(深夜宇宙蓝)
- 标题字体Press Start 2P像素字体颜色 `#A78BFA`(霓虹紫)
- 副标题VT323 字体
- 主色 CTA 按钮(进入游戏):`#F43F5E` 红色
- 效果CRT 扫描线叠加层(`::before` 伪元素repeating-linear-gradient 细黑线)
- 霓虹发光效果:标题 `text-shadow` 双层发光
- 装饰性文字:背景中随机排列大厂黑话(低透明度)
主页内容结构:
```
[全屏黑色背景]
[CRT扫描线叠加层]
[装饰黑话背景文字]
[中央卡片]
标题:大厂保卫战
副标题:最后的打工人
英文副标THE LAST GRINDER
描述保住KPI战胜空降VP
[开始游戏] 按钮 → 跳转 /game
[底部版权:像素风格]
```
### Step 3: 创建 `app/game/page.tsx`(游戏页面)
```typescript
'use client'
import { useEffect, useRef } from 'react'
export default function GamePage() {
const gameRef = useRef<any>(null)
useEffect(() => {
let game: any = null
const initGame = async () => {
const Phaser = (await import('phaser')).default
const { createGameConfig } = await import('@/game/config')
if (gameRef.current && !game) {
game = new Phaser.Game(createGameConfig('game-container'))
}
}
initGame()
return () => {
game?.destroy(true)
}
}, [])
return (
<div className="w-full h-screen bg-[#0F0F23] overflow-hidden">
<div id="game-container" className="w-full h-full" />
</div>
)
}
```
### Step 4: 创建游戏核心文件
**`game/constants.ts`**(游戏常量):
```typescript
export const MAP_COLS = 16
export const MAP_ROWS = 12
export const TILE_SIZE = 80 // 每格80px总计 1280x960Phaser会缩放
export const GAME_WIDTH = 1280
export const GAME_HEIGHT = 720
// S型路径格子坐标
export const PATH_WAYPOINTS = [
{ x: 0, y: 2 },
{ x: 11, y: 2 },
{ x: 11, y: 9 },
{ x: 15, y: 9 },
]
// 游戏初始数值
export const INITIAL_HC = 200
export const INITIAL_KPI = 100
export const STAMINA_MAX = 100
export const STAMINA_REGEN = 5 // 每秒恢复量
export const COFFEE_COST = 10 // 瑞幸咖啡 HC 成本
// 颜色常量
export const COLOR_PATH = 0x3d2b1f
export const COLOR_BUILDABLE = 0x1e3a5f
export const COLOR_HOVER = 0x2d5a8e
export const COLOR_BORDER = 0x0a1628
```
**`game/config.ts`**Phaser 配置):
```typescript
import Phaser from 'phaser'
import { GAME_WIDTH, GAME_HEIGHT } from './constants'
export function createGameConfig(containerId: string): Phaser.Types.Core.GameConfig {
return {
type: Phaser.AUTO,
width: GAME_WIDTH,
height: GAME_HEIGHT,
parent: containerId,
backgroundColor: '#0a1628',
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
},
scene: [], // 在 GameScene 中动态加载
physics: {
default: 'arcade',
arcade: { debug: false }
}
}
}
```
**`game/GameScene.ts`**(主游戏场景):
核心地图渲染逻辑:
- 创建 16x12 格子网格
- 根据 PATH_WAYPOINTS 计算路径格子集合(路径上的每个格子都是路径格)
- 路径格子:深褐色(`0x3d2b1f`
- 可建塔格子:深蓝色(`0x1e3a5f`
- 格子间有1px间距
- 鼠标悬停非路径格子时高亮
- 点击非路径格子触发建塔逻辑Phase 1先只做UI提示
- 添加地图装饰性文字("面试间"、"财务室"
- 场景中显示:顶部 HUD 区域KPI进度条 + HC数值
地图路径计算方式(不能只用 4 个坐标点,需要填充中间格子):
```typescript
// 将折线坐标展开为完整路径格子集合
function buildPathTiles(waypoints): Set<string> {
const tiles = new Set<string>()
for (let i = 0; i < waypoints.length - 1; i++) {
const from = waypoints[i]
const to = waypoints[i + 1]
// 水平或垂直连线
if (from.x === to.x) {
for (let y = Math.min(from.y, to.y); y <= Math.max(from.y, to.y); y++) {
tiles.add(`${from.x},${y}`)
}
} else {
for (let x = Math.min(from.x, to.x); x <= Math.max(from.x, to.x); x++) {
tiles.add(`${x},${from.y}`)
}
}
}
return tiles
}
```
HUD 设计Phaser Text/Graphics 实现,而非 HTML 层):
- 顶部黑色半透明条全宽高度60px
- 左:游戏标题 "大厂保卫战" Press Start 2P小字号
-KPI 进度条宽300px绿→黄→红渐变
-HC 数值 "HC: 200"
**`game/GameManager.ts`**(游戏状态管理器):
```typescript
export class GameManager {
private static instance: GameManager
public hc: number = INITIAL_HC
public kpi: number = INITIAL_KPI
public currentWave: number = 0
public gameState: 'idle' | 'playing' | 'paused' | 'victory' | 'defeat' = 'idle'
static getInstance(): GameManager { ... }
spendHC(amount: number): boolean { ... } // 扣除HC不足返回false
addHC(amount: number): void { ... }
reduceKPI(amount: number): void { ... } // 减少KPI归零触发失败
// 事件系统(用于 Scene 与 UI 通信)
onHCChange: ((hc: number) => void)[] = []
onKPIChange: ((kpi: number) => void)[] = []
onGameOver: (() => void)[] = []
onVictory: (() => void)[] = []
}
```
### Step 5: 更新 `app/layout.tsx`
在 layout 中引入 Google FontsPress Start 2P + VT323
```tsx
// 在 <head> 中添加
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323:wght@400&display=swap" rel="stylesheet" />
```
### Step 6: 更新 `app/globals.css`
添加游戏全局样式:
- CSS 变量(颜色 token
- CRT 扫描线效果(供主页使用)
- 霓虹发光 keyframes 动画
- 像素字体 class
## 验收标准
1. `pnpm dev` 启动成功,无编译错误
2. 访问 `/`:显示游戏封面,有进入游戏按钮,霓虹效果正常
3. 点击进入游戏跳转至 `/game`
4. 访问 `/game`:渲染 16x12 格子地图S型路径可见深褐色路径深蓝色可建区域
5. 鼠标悬停可建塔格子时高亮
6. 顶部 HUD 显示 KPI 进度条和 HC 数值
## 相关上下文
- 设计系统:`design-system/大厂保卫战/MASTER.md`
- PRD`.docs/prd-tower-defense-game.md`
- 计划文档:`.docs/plans/2026-03-21-tower-defense-game.md`
- 开发规范:`@rules/dev-best-practices.md`**必须先阅读**
完成开发后,按 `@rules/atomic-commit.md` 规范,将变更按逻辑模块分组提交(禁止 `git add .`),确保所有文件均已 commit 后再结束任务。