Complete Cocos Creator port with level bundles, themes, and tooling.
Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
96
assets/scripts/level/LevelTileSync.ts
Normal file
96
assets/scripts/level/LevelTileSync.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
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;
|
||||
}
|
||||
|
||||
function ensureTile(layer: Node, layerKind: 'ground' | 'border', x: number, y: number): Node {
|
||||
const nodeName = tileNodeName(layerKind, x, y);
|
||||
let node = layer.getChildByName(nodeName);
|
||||
if (!node) {
|
||||
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(ground, 'ground', p.x, p.y);
|
||||
n++;
|
||||
}
|
||||
for (const key of wantBorder) {
|
||||
const p = parseCellKey(key);
|
||||
if (!p) continue;
|
||||
ensureTile(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);
|
||||
}
|
||||
Reference in New Issue
Block a user