import { _decorator, Component, Node, Prefab, Label, Animation, instantiate, Vec3, tween, macro, Sprite, Color, Button, animation, director, assetManager } from 'cc'; import { WoodenManPlayer } from './WoodenManPlayer'; import { tgxAudioMgr } from 'db://assets/core_tgx/tgx'; import { ModuleDef } from 'db://assets/scripts/ModuleDef'; import { resLoader } from 'db://assets/core_tgx/base/utils/ResLoader'; import { EMusicDefine } from 'db://assets/module_basic/scripts/MusicDefine'; import { SceneDef } from 'db://assets/scripts/SceneDef'; const { ccclass, property } = _decorator; @ccclass('WoodenManGame') export class WoodenManGame extends Component { @property(Prefab) rolePrefab: Prefab = null; // 角色预制体 @property(Node) dollNode: Node = null; // 木头人节点 @property(Label) aliveCountLabel: Label = null; // 存活人数显示 @property(Label) winCountLabel: Label = null; // 晋级名额显示 @property(Node) playersNode: Node = null; // 存放所有玩家的节点 @property(Node) localPlayerNode: Node = null; // 本地玩家节点引用 @property(Node) guiNode: Node = null; // ui节点 private turnAnimationDuration: number = 0.3; // 转身动画持续时间 private readonly TURN_BACK_DELAY = 2; // 转回正面的延迟时间 private readonly INITIAL_PLAYERS = 36; // 初始玩家数 private aiPlayers: Node[] = []; // AI玩家节点列表 private playerNodes: Map = new Map(); // 所有玩家节点映射 private readonly minRoleId: number = 1; // 最小角色ID readonly maxRoleId: number = 5; readonly REQUIRED_QUALIFIED = 10; // 需要晋级的人数 qualifiedCount: number = 0; // 已晋级人数 gameTimer: number = 180; // 游戏时间(3分钟) isGameOver: boolean = false; // 游戏是否结束 @property(Node) warningLightsNode: Node = null; // 三个警示灯的父节点 private readonly WARNING_LIGHT_BASE_INTERVAL = 1; // 修改基础间隔为0.7秒 private isGameStarted: boolean = false; // 添加游戏是否已开始的标志 @property(Node) stopButton: Node = null; // 改为停止按钮引用 @property(Animation) goAnim: Animation; private isDollTurning: boolean = false; // 添加标记,表示娃娃是否正在转身 private turnStartTime: number = 0; // 记录转身开始时间 // 添加公共 getter 方法 public getGameStarted(): boolean { return this.isGameStarted; } public getIsDollTurning(): boolean { return this.isDollTurning; } async start() { await this.initPlayers(); this.schedule(this.updateGameTimer, 1); this.updateWinCount(); tgxAudioMgr.inst.play(EMusicDefine.MUSIC_BGM_BATTLE, 1, ModuleDef.BASIC); // 修改按钮事件监听 if (this.stopButton) { this.stopButton.active = false; // 初始隐藏 this.stopButton.on(Node.EventType.TOUCH_START, this.onStopButtonPress, this); this.stopButton.on(Node.EventType.TOUCH_END, this.onStopButtonRelease, this); this.stopButton.on(Node.EventType.TOUCH_CANCEL, this.onStopButtonRelease, this); } // 延迟3秒后开始第一次转身 this.scheduleOnce(() => { this.isGameStarted = true; // 标记游戏开始 // 显示停止按钮 this.guiNode.getComponent(Animation).play("showUI"); this.executeTurnBackPhase(true); }, 12); // 延长等待时间,等摄像机序列播放完 } onDestroy() { // 停止所有定时器 this.unscheduleAllCallbacks(); // 停止所有 tween if (this.dollNode) { tween(this.dollNode).stop(); } // 停止所有玩家的 tween 和动画 this.playerNodes.forEach((playerNode) => { if (playerNode && playerNode.isValid) { tween(playerNode).stop(); const playerComp = playerNode.getComponent(WoodenManPlayer); if (playerComp && playerComp.isValid) { try { playerComp.cleanup(); } catch (error) { console.warn('Error cleaning up player:', error); } } } }); // 停止警示灯相关 if (this.warningLightsNode && this.warningLightsNode.isValid) { this.warningLightsNode.children.forEach(light => { if (light && light.isValid) { tween(light).stop(); } }); } // 取消按钮事件监听 if (this.stopButton && this.stopButton.isValid) { this.stopButton.off(Node.EventType.TOUCH_START, this.onStopButtonPress, this); this.stopButton.off(Node.EventType.TOUCH_END, this.onStopButtonRelease, this); this.stopButton.off(Node.EventType.TOUCH_CANCEL, this.onStopButtonRelease, this); } // 停止音频 try { tgxAudioMgr.inst.stop(); } catch (error) { console.warn('Error stopping audio:', error); } } // 修改按钮事件处理 private onStopButtonPress() { const localPlayer = this.localPlayerNode?.getComponent(WoodenManPlayer); if (localPlayer && localPlayer.isAlive() && !localPlayer.isPassed()) { localPlayer.setButtonPressed(true); localPlayer.stopMoving(); } } private onStopButtonRelease() { const localPlayer = this.localPlayerNode?.getComponent(WoodenManPlayer); if (localPlayer && localPlayer.isAlive() && !localPlayer.isPassed()) { localPlayer.setButtonPressed(false); localPlayer.startMoving(); } } private async initPlayers() { // 初始化本地玩家 if (this.localPlayerNode) { const playerComp = this.localPlayerNode.getComponent(WoodenManPlayer); playerComp.init(0, true, this); // 设置为本地玩家 // 添加随机位置设置 const x = 0; //const x = Math.random() * 15 - 7; const z = 50 + Math.random() * 5; this.localPlayerNode.setPosition(new Vec3(x, 0, z)); this.localPlayerNode.setRotationFromEuler(0, 180, 0); // 添加本地玩家名字 const nameLabel = this.localPlayerNode.getChildByName("NameLabel"); if (nameLabel) { const labelComponent = nameLabel.getComponent(Label); if (labelComponent) { labelComponent.string = "You"; // 本地玩家显示为 "You" labelComponent.color = new Color(255, 255, 0, 255); // 黄色 } } nameLabel.active = false; this.playerNodes.set(0, this.localPlayerNode); } // 创建AI玩家 for (let i = 1; i < this.INITIAL_PLAYERS; i++) { const aiPlayer = await this.createPlayer(i); this.playerNodes.set(i, aiPlayer); this.aiPlayers.push(aiPlayer); } this.updateAliveCount(); } private async createPlayer(id: number): Promise { return new Promise((resolve) => { // 随机位置:起点区域 const x = Math.random() * 11 - 5; const z = 50 + Math.random() * 3; // 50-55随机 // 随机角色ID(1-5) const roleId = Math.floor(Math.random() * (this.maxRoleId - this.minRoleId + 1)) + this.minRoleId; const roleIdStr = ('000' + roleId).slice(-3); resLoader.load(ModuleDef.Arean, `common/role/Character${roleIdStr}`, (err: Error | null, prefab: Prefab) => { if (!err && prefab) { const role = instantiate(prefab); this.playersNode.addChild(role); // 设置位置和朝向(面向-Z方向) role.setPosition(new Vec3(x, 0, z)); role.setRotationFromEuler(0, 180, 0); // 添加 WoodenManPlayer 组件 const playerComp = role.addComponent(WoodenManPlayer); playerComp.init(id, false, this); // 添加角色名字NameLabel const nameLabel = role.getChildByName("NameLabel"); if (nameLabel) { const labelComponent = nameLabel.getComponent(Label); if (labelComponent) { labelComponent.string = `player${id}`; labelComponent.color = new Color(255, 255, 255, 255); } } nameLabel.active = false; resolve(role); } else { console.error('Failed to load character prefab:', err); resolve(null); } }); }); } // 更新存活人数显示 private updateAliveCount() { let aliveCount = 0; this.playerNodes.forEach((node) => { const player = node.getComponent(WoodenManPlayer); if (player && player.isAlive()) { aliveCount++; } }); this.aliveCountLabel.string = `${aliveCount}/${this.INITIAL_PLAYERS}`; } // 修改 startFirstTurn 方法 // 修改 executeTurnBackPhase 方法 private executeTurnBackPhase(firstTimes: boolean = false) { if (!this.isGameStarted || this.isGameOver) return; tgxAudioMgr.inst.playOneShot(EMusicDefine.WOODENMAN_GO, 1, ModuleDef.BASIC); this.goAnim.getComponent(Animation).play("showGo"); // 直接执行转身动画 this.playDollTurnAnimation(); this.playerNodes.forEach((playerNode) => { const playerComp = playerNode.getComponent(WoodenManPlayer); if (playerComp && (firstTimes || !playerComp.getIsLocalPlayer()) && playerComp.isAlive() && !playerComp.isPassed()) { playerComp.setButtonPressed(false); // 添加随机延迟(0-0.3秒) this.scheduleOnce(() => { playerComp.startMoving(); }, Math.random() * 0.3 + 0.3); } }); // 延迟后开始下一轮 this.scheduleOnce(() => { this.executeTurningPhase(); }, this.TURN_BACK_DELAY); } private executeTurningPhase() { // 先播放警告音效和警示灯 tgxAudioMgr.inst.playOneShot(EMusicDefine.WOODENMAN, 1, ModuleDef.BASIC); if (this.warningLightsNode) { this.warningLightsNode.active = true; // AI玩家在警示灯期间随机停止 this.playerNodes.forEach((playerNode) => { const playerComp = playerNode.getComponent(WoodenManPlayer); if (playerComp && !playerComp.getIsLocalPlayer() && playerComp.isAlive() && !playerComp.isPassed()) { const stopDelay = Math.random() * 1.8 + 2.5; this.scheduleOnce(() => { if (playerComp.isAlive() && playerComp.isMoving) { playerComp.setButtonPressed(true); playerComp.stopMoving(); } }, stopDelay); } }); // 播放警示灯序列 this.playWarningLights(() => { if (this.warningLightsNode) { this.warningLightsNode.active = false; } // 开始转身 this.playDollTurnAnimation(() => { // 等待0.3秒后开始检查移动状态 this.scheduleOnce(() => { this.schedule(this.checkMovingInterval, 0); tgxAudioMgr.inst.playOneShot(EMusicDefine.WOODENMAN_Kill, 1, ModuleDef.BASIC); }, 0.3); }); // 延迟后开始下一轮 this.scheduleOnce(() => { this.unschedule(this.checkMovingInterval); this.executeTurnBackPhase(); }, Math.round((3 + Math.random() * 2) * 10) / 10); }); } } checkMovingInterval() { const currentTime = Date.now(); const timeSinceTurnStart = (currentTime - this.turnStartTime) / 1000; // 转换为秒 this.playerNodes.forEach((playerNode) => { const playerComp = playerNode.getComponent(WoodenManPlayer); if (playerComp && playerComp.isAlive() && !playerComp.isPassed()) { if (playerComp.isMoving) { if (timeSinceTurnStart > 0.3) { // 如果在转身0.3秒后还在移动 this.killPlayer(playerComp, playerNode); } } } }); } // 检查游戏是否结束 private checkGameEnd(): boolean { if (this.isGameOver) return true; let aliveCount = 0; let qualifiedCount = 0; let totalCount = 0; this.playerNodes.forEach((playerNode) => { const playerComp = playerNode.getComponent(WoodenManPlayer); if (playerComp) { totalCount++; if (playerComp.isAlive()) aliveCount++; if (playerComp.isPassed()) qualifiedCount++; } }); // 如果所有玩家都要么死亡要么晋级,游戏结束 if (qualifiedCount + (totalCount - aliveCount) === totalCount) { this.endGame('所有玩家已完成比赛!游戏结束'); return true; } return false; } // 游戏主循环 update(dt: number) { if (!this.isGameOver) { this.checkGameEnd(); } } endGame(reason: string = '') { if (this.isGameOver) return; this.isGameOver = true; this.unscheduleAllCallbacks(); // 停止所有定时器 // 如果是因为达到晋级人数而结束,则击杀所有未晋级的存活玩家 if (reason.includes('达到晋级人数要求')) { this.killAllNonQualifiedPlayers(); } // 统计最终结果 let qualifiedPlayers = 0; let deadPlayers = 0; this.playerNodes.forEach((playerNode) => { const playerComp = playerNode.getComponent(WoodenManPlayer); if (playerComp) { if (playerComp.isPassed()) qualifiedPlayers++; if (!playerComp.isAlive()) deadPlayers++; } }); // 更新最终显示 this.updateWinCount(); this.updateAliveCount(); this.scheduleOnce(() => { resLoader.load(ModuleDef.Arean, 'common/prefabs/settlement', (err: Error | null, prefab: Prefab) => { if (!err && prefab) { const n = instantiate(prefab); this.guiNode.addChild(n); n.getComponent(Animation).play("showSettlement"); // Lives从10滚动到8 const livesLabel = n.getChildByPath("settlementNode/Node/lives").getComponent(Label); let lives = { value: 36 }; tween(lives) .to(1, { value: 10 }, { easing: 'linear', onUpdate: () => { livesLabel.string = Math.floor(lives.value).toString(); } }) .start(); // Money从400w滚动到480w const moneyLabel = n.getChildByPath("settlementNode/Node/money").getComponent(Label); let money = { value: 420 }; tween(money) .to(1, { value: 446 }, { easing: 'linear', onUpdate: () => { moneyLabel.string = Math.floor(money.value) + "w"; } }) .start(); let isWin = this.localPlayerNode.getComponent(WoodenManPlayer).isPassed(); n.getChildByPath("settlementNode/btnOK/label").getComponent(Label).string = isWin ? "NEXT" : "QUIT"; n.getChildByPath("settlementNode/btnOK").on(Node.EventType.TOUCH_START, () => { if (isWin) { SceneDef.Next_Scene = "level2/scene/level2Scene"; // 切换到加载场景 director.loadScene('LoadingScene'); // director.loadScene("level2/scene/level2Scene"); } else { director.loadScene("lobby_arean"); } }); } }); }, 3); console.log(`游戏结束! 原因:${reason} 晋级人数:${qualifiedPlayers} 淘汰人数:${deadPlayers} 剩余时间:${this.gameTimer}秒`); // 触发游戏结束事件 this.node.emit('gameOver'); // 隐藏停止按钮 if (this.stopButton) { this.stopButton.active = false; } } // 添加新方法:击杀所有未晋级的存活玩家 private killAllNonQualifiedPlayers() { this.playerNodes.forEach((playerNode) => { const playerComp = playerNode.getComponent(WoodenManPlayer); if (playerComp && playerComp.isAlive() && !playerComp.isPassed()) { this.killPlayer(playerComp, playerNode); } }); } // 获取游戏状态 // 木头人转身动画 private playDollTurnAnimation(callback?: () => void) { const currentRotation = this.dollNode.eulerAngles.clone(); const targetRotation = new Vec3( currentRotation.x, currentRotation.y + 180, currentRotation.z ); this.isDollTurning = true; this.turnStartTime = Date.now(); tween(this.dollNode) .to(this.turnAnimationDuration, { eulerAngles: targetRotation }, { easing: 'quadOut', onStart: () => { this.node.parent?.emit('dollTurnStart'); }, onComplete: () => { this.isDollTurning = false; this.node.parent?.emit('dollTurnComplete'); if (callback) callback(); } }) .start(); } // 玩家死亡效果 private killPlayer(playerComp: WoodenManPlayer, playerNode: Node) { if (!playerComp.isAlive()) return; // 防止重复处理 // 先设置玩家状态为死亡 playerComp.setAlive(false); playerComp.isMoving = false; // 确保停止所有正在进行的动画和移动 tween(playerNode).stop(); // 延迟一小段时间再播放死亡动画,避免动作冲突 this.scheduleOnce(() => { // 播放死亡动画 playerComp.playDieAnimation(); // 更新存活人数显示 this.updateAliveCount(); // 如果是本地玩家死亡,隐藏停止按钮 if (playerComp.getIsLocalPlayer() && this.stopButton) { this.stopButton.active = false; } }, 0.1); } // 更新游戏时间 private updateGameTimer() { if (this.isGameOver) return; this.gameTimer--; if (this.gameTimer <= 0) { this.endGame('时间到!游戏结束'); } } // 更新晋级名额显示 updateWinCount() { if (this.winCountLabel) { const displayCount = Math.min(this.qualifiedCount, this.REQUIRED_QUALIFIED); this.winCountLabel.string = `${displayCount}/${this.REQUIRED_QUALIFIED}`; } } // 修改警示灯动画方法 private playWarningLights(callback?: () => void) { // 生成随机加速倍率 (0.5~1倍) const actualInterval = this.WARNING_LIGHT_BASE_INTERVAL; // 先重置所有灯为初始状态 this.warningLightsNode.children.forEach(light => { light.getComponent(Sprite).color = new Color(128, 128, 128, 255); }); // 依次点亮每个灯 let currentLight = 0; const lightInterval = () => { if (currentLight >= this.warningLightsNode.children.length) { if (callback) callback(); return; } // 点亮当前灯并播放音效 const light = this.warningLightsNode.children[currentLight]; light.getComponent(Sprite).color = new Color(255, 0, 0, 255); currentLight++; this.scheduleOnce(() => { lightInterval(); }, actualInterval); } // 开始点亮第一个灯 lightInterval(); } }