import { _decorator, Component, Node, SkeletalAnimation, Input, EventTouch, input, Label, Button, Vec3, tween, Animation, Prefab, instantiate } from 'cc'; import { WoodenManGame } from './WoodenManGame'; import { CameraManager } from './CameraManager'; import { resLoader } from 'db://assets/core_tgx/base/utils/ResLoader'; import { ModuleDef } from 'db://assets/scripts/ModuleDef'; import { tgxAudioMgr } from 'db://assets/core_tgx/tgx'; import { EMusicDefine } from 'db://assets/module_basic/scripts/MusicDefine'; const { ccclass, property } = _decorator; /** 玩家状态 */ enum EPlayerState { IDLE, WALK, RUN, DIE, VICTORY } @ccclass('WoodenManPlayer') export class WoodenManPlayer extends Component { private id: number = 0; private game: WoodenManGame; private alive: boolean = true; private passed: boolean = false; isLocalPlayer: boolean = false; private animator: SkeletalAnimation = null; public isMoving: boolean = false; private hasPlayedDieAnimation: boolean = false; private moveTimer: number = 0; // 添加移动计时器 private readonly RUN_SPEED = 6; // 跑步速度 private _playerState: EPlayerState = EPlayerState.IDLE; private isButtonPressed: boolean = false; @property(CameraManager) private cameraManager: CameraManager = null; init(id: number, isLocal: boolean, game: WoodenManGame) { this.id = id; this.game = game; this.isLocalPlayer = isLocal; this.animator = this.getComponentInChildren(SkeletalAnimation); if (!this.animator) { console.error(`Player ${id} 缺少 SkeletalAnimation 组件`); } else { const idleState = this.animator.getState('player_idle1'); if (idleState && this._playerState === EPlayerState.IDLE) { this.animator.crossFade('player_idle1', 0.3); } } } getId(): number { return this.id; } isAlive(): boolean { return this.alive; } isPassed(): boolean { return this.passed; } setAlive(alive: boolean) { if (this.alive === alive) return; this.alive = alive; if (!alive) { // 确保停止所有运动和动画 this.isMoving = false; this.isButtonPressed = false; // 停止当前节点的 tween tween(this.node).stop(); // 重置状态 this._playerState = EPlayerState.DIE; // 切换到死亡摄像机 if (this.isLocalPlayer && this.cameraManager) { this.cameraManager.switchToDeadCamera(this.node.worldPosition); } // 播放死亡 GUI 动画 if (this.isLocalPlayer) { resLoader.load(ModuleDef.Arean, 'common/prefabs/settlement', (err: Error | null, prefab: Prefab) => { if (!err && prefab) { const n = instantiate(prefab); this.game.guiNode.addChild(n); n.getComponent(Animation).play("showLose"); } }); } } } setPassed(value: boolean) { this.passed = value; } // 播放死亡动画 playDieAnimation() { if (this.hasPlayedDieAnimation) return; this.hasPlayedDieAnimation = true; // 播放音效 tgxAudioMgr.inst.playOneShot(EMusicDefine.GUN, 1, ModuleDef.BASIC); tgxAudioMgr.inst.playOneShot(EMusicDefine.WOODENMANDIE, 1, ModuleDef.BASIC); // 加载并播放死亡特效 resLoader.load(ModuleDef.Arean, 'common/prefabs/hit001', (err: Error | null, prefab: Prefab) => { if (!err && prefab) { const effectNode = instantiate(prefab); this.node.addChild(effectNode); effectNode.setPosition(new Vec3(0, 1, 0)); // 稍微抬高一点,让特效更明显 } }); if (this._getAnimator()) { // 转向动画 const state = this.animator.getState('player_die'); if (state) { state.wrapMode = 1; // 1 表示只播放一次 this.animator.crossFade('player_die', 0.3); // 动画结束后确保停止 state.once(SkeletalAnimation.EventType.FINISHED, () => { this.animator.stop(); }); } } } // 播放胜利动画 playVictoryAnimation() { // if (this.animator) { // // 胜利动画带过渡 // this.animator.crossFade('victory', 0.5); // } } // 播放行走动画 playWalkAnimation() { if (this._getAnimator()) { const state = this.animator.getState('player_walk'); if (state) { this._playerState = EPlayerState.WALK; state.wrapMode = 2; // 2 表示循环模式 //this.animator.play('player_walk'); this.animator.crossFade('player_walk', 0.3); } } } // 播放跑步动画 playRunAnimation() { if (this._getAnimator()) { const state = this.animator.getState('player_run'); if (state) { this._playerState = EPlayerState.RUN; state.wrapMode = 2; // 2 表示循环模式 //this.animator.play('player_run'); this.animator.crossFade('player_run', 0.3); } } } // 停止移动c stopMoving() { this.isMoving = false; if (this._getAnimator()) { const game = this.game as WoodenManGame; if (game.getIsDollTurning()) { // 如果娃娃正在转身,只改变动画速度 const runState = this.animator.getState('player_run'); if (runState?.isPlaying) { runState.speed = 0; } else { // 如果跑步动画没有播放,可能需要先播放再暂停 this.animator.crossFade('player_run', 0.1); this.scheduleOnce(() => { const state = this.animator.getState('player_run'); if (state) state.speed = 0; }, 0.1); } } else { // 如果娃娃还没开始转身,切换到待机动画 this.animator.crossFade('player_idle1', 0.2); } } } // 添加 setter 方法 setButtonPressed(pressed: boolean) { this.isButtonPressed = pressed; } private _getAnimator() { if (!this.animator) { this.animator = this.getComponentInChildren(SkeletalAnimation); if (!this.animator) { console.error(`Player ${this.id} 缺少 SkeletalAnimation 组件`); } } return this.animator; } public startMoving() { if (!this.isAlive() || this.isPassed()) return; this.isMoving = true; if (this._getAnimator()) { const game = this.game as WoodenManGame; const runState = this.animator.getState('player_run'); runState.speed = 1; if (game.getIsDollTurning()) { // 如果娃娃正在转身,需要确保动画正确播放 this.animator.crossFade('player_run', 0.1); } else { // 如果娃娃还没开始转身,正常播放跑步动画 this.playRunAnimation(); } this._playerState = EPlayerState.RUN; } } update(dt: number) { if (!this.game.getGameStarted()) return; if (this.isMoving) { this.moveTimer += dt; // 直接使用跑步速度 const newPos = this.node.position.clone(); newPos.z -= this.RUN_SPEED * dt; this.node.setPosition(newPos); if (this.node.position.z <= -49.5) { this.handleFinishLine(); } } } // 添加处理终点线的方法 private handleFinishLine() { if (!this.isPassed()) { this.game.qualifiedCount++; this.setPassed(true); // 同时旋转角色和摄像机 const duration = 1.0; // 转身动画持续时间 // 角色转身动画 tween(this.node) .to(duration, { eulerAngles: new Vec3(0, 0, 0) }, { easing: 'quadOut' }) .start(); // 如果是本地玩家,执行摄像机旋转 if (this.isLocalPlayer && this.cameraManager) { const camera = this.cameraManager.defaultCamera; if (camera) { // 保存摄像机初始位置 const startPos = camera.position.clone(); const centerPos = this.node.position.clone(); centerPos.y += 1.5; // 调整旋转中心点高度 // 计算摄像机到角色的距离和高度差 const radius = Vec3.distance( new Vec3(startPos.x, centerPos.y, startPos.z), centerPos ); const heightOffset = startPos.y - centerPos.y; // 执行摄像机环绕动画 tween(camera) .to(duration, { position: new Vec3( centerPos.x, centerPos.y + heightOffset, centerPos.z - radius ) }, { easing: 'quadOut', onUpdate: (target: Node, ratio: number) => { // 计算当前角度 const angle = Math.PI * ratio; const x = centerPos.x + Math.sin(angle) * radius; const z = centerPos.z + Math.cos(angle) * radius; // 更新摄像机位置 target.setPosition(x, centerPos.y + heightOffset, z); // 始终看向角色 target.lookAt(centerPos); } }) .start(); } } // 播放晋级动画 if (this.isLocalPlayer && this.game.guiNode) { resLoader.load(ModuleDef.Arean, 'common/prefabs/settlement', (err: Error | null, prefab: Prefab) => { if (!err && prefab) { const n = instantiate(prefab); this.game.guiNode.addChild(n); n.getComponent(Animation).play("showWin"); } }); } this.game.updateWinCount(); if (this.game.qualifiedCount >= this.game.REQUIRED_QUALIFIED) { this.game.endGame('达到晋级人数要求!游戏结束'); } } // 停止移动并播放待机动画 this.stopMoving(); if (this._getAnimator()) { const idleState = this.animator.getState('player_idle1'); if (idleState) { idleState.speed = 1; this.animator.crossFade('player_idle1', 0.3); } } } // 添加 getter 方法 public getIsLocalPlayer(): boolean { return this.isLocalPlayer; } // 添加一个方法来检查玩家是否应该被击杀 public shouldBeKilled(): boolean { // 如果玩家已经停止移动,就不应该被击杀 if (!this.isMoving) return false; // 如果玩家按住了按钮,也不应该被击杀 if (this.isButtonPressed) return false; // 如果玩家的动画速度为0(已经停止),也不应该被击杀 if (this._getAnimator()) { const runState = this.animator.getState('player_run'); if (runState?.isPlaying && runState.speed === 0) { return false; } } // 其他情况下,如果在移动就应该被击杀 return true; } // 修改清理方法 public cleanup() { // 停止所有定时器 this.unscheduleAllCallbacks(); // 停止所有 tween if (this.node) { tween(this.node).stop(); } // 停止动画 if (this.animator && this.animator.isValid) { try { this.animator.stop(); } catch (error) { console.warn('Failed to stop animator:', error); } } // 重置状态 this.isMoving = false; this.isButtonPressed = false; this.hasPlayedDieAnimation = false; } onDestroy() { try { this.cleanup(); } catch (error) { console.warn('Error during WoodenManPlayer cleanup:', error); } } }