12 KiB
12 KiB
任务:多地图 + 难度系统 + 场景化改造
目标
- 主页新增难度选择(低/中/高),点击后带参数跳转游戏
- 实现3张地图,每5波切换一次(地图1→地图2→地图3)
- 每张地图有专属背景图、路径形状、装饰物
- 波次配置根据难度动态调整(怪物数量、速度、Boss频率)
素材已就绪
/public/game-assets/ 目录中已有:
map1-bg.png、map2-bg.png、map3-bg.png— 三张地图背景deco-coffee.png、deco-monitor.png、deco-desk.png— 装饰物
文件改动清单
1. game/data/mapConfigs.ts(新建)
定义3张地图的配置:
export type DifficultyLevel = 'easy' | 'normal' | 'hard'
export interface MapConfig {
id: number
name: string // 地图名称
bgKey: string // Phaser 加载的背景图 key
bgPath: string // 背景图路径
pathColor: number // 路径格颜色
buildColor: number // 可建格颜色
waypoints: readonly { x: number; y: number }[] // 路径折线坐标
decorations: { // 装饰物(固定摆放的非路径格)
key: string
col: number
row: number
}[]
labels: { col: number; row: number; text: string }[]
waveCount: number // 每张地图波次数(固定5)
// 每波配置(相对于 normal 难度的基础配置)
waves: WaveConfigEntry[]
}
export interface WaveConfigEntry {
enemies: {
type: 'FreshGraduate' | 'OldEmployee' | 'TroubleMaker' | 'BossVP'
count: number
interval: number // ms
}[]
}
// 难度倍率
export const DIFFICULTY_MULTIPLIER: Record<DifficultyLevel, {
enemyCount: number // 数量倍率
enemySpeed: number // 速度倍率
bossHp: number // Boss HP 倍率
hcReward: number // HC 奖励倍率(难度高奖励少)
}> = {
easy: { enemyCount: 0.7, enemySpeed: 0.8, bossHp: 0.7, hcReward: 1.2 },
normal: { enemyCount: 1.0, enemySpeed: 1.0, bossHp: 1.0, hcReward: 1.0 },
hard: { enemyCount: 1.4, enemySpeed: 1.3, bossHp: 1.5, hcReward: 0.8 },
}
// 地图1:人力资源部(HR走廊,S型路径)
// 路径:(0,2)→(11,2)→(11,9)→(15,9)
const MAP1: MapConfig = {
id: 1,
name: '人力资源部 · 降本增效',
bgKey: 'map1-bg',
bgPath: '/game-assets/map1-bg.png',
pathColor: 0x3d2b1f,
buildColor: 0x1e3a5f,
waypoints: [
{ x: 0, y: 2 },
{ x: 11, y: 2 },
{ x: 11, y: 9 },
{ x: 15, y: 9 },
],
decorations: [
{ key: 'deco-coffee', col: 3, row: 0 },
{ key: 'deco-monitor', col: 14, row: 1 },
{ key: 'deco-desk', col: 5, row: 5 },
{ key: 'deco-coffee', col: 9, row: 11 },
{ key: 'deco-monitor', col: 1, row: 7 },
{ key: 'deco-desk', col: 13, row: 4 },
],
labels: [
{ col: 0, row: 0, text: '面试间' },
{ col: 14, row: 10, text: '财务室' },
{ col: 6, row: 5, text: 'HR办公区' },
],
waveCount: 5,
waves: [
{ enemies: [{ type: 'FreshGraduate', count: 10, interval: 800 }] },
{ enemies: [{ type: 'FreshGraduate', count: 8, interval: 700 }, { type: 'OldEmployee', count: 2, interval: 2000 }] },
{ enemies: [{ type: 'OldEmployee', count: 4, interval: 1800 }, { type: 'TroubleMaker', count: 3, interval: 1500 }] },
{ enemies: [{ type: 'FreshGraduate', count: 12, interval: 600 }, { type: 'TroubleMaker', count: 3, interval: 1200 }] },
{ enemies: [{ type: 'BossVP', count: 1, interval: 0 }, { type: 'OldEmployee', count: 3, interval: 2000 }, { type: 'FreshGraduate', count: 5, interval: 800 }] },
],
}
// 地图2:技术研发部(Z型路径,更复杂)
// 路径:(0,1)→(8,1)→(8,5)→(3,5)→(3,10)→(15,10)
const MAP2: MapConfig = {
id: 2,
name: '技术研发部 · 996福报',
bgKey: 'map2-bg',
bgPath: '/game-assets/map2-bg.png',
pathColor: 0x1a3a2a,
buildColor: 0x0f2040,
waypoints: [
{ x: 0, y: 1 },
{ x: 8, y: 1 },
{ x: 8, y: 5 },
{ x: 3, y: 5 },
{ x: 3, y: 10 },
{ x: 15, y: 10 },
],
decorations: [
{ key: 'deco-monitor', col: 1, row: 3 },
{ key: 'deco-monitor', col: 10, row: 0 },
{ key: 'deco-coffee', col: 6, row: 7 },
{ key: 'deco-desk', col: 11, row: 3 },
{ key: 'deco-monitor', col: 5, row: 8 },
{ key: 'deco-coffee', col: 14, row: 7 },
{ key: 'deco-desk', col: 0, row: 6 },
{ key: 'deco-monitor', col: 13, row: 5 },
],
labels: [
{ col: 0, row: 0, text: '研发中心' },
{ col: 14, row: 11, text: '服务器机房' },
{ col: 5, row: 3, text: '格子间' },
{ col: 10, row: 8, text: 'Bug池' },
],
waveCount: 5,
waves: [
{ enemies: [{ type: 'FreshGraduate', count: 12, interval: 700 }, { type: 'OldEmployee', count: 2, interval: 2000 }] },
{ enemies: [{ type: 'OldEmployee', count: 5, interval: 1500 }, { type: 'TroubleMaker', count: 3, interval: 1200 }] },
{ enemies: [{ type: 'FreshGraduate', count: 15, interval: 500 }, { type: 'TroubleMaker', count: 4, interval: 1000 }] },
{ enemies: [{ type: 'BossVP', count: 1, interval: 0 }, { type: 'OldEmployee', count: 4, interval: 1500 }] },
{ enemies: [{ type: 'BossVP', count: 1, interval: 0 }, { type: 'FreshGraduate', count: 10, interval: 600 }, { type: 'TroubleMaker', count: 5, interval: 1000 }] },
],
}
// 地图3:高管会议室(W型路径,最难)
// 路径:(0,0)→(5,0)→(5,6)→(10,6)→(10,2)→(15,2) 先上后下再上
const MAP3: MapConfig = {
id: 3,
name: '高管会议室 · 最后决战',
bgKey: 'map3-bg',
bgPath: '/game-assets/map3-bg.png',
pathColor: 0x3d1f2b,
buildColor: 0x2d1a40,
waypoints: [
{ x: 0, y: 0 },
{ x: 5, y: 0 },
{ x: 5, y: 6 },
{ x: 10, y: 6 },
{ x: 10, y: 2 },
{ x: 15, y: 2 },
],
decorations: [
{ key: 'deco-desk', col: 2, row: 2 },
{ key: 'deco-desk', col: 8, row: 0 },
{ key: 'deco-coffee', col: 12, row: 5 },
{ key: 'deco-desk', col: 7, row: 9 },
{ key: 'deco-monitor', col: 2, row: 9 },
{ key: 'deco-coffee', col: 13, row: 9 },
{ key: 'deco-desk', col: 1, row: 4 },
{ key: 'deco-monitor', col: 14, row: 4 },
{ key: 'deco-desk', col: 8, row: 8 },
],
labels: [
{ col: 0, row: 1, text: '董事会' },
{ col: 14, row: 3, text: '董事长室' },
{ col: 7, row: 4, text: '会议室' },
],
waveCount: 5,
waves: [
{ enemies: [{ type: 'OldEmployee', count: 6, interval: 1500 }, { type: 'TroubleMaker', count: 4, interval: 1000 }] },
{ enemies: [{ type: 'BossVP', count: 1, interval: 0 }, { type: 'FreshGraduate', count: 12, interval: 600 }, { type: 'OldEmployee', count: 3, interval: 1500 }] },
{ enemies: [{ type: 'BossVP', count: 1, interval: 0 }, { type: 'TroubleMaker', count: 6, interval: 800 }, { type: 'OldEmployee', count: 4, interval: 1500 }] },
{ enemies: [{ type: 'BossVP', count: 2, interval: 5000 }, { type: 'OldEmployee', count: 5, interval: 1200 }, { type: 'FreshGraduate', count: 8, interval: 600 }] },
{ enemies: [{ type: 'BossVP', count: 2, interval: 3000 }, { type: 'TroubleMaker', count: 8, interval: 600 }, { type: 'OldEmployee', count: 6, interval: 1000 }] },
],
}
export const ALL_MAPS: MapConfig[] = [MAP1, MAP2, MAP3]
2. game/constants.ts(修改)
- 移除硬编码的
PATH_WAYPOINTS和MAP_LABELS(改为从 mapConfigs 动态读取) - 保留其他常量不变
- 新增
WAVES_PER_MAP = 5
3. game/GameManager.ts(修改)
新增字段:
public difficulty: DifficultyLevel = 'normal'
public currentMapIndex: number = 0 // 0=地图1, 1=地图2, 2=地图3
public totalWaveCleared: number = 0 // 总计已清理波次
reset() 时保留 difficulty,重置其他字段。
新增方法:setDifficulty(d: DifficultyLevel)
4. game/mapRenderer.ts(修改)
buildPathTiles和PATH_TILES改为接受参数(不再使用全局常量)drawAllTiles接受pathTiles参数- 新增
renderMapBackground(scene, bgKey)— 绘制地图背景图(铺满地图区域) - 新增
renderDecorations(scene, decorations, pathTiles)— 在非路径格随机摆放装饰物 getCellSize()不变
5. game/enemies/WaveManager.ts(重构)
接受动态波次配置,而非硬编码 WAVE_CONFIG:
constructor(
scene,
waveConfigs: WaveConfigEntry[], // 从外部传入,支持多地图
difficultyMultiplier, // 难度倍率
callbacks: { onWaveComplete, onAllWavesComplete, onDestroyRandomTower }
)
难度影响:
enemyCount *= multiplier.enemyCount(向上取整)- 怪物创建时将速度和HP通过参数传入子类
- Boss HP 乘以
multiplier.bossHp
6. game/enemies/EnemyBase.ts(修改)
构造函数新增 speedMultiplier = 1.0 和 hpMultiplier = 1.0 参数(可选,默认1),子类创建时传入。
7. game/GameScene.ts(重构)
支持多地图流程:
private currentMapIndex = 0
private mapConfigs = ALL_MAPS
// create() 时加载当前地图
// 5波清空后:showMapClearModal → 延迟2s → loadNextMap()
// loadNextMap():销毁当前地图装饰物,重新 renderMapBackground/renderDecorations,重置 waveManager
新增方法:
loadMap(mapConfig: MapConfig)— 初始化地图背景、路径、装饰物、重置 WaveManageronMapCleared()— 5波清场后显示过关弹窗,然后切换下一张地图- 地图切换时:重新计算 PATH_TILES(传给 tileGraphics 重绘)、清除装饰物、加载新背景
8. game/ui/MapTransitionModal.ts(新建)
地图切换过渡弹窗(Phaser DOM或Canvas Text):
过关!人力资源部已清场
KPI: 87% | 获得奖励: +100 HC
[下一关:技术研发部 996福报] (3秒自动关闭)
3秒后自动关闭并触发下一张地图加载。
9. app/page.tsx(修改)— 主页加难度选择
在「开始游戏」按钮前增加难度选择器(三个按钮:简单/普通/困难):
// 选择难度后存入 localStorage,游戏页面读取
localStorage.setItem('game-difficulty', selectedDifficulty)
难度卡片设计:
- 简单:绿色 — "带薪摸鱼,准点下班"
- 普通:蓝色 — "常规打工,偶有加班"
- 困难:红色 — "地狱模式,007全开"
10. app/game/page.tsx(修改)
useEffect 初始化时从 localStorage 读取难度,通过 window.__gameDifficulty 传给场景。
注意事项
- 每张地图preload对应背景图(key不同),场景重用时用
this.textures.exists(key)检查避免重复加载 - PATH_TILES 不再是模块级常量,改为每次渲染时动态计算后传参
mapRenderer.ts导出的PATH_TILES保持向后兼容(先用地图1的配置初始化),GameScene 内部用局部变量覆盖- 地图背景图用
scene.add.image(x, y, key).setDisplaySize(mapW, mapH).setDepth(-1).setOrigin(0,0)铺在地图区域(HUD 下方) - 装饰物图片
setDisplaySize(cellW*0.7, cellH*0.7),setDepth(1),setAlpha(0.6),不影响塔的放置逻辑(只是视觉装饰) - 地图切换时必须 destroy 掉所有装饰物 Phaser 对象,避免内存泄漏
验收标准
- 主页有三个难度按钮,选中高亮,点击「开始游戏」带难度跳转
- 游戏开始后可见地图背景图(不是纯色格子)
- 装饰物(咖啡杯、电脑、桌子)散布在非路径格子上
- 第5波清场后弹出过关弹窗,3秒后自动切换到下一张地图
- 地图2路径形状与地图1不同(Z型 vs S型)
- 困难模式下怪物数量明显增多,Boss更频繁出现
- 三张地图全部通关后显示最终胜利界面
相关文件
- 需求文档:
.docs/prd-tower-defense-game.md - 设计系统:
design-system/大厂保卫战/MASTER.md - 开发规范:
@rules/dev-best-practices.md(必须先阅读) - 原子提交:
@rules/atomic-commit.md
完成后按原子提交规范提交,禁止 git add .。