import { _decorator, Component, Node, Enum, CCString, CCInteger, CCBoolean, game, } from 'cc'; import { GameManager } from './manager/GameManager'; import { Skin } from './core/Define'; import { VisualAssets } from './visual/VisualAssets'; import { getLevelCount } from './level/LevelRegistry'; const { ccclass, property, executionOrder } = _decorator; /** UI 主题(与 Unity ChangeUIStyle 参数一致) */ export enum UIStyleType { default = 0, chinese = 1, redArmy = 2, numMan = 3, snow = 4, sanxing = 5, } const UIStyleNames = ['default', 'chinese', 'redArmy', 'numMan', 'snow', 'sanxing']; /** 多人角色 */ export enum MultRole { PlayerA1 = 0, PlayerA2 = 1, PlayerA3 = 2, PlayerB1 = 3, PlayerB2 = 4, PlayerB3 = 5, } const MultRoleNames = ['PlayerA1', 'PlayerA2', 'PlayerA3', 'PlayerB1', 'PlayerB2', 'PlayerB3']; /** * 对应 Unity 场景 GameController + Inspector 调试面板(TestGame2) * 挂在 GameController 节点,与 GameManager 同级或同节点 */ @ccclass('GameController') @executionOrder(-50) export class GameController extends Component { @property({ group: { name: '场景', id: '1' }, type: Node, displayName: 'Main Level Entrance' }) mainLevelEntrance: Node | null = null; @property({ group: { name: '场景', id: '1' }, type: Node, displayName: 'Cur Level', readonly: true }) curLevelNode: Node | null = null; @property({ group: { name: '关卡', id: '2' }, type: CCInteger, displayName: 'Initial Level ID' }) initialLevelID = 1; @property({ group: { name: '关卡', id: '2' }, type: CCInteger, displayName: 'Cur Level ID', readonly: true }) curLevelID = 1; @property({ group: { name: '关卡', id: '2' }, displayName: '已注册关卡数', readonly: true }) registeredLevelCount = 0; @property({ group: { name: '角色', id: '3' }, type: Enum(Skin), displayName: 'Player Skin' }) playerSkin: Skin = Skin.Silu; @property({ group: { name: '多人', id: '4' }, displayName: 'Mult Mode' }) multMode = false; @property({ group: { name: '多人', id: '4' }, type: Enum(MultRole), displayName: 'Mult Player Role' }) multPlayerRoleEnum: MultRole = MultRole.PlayerA1; @property({ group: { name: '主题', id: '5' }, type: Enum(UIStyleType), displayName: 'UI Style' }) uiStyleEnum: UIStyleType = UIStyleType.default; // --- 与 Unity Inspector 输入框一致 --- @property({ group: { name: '调试输入', id: '6' }, displayName: 'inputLevel' }) inputLevel = '1'; @property({ group: { name: '调试输入', id: '6' }, displayName: 'inputStyle' }) inputStyle = 'default'; @property({ group: { name: '调试输入', id: '6' }, multiline: true, displayName: 'coinStr' }) coinStr = '["-2,0","-2,2","-1,2"]'; // --- 播放时勾选即执行(等同 Unity 按钮) --- @property({ group: { name: '调试操作(播放时勾选)', id: '7' }, displayName: '▶ SwitchLevel' }) execSwitchLevel = false; @property({ group: { name: '调试操作(播放时勾选)', id: '7' }, displayName: '▶ EndInput' }) execEndInput = false; @property({ group: { name: '调试操作(播放时勾选)', id: '7' }, displayName: '▶ ChangeStyle' }) execChangeStyle = false; @property({ group: { name: '调试操作(播放时勾选)', id: '7' }, displayName: '▶ StartMultPlay' }) execStartMultPlay = false; @property({ group: { name: '调试操作(播放时勾选)', id: '7' }, displayName: '▶ Mute' }) execMute = false; @property({ group: { name: '调试操作(播放时勾选)', id: '7' }, displayName: '▶ Unmute' }) execUnmute = false; private gm!: GameManager; onLoad() { this.gm = this.getComponent(GameManager) || this.node.getComponentInChildren(GameManager)!; if (!this.gm) { this.gm = this.node.addComponent(GameManager); } this.syncToManager(); this.registerWebApi(); this.registeredLevelCount = getLevelCount(); } start() { this.refreshReadonly(); } update() { if (!game.isRunning()) return; this.refreshReadonly(); this.pollInspectorActions(); } private refreshReadonly() { this.curLevelID = this.gm?.curLevelID ?? this.curLevelID; if (this.gm?.mainLevelEntrance) { this.mainLevelEntrance = this.gm.mainLevelEntrance; const lv = this.gm.mainLevelEntrance.children.find((c) => c.name.startsWith('Level_')); this.curLevelNode = lv ?? null; } this.multMode = this.gm?.multMode ?? false; } private syncToManager() { if (!this.gm) return; if (this.mainLevelEntrance) this.gm.mainLevelEntrance = this.mainLevelEntrance; this.gm.initialLevelID = this.initialLevelID; this.gm.playerSkin = this.playerSkin; this.gm.multMode = this.multMode; this.gm.multPlayerRole = MultRoleNames[this.multPlayerRoleEnum] ?? 'PlayerA1'; this.gm.uiStyle = UIStyleNames[this.uiStyleEnum] ?? 'default'; this.gm.curLevelID = this.initialLevelID; } private pollInspectorActions() { if (this.execSwitchLevel) { this.execSwitchLevel = false; this.onInspectorSwitchLevel(); } if (this.execEndInput) { this.execEndInput = false; this.callSetIsInputEnd(1); } if (this.execChangeStyle) { this.execChangeStyle = false; this.onInspectorChangeStyle(); } if (this.execStartMultPlay) { this.execStartMultPlay = false; this.onInspectorStartMultPlay(); } if (this.execMute) { this.execMute = false; this.callMute(); } if (this.execUnmute) { this.execUnmute = false; this.callUnmute(); } } // --- Inspector / SendMessage 共用 --- onInspectorSwitchLevel() { const id = parseInt(this.inputLevel, 10); if (Number.isNaN(id)) { console.warn('[GameController] inputLevel 无效:', this.inputLevel); return; } this.syncToManager(); this.gm.switchLevel(id); this.curLevelID = id; console.log('[GameController] SwitchLevel', id); } onInspectorChangeStyle() { const style = this.inputStyle.trim() || UIStyleNames[this.uiStyleEnum]; this.gm.changeUIStyle(style); this.uiStyleEnum = UIStyleNames.indexOf(style) as UIStyleType; if (this.uiStyleEnum < 0) this.uiStyleEnum = UIStyleType.default; console.log('[GameController] ChangeUIStyle', style); } onInspectorStartMultPlay() { const role = MultRoleNames[this.multPlayerRoleEnum] ?? 'PlayerA1'; this.gm.startMultPlay(role, this.coinStr); this.multMode = true; console.log('[GameController] StartMultPlay', role, this.coinStr); } applyPlayerSkinFromInspector() { const player = this.gm.findNodeByName('Player') ?? this.gm.findNodeByName(this.gm.multPlayerRole); const pc = player?.getComponent('PlayerController') as { callChangeSkin?: (n: number) => void }; pc?.callChangeSkin?.(this.playerSkin); this.gm.playerSkin = this.playerSkin; } // --- JS Bridge (SendMessage) --- private registerWebApi() { const api = { SendMessage: (objectName: string, methodName: string, param?: string | number) => { this.sendMessage(objectName, methodName, param); }, }; if (typeof window !== 'undefined') { (window as unknown as { cocosIns?: typeof api }).cocosIns = api; (window as unknown as { unityInstance?: typeof api }).unityInstance = api; } } sendMessage(objectName: string, methodName: string, param?: string | number) { if (objectName === 'GameController') { this.invoke(this, methodName, param); return; } if (objectName === 'UIMain') { const n = this.gm.findNodeByName('UIMain'); const c = n?.getComponent('UIMain'); if (c) this.invoke(c, methodName, param); return; } const node = this.gm.findNodeByName(objectName); if (!node) { console.warn(`SendMessage: 未找到 ${objectName}`); return; } for (const comp of node.getComponents(Component)) { if (typeof (comp as Record)[methodName] === 'function') { this.invoke(comp, methodName, param); return; } } console.warn(`SendMessage: ${objectName}.${methodName} 无此方法`); } private invoke(target: object, methodName: string, param?: string | number) { const fn = (target as Record)[methodName]; if (typeof fn !== 'function') return; if (param === undefined) (fn as () => void).call(target); else (fn as (p: string | number) => void).call(target, param); } switchLevel(levelID: number) { this.inputLevel = String(levelID); this.onInspectorSwitchLevel(); } callSetIsInputEnd(v: number) { this.gm.callSetIsInputEnd(v); } changeUIStyle(style: string) { this.inputStyle = style; this.onInspectorChangeStyle(); } startMultPlay(role: string, coins: string) { this.coinStr = coins; const idx = MultRoleNames.indexOf(role); if (idx >= 0) this.multPlayerRoleEnum = idx as MultRole; this.onInspectorStartMultPlay(); } callMute() { this.gm.callMute(); } callUnmute() { this.gm.callUnmute(); } }