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

7.1 KiB
Raw Blame History

Task 1A+1B: 游戏基础框架搭建

任务概要

在现有 Next.js 项目中,安装 Phaser.js改造主页封面创建游戏页面实现 Phaser 游戏框架挂载 + S型地图渲染。

执行步骤

Step 1: 安装 Phaser.js

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(游戏页面)

'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(游戏常量):

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.tsPhaser 配置):

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 个坐标点,需要填充中间格子):

// 将折线坐标展开为完整路径格子集合
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(游戏状态管理器):

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

// 在 <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 后再结束任务。