Files
cocos/assets/scripts/level/LevelTileSync.ts
2026-07-06 09:43:46 +08:00

110 lines
3.7 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 { Layers, Node, Sprite, UITransform, Vec3 } from 'cc';
import { cellToWorldCenter, parseCellKey, tileNodeName } from '../core/GridCoords';
import { LevelConfig } from './LevelTypes';
import { setupLayerContainer } from './TileLayout';
const UI_LAYER = Layers.Enum.UI_2D;
function ensureLayer(root: Node, name: 'Ground' | 'Border'): Node {
let layer = root.getChildByName(name);
if (!layer) {
layer = new Node(name);
layer.parent = root;
layer.layer = UI_LAYER;
}
setupLayerContainer(layer);
return layer;
}
/** 砖块可能在 Tiles排序合并后优先 Tiles 避免在 Ground/Border 重复创建 */
function findTileNode(levelRoot: Node, layerKind: 'ground' | 'border', x: number, y: number): Node | null {
const nodeName = tileNodeName(layerKind, x, y);
for (const parentName of ['Tiles', 'Ground', 'Border'] as const) {
const parent = levelRoot.getChildByName(parentName);
const node = parent?.getChildByName(nodeName) ?? null;
if (node?.isValid) return node;
}
return null;
}
function ensureTile(levelRoot: Node, layer: Node, layerKind: 'ground' | 'border', x: number, y: number): Node {
const existing = findTileNode(levelRoot, layerKind, x, y);
if (existing) {
existing.active = true;
return existing;
}
const nodeName = tileNodeName(layerKind, x, y);
const node = new Node(nodeName);
node.parent = layer;
node.layer = UI_LAYER;
node.addComponent(UITransform);
node.addComponent(Sprite);
node.active = true;
return node;
}
/** 按 levels-database.json 补齐 / 隐藏砖块节点(与关卡编辑器数据一致) */
export function syncTileNodesFromConfig(levelRoot: Node, config: LevelConfig): number {
const ground = ensureLayer(levelRoot, 'Ground');
const border = ensureLayer(levelRoot, 'Border');
const wantGround = new Set(Object.keys(config.ground ?? {}));
const wantBorder = new Set(Object.keys(config.border ?? {}));
let n = 0;
for (const key of wantGround) {
const p = parseCellKey(key);
if (!p) continue;
ensureTile(levelRoot, ground, 'ground', p.x, p.y);
n++;
}
for (const key of wantBorder) {
const p = parseCellKey(key);
if (!p) continue;
ensureTile(levelRoot, border, 'border', p.x, p.y);
n++;
}
for (const ch of ground.children) {
const m = /^g_(-?\d+)_(-?\d+)$/.exec(ch.name);
if (!m) continue;
const key = `${m[1]},${m[2]}`;
ch.active = wantGround.has(key);
}
for (const ch of border.children) {
const m = /^b_(-?\d+)_(-?\d+)$/.exec(ch.name);
if (!m) continue;
const key = `${m[1]},${m[2]}`;
ch.active = wantBorder.has(key);
}
return n;
}
/** 关卡包围盒中心(格子 pivot 落点),用于居中显示 */
export function computeLevelCenterOffset(config: LevelConfig): Vec3 {
const keys = [
...Object.keys(config.ground ?? {}),
...Object.keys(config.border ?? {}),
];
if (!keys.length) return new Vec3(0, 0, 0);
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
for (const key of keys) {
const p = parseCellKey(key);
if (!p) continue;
const w = cellToWorldCenter(new Vec3(p.x, p.y, 0));
minX = Math.min(minX, w.x);
maxX = Math.max(maxX, w.x);
minY = Math.min(minY, w.y);
maxY = Math.max(maxY, w.y);
}
return new Vec3(-(minX + maxX) * 0.5, -(minY + maxY) * 0.5, 0);
}
export function centerLevelRoot(levelRoot: Node, config: LevelConfig) {
const off = computeLevelCenterOffset(config);
levelRoot.setPosition(off.x, off.y, 0);
}