Files
test1/game/mapRenderer.ts

249 lines
8.2 KiB
TypeScript
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.
import type Phaser from 'phaser'
import {
MAP_COLS,
MAP_ROWS,
GAME_WIDTH,
GAME_HEIGHT,
HUD_HEIGHT,
INITIAL_HC,
INITIAL_KPI,
} from './constants'
import type { MapConfig } from './data/mapConfigs'
import { ALL_MAPS } from './data/mapConfigs'
// ─── PATH TILES ────────────────────────────────────────────────────────────────
/** 将折线关键坐标点展开为完整路径格子集合 */
export function buildPathTiles(
waypoints: readonly { x: number; y: number }[]
): 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) {
const minY = Math.min(from.y, to.y)
const maxY = Math.max(from.y, to.y)
for (let y = minY; y <= maxY; y++) tiles.add(`${from.x},${y}`)
} else {
const minX = Math.min(from.x, to.x)
const maxX = Math.max(from.x, to.x)
for (let x = minX; x <= maxX; x++) tiles.add(`${x},${from.y}`)
}
}
return tiles
}
/**
* 向后兼容导出用地图1配置初始化GameScene 内部用局部变量覆盖)
*/
export const PATH_TILES = buildPathTiles(ALL_MAPS[0].waypoints)
// ─── CELL SIZE ────────────────────────────────────────────────────────────────
/** 计算格子尺寸(正方形格子,取宽高中较小值保证正方形) */
export function getCellSize() {
const rawW = Math.floor(GAME_WIDTH / MAP_COLS)
const rawH = Math.floor((GAME_HEIGHT - HUD_HEIGHT) / MAP_ROWS)
const cell = Math.min(rawW, rawH)
return { cellW: cell, cellH: cell }
}
// ─── TILE RENDERING ───────────────────────────────────────────────────────────
/**
* 绘制所有地图格子
* @param pathTiles 当前地图的路径格集合(若不传则用模块级默认值)
* @param mapConfig 当前地图配置用于读取颜色可选默认用地图1颜色
*/
export function drawAllTiles(
g: Phaser.GameObjects.Graphics,
hovered: { col: number; row: number } | null,
pathTiles?: Set<string>,
mapConfig?: MapConfig
): void {
const { cellW, cellH } = getCellSize()
const tiles = pathTiles ?? PATH_TILES
const pathColor = mapConfig?.pathColor ?? 0x3d2b1f
const hoverColor = 0x7c3aed
g.clear()
for (let row = 0; row < MAP_ROWS; row++) {
for (let col = 0; col < MAP_COLS; col++) {
const key = `${col},${row}`
const isPath = tiles.has(key)
const isHovered = !isPath && hovered?.col === col && hovered?.row === row
const x = col * cellW
const y = HUD_HEIGHT + row * cellH
if (isPath) {
// 路径格:半透明深色叠加,让背景隐约可见
g.fillStyle(pathColor, 0.78)
g.fillRect(x, y, cellW, cellH)
// 路径边框:稍亮一点
g.lineStyle(1, 0x6b4226, 0.9)
g.strokeRect(x, y, cellW, cellH)
} else if (isHovered) {
// 悬停格:紫色高亮半透明
g.fillStyle(hoverColor, 0.35)
g.fillRect(x, y, cellW, cellH)
g.lineStyle(2, 0xa78bfa, 0.9)
g.strokeRect(x, y, cellW, cellH)
} else {
// 可建格:只画细边框,背景图完全透出
g.lineStyle(1, 0x334155, 0.35)
g.strokeRect(x, y, cellW, cellH)
}
}
}
}
// ─── MAP LABELS ───────────────────────────────────────────────────────────────
/**
* 渲染地图区域标签,返回创建的文字对象(便于切图时销毁)
*/
export function renderMapLabels(
scene: Phaser.Scene,
labels?: { col: number; row: number; text: string }[]
): Phaser.GameObjects.Text[] {
const { cellW, cellH } = getCellSize()
const list = labels ?? ALL_MAPS[0].labels
return list.map(label => {
const x = label.col * cellW + cellW / 2
const y = HUD_HEIGHT + label.row * cellH + cellH / 2
return scene.add
.text(x, y, label.text, {
fontFamily: 'VT323, monospace',
fontSize: '13px',
color: '#e2e8f0',
backgroundColor: 'rgba(0,0,0,0.55)',
padding: { x: 5, y: 2 },
})
.setOrigin(0.5, 0.5)
.setDepth(2)
.setAlpha(0.85)
})
}
// ─── 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[] {
// 装饰物改为纯文字标注,不再叠加图片,直接返回空数组
// 视觉信息已由地图标签renderMapLabels和背景图承载
void scene; void decorations; void pathTiles
return []
}
// ─── HUD ──────────────────────────────────────────────────────────────────────
/** HUD 进度条参数 */
export const BAR_W = 300
export const BAR_H = 16
export const BAR_X = (GAME_WIDTH - BAR_W) / 2
export const BAR_Y = (HUD_HEIGHT - BAR_H) / 2
/**
* 渲染顶部 HUD 区域,返回可更新的 Graphics 和 Text 对象
*/
export function renderHUD(scene: Phaser.Scene): {
kpiBar: Phaser.GameObjects.Graphics
kpiText: Phaser.GameObjects.Text
hcText: Phaser.GameObjects.Text
} {
const hudBg = scene.add.graphics()
hudBg.fillStyle(0x0a1628, 0.92)
hudBg.fillRect(0, 0, GAME_WIDTH, HUD_HEIGHT)
hudBg.lineStyle(1, 0x1e3a5f, 1)
hudBg.strokeRect(0, 0, GAME_WIDTH, HUD_HEIGHT)
scene.add.text(16, HUD_HEIGHT / 2, '大厂保卫战', {
fontFamily: "'Press Start 2P', monospace",
fontSize: '10px',
color: '#A78BFA',
}).setOrigin(0, 0.5)
scene.add.text(BAR_X - 8, HUD_HEIGHT / 2, 'KPI', {
fontFamily: "'Press Start 2P', monospace",
fontSize: '8px',
color: '#E2E8F0',
}).setOrigin(1, 0.5)
const trackBar = scene.add.graphics()
trackBar.fillStyle(0x1a1a2e, 1)
trackBar.fillRect(BAR_X, BAR_Y, BAR_W, BAR_H)
trackBar.lineStyle(1, 0x7c3aed, 0.6)
trackBar.strokeRect(BAR_X, BAR_Y, BAR_W, BAR_H)
const kpiBar = scene.add.graphics()
updateKPIBar(kpiBar, INITIAL_KPI)
const kpiText = scene.add.text(
BAR_X + BAR_W / 2, HUD_HEIGHT / 2,
`${INITIAL_KPI}%`,
{
fontFamily: "'Press Start 2P', monospace",
fontSize: '8px',
color: '#E2E8F0',
}
).setOrigin(0.5, 0.5).setDepth(1)
const hcText = scene.add.text(
GAME_WIDTH - 16, HUD_HEIGHT / 2,
`HC: ${INITIAL_HC}`,
{
fontFamily: "'Press Start 2P', monospace",
fontSize: '10px',
color: '#A78BFA',
}
).setOrigin(1, 0.5)
return { kpiBar, kpiText, hcText }
}
/**
* 更新 KPI 进度条(颜色随数值变化:绿→黄→红)
*/
export function updateKPIBar(
kpiBar: Phaser.GameObjects.Graphics,
kpi: number
): void {
kpiBar.clear()
const color = kpi > 60 ? 0x22c55e : kpi > 30 ? 0xf59e0b : 0xef4444
kpiBar.fillStyle(color, 1)
kpiBar.fillRect(BAR_X + 1, BAR_Y + 1, (BAR_W - 2) * (kpi / 100), BAR_H - 2)
}