import { Sprite, UITransform, Node, view, Layers, director, find } from 'cc'; import { normalizeTexturePath } from '../visual/EntityTextureResolver'; import { VisualAssets } from '../visual/VisualAssets'; import { getThemeBackground } from './ThemeRegistry'; const BG_NODE_NAME = 'LevelThemeBackground'; /** 与主关卡 UI_2D、HUD UI_3D 分离,由 BgCamera 固定渲染 */ const BG_LAYER = Layers.Enum.DEFAULT; /** 按关卡 theme 应用背景图(BgOverlay + BgCamera,不随主相机缩放/拖拽) */ export class ThemeBackground { static resolveHost(entrance: Node | null): Node | null { const scene = entrance?.scene ?? director.getScene(); if (!scene) return null; return find('BgOverlay', scene); } /** 移除误挂在 GameRoot / MainLevelEntrance 等处的旧背景节点 */ static purgeStaleNodes(entrance: Node | null) { const scene = entrance?.scene ?? director.getScene(); if (!scene) return; const host = find('BgOverlay', scene); const walk = (node: Node) => { for (const ch of [...node.children]) { if (ch.name === BG_NODE_NAME && ch.parent !== host) { ch.destroy(); } else { walk(ch); } } }; walk(scene); } static async apply(entrance: Node | null, themeId: string | undefined): Promise { const parent = this.resolveHost(entrance); if (!parent?.isValid) return; this.purgeStaleNodes(entrance); const path = normalizeTexturePath(getThemeBackground(themeId)); let bgNode = parent.getChildByName(BG_NODE_NAME); if (!path) { if (bgNode) bgNode.active = false; return; } if (!bgNode) { bgNode = new Node(BG_NODE_NAME); bgNode.parent = parent; bgNode.addComponent(UITransform); bgNode.addComponent(Sprite); } bgNode.layer = BG_LAYER; bgNode.active = true; bgNode.setSiblingIndex(0); bgNode.setPosition(0, 0, 0); bgNode.setScale(1, 1, 1); const sf = await VisualAssets.loadTexturePath(path); if (!sf || !bgNode?.isValid) return; const ui = bgNode.getComponent(UITransform)!; const spr = bgNode.getComponent(Sprite)!; spr.spriteFrame = sf; spr.sizeMode = Sprite.SizeMode.CUSTOM; this.layoutFullScreen(bgNode, sf); } private static layoutFullScreen(bgNode: Node, sf: NonNullable>>) { const ui = bgNode.getComponent(UITransform)!; const vs = view.getVisibleSize(); const scale = Math.max(vs.width / sf.originalSize.width, vs.height / sf.originalSize.height); ui.setContentSize(sf.originalSize.width * scale, sf.originalSize.height * scale); ui.setAnchorPoint(0.5, 0.5); bgNode.setPosition(0, 0, 0); } static clear(entrance: Node | null) { this.purgeStaleNodes(entrance); const parent = this.resolveHost(entrance); const bgNode = parent?.getChildByName(BG_NODE_NAME); if (bgNode) bgNode.active = false; } }