refactor(game/mapRenderer): 支持动态路径、地图背景图渲染和装饰物绘制
This commit is contained in:
@@ -5,14 +5,12 @@ import {
|
|||||||
GAME_WIDTH,
|
GAME_WIDTH,
|
||||||
GAME_HEIGHT,
|
GAME_HEIGHT,
|
||||||
HUD_HEIGHT,
|
HUD_HEIGHT,
|
||||||
PATH_WAYPOINTS,
|
|
||||||
COLOR_PATH,
|
|
||||||
COLOR_BUILDABLE,
|
|
||||||
COLOR_HOVER,
|
|
||||||
COLOR_BORDER,
|
|
||||||
MAP_LABELS,
|
|
||||||
INITIAL_KPI,
|
INITIAL_KPI,
|
||||||
} from './constants'
|
} from './constants'
|
||||||
|
import type { MapConfig } from './data/mapConfigs'
|
||||||
|
import { ALL_MAPS } from './data/mapConfigs'
|
||||||
|
|
||||||
|
// ─── PATH TILES ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** 将折线关键坐标点展开为完整路径格子集合 */
|
/** 将折线关键坐标点展开为完整路径格子集合 */
|
||||||
export function buildPathTiles(
|
export function buildPathTiles(
|
||||||
@@ -35,57 +33,78 @@ export function buildPathTiles(
|
|||||||
return tiles
|
return tiles
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PATH_TILES = buildPathTiles(PATH_WAYPOINTS)
|
/**
|
||||||
|
* 向后兼容导出:用地图1配置初始化(GameScene 内部用局部变量覆盖)
|
||||||
|
*/
|
||||||
|
export const PATH_TILES = buildPathTiles(ALL_MAPS[0].waypoints)
|
||||||
|
|
||||||
|
// ─── CELL SIZE ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** 计算格子尺寸(正方形格子,取宽高中较小值保证正方形) */
|
/** 计算格子尺寸(正方形格子,取宽高中较小值保证正方形) */
|
||||||
export function getCellSize() {
|
export function getCellSize() {
|
||||||
const rawW = Math.floor(GAME_WIDTH / MAP_COLS) // 80
|
const rawW = Math.floor(GAME_WIDTH / MAP_COLS)
|
||||||
const rawH = Math.floor((GAME_HEIGHT - HUD_HEIGHT) / MAP_ROWS) // 55
|
const rawH = Math.floor((GAME_HEIGHT - HUD_HEIGHT) / MAP_ROWS)
|
||||||
const cell = Math.min(rawW, rawH) // 55 → 正方形
|
const cell = Math.min(rawW, rawH)
|
||||||
return { cellW: cell, cellH: cell }
|
return { cellW: cell, cellH: cell }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── TILE RENDERING ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绘制所有地图格子
|
* 绘制所有地图格子
|
||||||
|
* @param pathTiles 当前地图的路径格集合(若不传则用模块级默认值)
|
||||||
|
* @param mapConfig 当前地图配置,用于读取颜色(可选,默认用地图1颜色)
|
||||||
*/
|
*/
|
||||||
export function drawAllTiles(
|
export function drawAllTiles(
|
||||||
g: Phaser.GameObjects.Graphics,
|
g: Phaser.GameObjects.Graphics,
|
||||||
hovered: { col: number; row: number } | null
|
hovered: { col: number; row: number } | null,
|
||||||
|
pathTiles?: Set<string>,
|
||||||
|
mapConfig?: MapConfig
|
||||||
): void {
|
): void {
|
||||||
const { cellW, cellH } = getCellSize()
|
const { cellW, cellH } = getCellSize()
|
||||||
|
const tiles = pathTiles ?? PATH_TILES
|
||||||
|
const pathColor = mapConfig?.pathColor ?? 0x3d2b1f
|
||||||
|
const buildColor = mapConfig?.buildColor ?? 0x1e3a5f
|
||||||
|
const hoverColor = 0x2d5a8e
|
||||||
|
const borderColor = 0x0a1628
|
||||||
|
|
||||||
g.clear()
|
g.clear()
|
||||||
|
|
||||||
for (let row = 0; row < MAP_ROWS; row++) {
|
for (let row = 0; row < MAP_ROWS; row++) {
|
||||||
for (let col = 0; col < MAP_COLS; col++) {
|
for (let col = 0; col < MAP_COLS; col++) {
|
||||||
const isPath = PATH_TILES.has(`${col},${row}`)
|
const isPath = tiles.has(`${col},${row}`)
|
||||||
const isHovered =
|
const isHovered =
|
||||||
!isPath && hovered !== null &&
|
!isPath && hovered !== null &&
|
||||||
hovered.col === col && hovered.row === row
|
hovered.col === col && hovered.row === row
|
||||||
|
|
||||||
const fillColor = isPath
|
const fillColor = isPath ? pathColor : isHovered ? hoverColor : buildColor
|
||||||
? COLOR_PATH
|
|
||||||
: isHovered ? COLOR_HOVER : COLOR_BUILDABLE
|
|
||||||
|
|
||||||
const x = col * cellW
|
const x = col * cellW
|
||||||
const y = HUD_HEIGHT + row * cellH
|
const y = HUD_HEIGHT + row * cellH
|
||||||
|
|
||||||
g.fillStyle(fillColor, 1)
|
g.fillStyle(fillColor, 1)
|
||||||
g.fillRect(x + 1, y + 1, cellW - 2, cellH - 2)
|
g.fillRect(x + 1, y + 1, cellW - 2, cellH - 2)
|
||||||
g.lineStyle(1, COLOR_BORDER, 0.6)
|
g.lineStyle(1, borderColor, 0.6)
|
||||||
g.strokeRect(x, y, cellW, cellH)
|
g.strokeRect(x, y, cellW, cellH)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── MAP LABELS ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 渲染地图装饰性标签
|
* 渲染地图装饰性标签,返回创建的文字对象(便于切图时销毁)
|
||||||
*/
|
*/
|
||||||
export function renderMapLabels(scene: Phaser.Scene): void {
|
export function renderMapLabels(
|
||||||
|
scene: Phaser.Scene,
|
||||||
|
labels?: { col: number; row: number; text: string }[]
|
||||||
|
): Phaser.GameObjects.Text[] {
|
||||||
const { cellW, cellH } = getCellSize()
|
const { cellW, cellH } = getCellSize()
|
||||||
for (const label of MAP_LABELS) {
|
const list = labels ?? ALL_MAPS[0].labels
|
||||||
|
return list.map(label => {
|
||||||
const x = label.col * cellW + cellW / 2
|
const x = label.col * cellW + cellW / 2
|
||||||
const y = HUD_HEIGHT + label.row * cellH + cellH / 2
|
const y = HUD_HEIGHT + label.row * cellH + cellH / 2
|
||||||
scene.add
|
return scene.add
|
||||||
.text(x, y, label.text, {
|
.text(x, y, label.text, {
|
||||||
fontFamily: 'VT323, monospace',
|
fontFamily: 'VT323, monospace',
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
@@ -93,9 +112,68 @@ export function renderMapLabels(scene: Phaser.Scene): void {
|
|||||||
})
|
})
|
||||||
.setOrigin(0.5, 0.5)
|
.setOrigin(0.5, 0.5)
|
||||||
.setAlpha(0.5)
|
.setAlpha(0.5)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── BACKGROUND ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染地图背景图(铺满地图区域,HUD 下方)
|
||||||
|
* 返回创建的 Image 对象,切图时需 destroy
|
||||||
|
*/
|
||||||
|
export function renderMapBackground(
|
||||||
|
scene: Phaser.Scene,
|
||||||
|
bgKey: string
|
||||||
|
): Phaser.GameObjects.Image | null {
|
||||||
|
if (!scene.textures.exists(bgKey)) return null
|
||||||
|
|
||||||
|
const { cellW, cellH } = getCellSize()
|
||||||
|
const mapW = MAP_COLS * cellW
|
||||||
|
const mapH = MAP_ROWS * cellH
|
||||||
|
|
||||||
|
return scene.add
|
||||||
|
.image(0, HUD_HEIGHT, bgKey)
|
||||||
|
.setDisplaySize(mapW, mapH)
|
||||||
|
.setDepth(-1)
|
||||||
|
.setOrigin(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── DECORATIONS ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在非路径格子上渲染装饰物图片,返回对象数组(切图时销毁)
|
||||||
|
*/
|
||||||
|
export function renderDecorations(
|
||||||
|
scene: Phaser.Scene,
|
||||||
|
decorations: MapConfig['decorations'],
|
||||||
|
pathTiles: Set<string>
|
||||||
|
): Phaser.GameObjects.Image[] {
|
||||||
|
const { cellW, cellH } = getCellSize()
|
||||||
|
const result: Phaser.GameObjects.Image[] = []
|
||||||
|
|
||||||
|
for (const deco of decorations) {
|
||||||
|
const key = `${deco.col},${deco.row}`
|
||||||
|
// 跳过路径格
|
||||||
|
if (pathTiles.has(key)) continue
|
||||||
|
// 检查贴图是否已加载
|
||||||
|
if (!scene.textures.exists(deco.key)) continue
|
||||||
|
|
||||||
|
const x = deco.col * cellW + cellW / 2
|
||||||
|
const y = HUD_HEIGHT + deco.row * cellH + cellH / 2
|
||||||
|
|
||||||
|
const img = scene.add
|
||||||
|
.image(x, y, deco.key)
|
||||||
|
.setDisplaySize(cellW * 0.7, cellH * 0.7)
|
||||||
|
.setDepth(1)
|
||||||
|
.setAlpha(0.6)
|
||||||
|
result.push(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── HUD ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** HUD 进度条参数 */
|
/** HUD 进度条参数 */
|
||||||
export const BAR_W = 300
|
export const BAR_W = 300
|
||||||
export const BAR_H = 16
|
export const BAR_H = 16
|
||||||
@@ -110,39 +188,33 @@ export function renderHUD(scene: Phaser.Scene): {
|
|||||||
kpiText: Phaser.GameObjects.Text
|
kpiText: Phaser.GameObjects.Text
|
||||||
hcText: Phaser.GameObjects.Text
|
hcText: Phaser.GameObjects.Text
|
||||||
} {
|
} {
|
||||||
// HUD 背景
|
|
||||||
const hudBg = scene.add.graphics()
|
const hudBg = scene.add.graphics()
|
||||||
hudBg.fillStyle(0x0a1628, 0.92)
|
hudBg.fillStyle(0x0a1628, 0.92)
|
||||||
hudBg.fillRect(0, 0, GAME_WIDTH, HUD_HEIGHT)
|
hudBg.fillRect(0, 0, GAME_WIDTH, HUD_HEIGHT)
|
||||||
hudBg.lineStyle(1, 0x1e3a5f, 1)
|
hudBg.lineStyle(1, 0x1e3a5f, 1)
|
||||||
hudBg.strokeRect(0, 0, GAME_WIDTH, HUD_HEIGHT)
|
hudBg.strokeRect(0, 0, GAME_WIDTH, HUD_HEIGHT)
|
||||||
|
|
||||||
// 左侧标题
|
|
||||||
scene.add.text(16, HUD_HEIGHT / 2, '大厂保卫战', {
|
scene.add.text(16, HUD_HEIGHT / 2, '大厂保卫战', {
|
||||||
fontFamily: "'Press Start 2P', monospace",
|
fontFamily: "'Press Start 2P', monospace",
|
||||||
fontSize: '10px',
|
fontSize: '10px',
|
||||||
color: '#A78BFA',
|
color: '#A78BFA',
|
||||||
}).setOrigin(0, 0.5)
|
}).setOrigin(0, 0.5)
|
||||||
|
|
||||||
// KPI 标签
|
|
||||||
scene.add.text(BAR_X - 8, HUD_HEIGHT / 2, 'KPI', {
|
scene.add.text(BAR_X - 8, HUD_HEIGHT / 2, 'KPI', {
|
||||||
fontFamily: "'Press Start 2P', monospace",
|
fontFamily: "'Press Start 2P', monospace",
|
||||||
fontSize: '8px',
|
fontSize: '8px',
|
||||||
color: '#E2E8F0',
|
color: '#E2E8F0',
|
||||||
}).setOrigin(1, 0.5)
|
}).setOrigin(1, 0.5)
|
||||||
|
|
||||||
// 进度条背景轨道
|
|
||||||
const trackBar = scene.add.graphics()
|
const trackBar = scene.add.graphics()
|
||||||
trackBar.fillStyle(0x1a1a2e, 1)
|
trackBar.fillStyle(0x1a1a2e, 1)
|
||||||
trackBar.fillRect(BAR_X, BAR_Y, BAR_W, BAR_H)
|
trackBar.fillRect(BAR_X, BAR_Y, BAR_W, BAR_H)
|
||||||
trackBar.lineStyle(1, 0x7c3aed, 0.6)
|
trackBar.lineStyle(1, 0x7c3aed, 0.6)
|
||||||
trackBar.strokeRect(BAR_X, BAR_Y, BAR_W, BAR_H)
|
trackBar.strokeRect(BAR_X, BAR_Y, BAR_W, BAR_H)
|
||||||
|
|
||||||
// KPI 进度前景
|
|
||||||
const kpiBar = scene.add.graphics()
|
const kpiBar = scene.add.graphics()
|
||||||
updateKPIBar(kpiBar, INITIAL_KPI)
|
updateKPIBar(kpiBar, INITIAL_KPI)
|
||||||
|
|
||||||
// KPI 百分比文字
|
|
||||||
const kpiText = scene.add.text(
|
const kpiText = scene.add.text(
|
||||||
BAR_X + BAR_W / 2, HUD_HEIGHT / 2,
|
BAR_X + BAR_W / 2, HUD_HEIGHT / 2,
|
||||||
`${INITIAL_KPI}%`,
|
`${INITIAL_KPI}%`,
|
||||||
@@ -153,7 +225,6 @@ export function renderHUD(scene: Phaser.Scene): {
|
|||||||
}
|
}
|
||||||
).setOrigin(0.5, 0.5).setDepth(1)
|
).setOrigin(0.5, 0.5).setDepth(1)
|
||||||
|
|
||||||
// 右侧 HC 数值
|
|
||||||
const hcText = scene.add.text(
|
const hcText = scene.add.text(
|
||||||
GAME_WIDTH - 16, HUD_HEIGHT / 2,
|
GAME_WIDTH - 16, HUD_HEIGHT / 2,
|
||||||
`HC: 200`,
|
`HC: 200`,
|
||||||
|
|||||||
Reference in New Issue
Block a user