Files
cocos/assets/scripts/GameController.ts
刘宇飞 cba5105908 Initial Cocos Creator port of main-site Unity WebGL game.
Includes core gameplay, 600 exported levels, visual assets, web bridge, and bootstrap scene.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 14:57:46 +08:00

276 lines
9.6 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 {
_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<string, unknown>)[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<string, unknown>)[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();
}
}