import { _decorator, animation, Animation, AnimationComponent, Button, Color, Component, EventTouch, HorizontalTextAlignment, Input, input, instantiate, Label, math, Node, Prefab, randomRange, randomRangeInt, screen, Screen, tween, UITransform, Vec2, Vec3, VerticalTextAlignment, view } from 'cc'; import { PanelBase } from '../framework/ui/PanelBase'; import { FPSCameraController } from '../FPSCameraController'; import { GameController } from '../GameController'; import { TargetIconItem } from './TargetIconItem'; import { EventCenter } from '../EventCenter'; import { UIManager } from '../framework/ui/UIManager'; import { QingBaoPanel } from './QingBaoPanel'; import { SettingPanel } from './SettingPanel'; import { SdkMgr } from '../framework/sdk/SdkMgr'; import { AudioMgr } from '../framework/audio/AudioMgr'; import { AssetController } from '../AssetController'; import { Ragdoll } from '../../Ragdoll/Ragdoll'; import { LocalSaveMgr } from '../framework/data/LocalSaveMgr'; import { LevelState } from '../EnumFile'; const { ccclass, property } = _decorator; @ccclass('PlayPanel') export class PlayPanel extends PanelBase { static instance: PlayPanel = null @property(Node) moveNode: Node = null; // 相机节点 @property(Node) saoMiaoTip: Node = null; // 相机节点 @property(Node) aimContent: Node = null; @property(Node) closeAim: Node = null; @property(Node) cancelLabel: Node = null; @property(Prefab) targetIconPrefab: Prefab = null; @property(Node) targetIconParent: Node = null; @property(Button) anim: Button = null; @property(Button) qingBao: Button = null; @property(Label) timerLabel: Label = null; @property(Button) saoMiao: Button = null; @property(Button) setting: Button = null; @property(Label) levelIndexLabel: Label = null; @property(Button) fire: Button = null; @property(Node) finger: Node = null; @property(Node) qingBaoResultNode: Node = null; @property(Button) qingBaoResultBtn: Button = null; @property(AnimationComponent) alienAnim: AnimationComponent = null; @property(Label) tutorialLabel: Label = null; @property(Node) tutorialLabelNode: Node = null; @property(Node) targetNode: Node = null; @property(Node) tipNode1: Node = null; @property(Node) tipNode2: Node = null; @property(Node) tipNode3: Node = null; @property([Animation]) shootAnimations: Animation[] = []; targetIcons: Array = []; onLoad() { PlayPanel.instance = this // Register the touch start event this.moveNode.on(Node.EventType.TOUCH_START, this.onTouchStart, this); this.moveNode.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this); this.moveNode.on(Node.EventType.TOUCH_END, this.onTouchEnd, this); this.anim.node.on(Button.EventType.CLICK, this.onClickAim, this); this.fire.node.on(Button.EventType.CLICK, this.onClickFire, this); this.qingBao.node.on(Button.EventType.CLICK, this.onClickQingBao, this); this.qingBaoResultBtn.node.on(Button.EventType.CLICK, this.onClickQingBaoResult, this); this.saoMiao.node.on(Button.EventType.CLICK, this.onClickSaoMiao, this); this.setting.node.on(Button.EventType.CLICK, this.onClickSetting, this); this.createDeveloperButtons() EventCenter.onEnemyDie.on(null, this.refreshTargetCount).bind(this) } /** 为测试关卡提供快捷操作,仅显示文字,避免与正式功能图标混淆。 */ private createDeveloperButtons() { this.createDeveloperButton('DEV_KillEnemies', '开发:杀死敌人', new Vec3(350, 830, 0), this.onClickDevKillEnemies) this.createDeveloperButton('DEV_CompleteLevel', '开发:完成过关', new Vec3(350, 760, 0), this.onClickDevCompleteLevel) } private createDeveloperButton(name: string, text: string, position: Vec3, callback: () => void) { const buttonNode = new Node(name) buttonNode.name = name buttonNode.layer = this.setting.node.layer buttonNode.setParent(this.setting.node.parent) buttonNode.setPosition(position) buttonNode.addComponent(UITransform).setContentSize(220, 60) const button = buttonNode.addComponent(Button) const labelNode = new Node('Label') labelNode.layer = buttonNode.layer labelNode.setParent(buttonNode) labelNode.addComponent(UITransform).setContentSize(220, 60) const label = labelNode.addComponent(Label) label.string = text label.fontSize = 24 label.lineHeight = 32 label.color = new Color(255, 220, 90, 255) label.horizontalAlign = HorizontalTextAlignment.CENTER label.verticalAlign = VerticalTextAlignment.CENTER button.node.on(Button.EventType.CLICK, callback, this) } private onClickDevKillEnemies() { const level = GameController.getInstance().curLevel if (level.levelState !== LevelState.Running) return AudioMgr.inst.playOneShot(AssetController.instance.click) for (const enemy of level.enemies) { if (!enemy.isDie) { enemy.setEnabled(true) } } } private onClickDevCompleteLevel() { const level = GameController.getInstance().curLevel if (level.levelState !== LevelState.Running) return AudioMgr.inst.playOneShot(AssetController.instance.click) level.win() } onClickSetting() { AudioMgr.inst.playOneShot(AssetController.instance.click) UIManager.instance.openPanel(SettingPanel) } showTimer() { let totalSeconds = GameController.getInstance().curLevel.timer totalSeconds = Math.ceil(totalSeconds); let minutes = Math.floor(totalSeconds / 60); let seconds = totalSeconds % 60; // 将分钟和秒格式化为两位数 let formattedMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`; let formattedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`; // 组合成 00:00 的格式 let formattedTime = `${formattedMinutes}:${formattedSeconds}`; this.timerLabel.string = formattedTime } onClickQingBao() { AudioMgr.inst.playOneShot(AssetController.instance.click) SdkMgr.instance.requestReward(() => { UIManager.instance.openPanel(QingBaoPanel) this.closeTip() this.qingBaoResultNode.active = true this.qingBao.node.active = false }) } onClickQingBaoResult() { AudioMgr.inst.playOneShot(AssetController.instance.click) UIManager.instance.openPanel(QingBaoPanel) this.closeTip() } isOpenSaomiao: boolean = false offset: Vec2 onClickSaoMiao() { AudioMgr.inst.playOneShot(AssetController.instance.click) SdkMgr.instance.requestReward(() => { this.saoMiaoTip.active = true GameController.getInstance().curLevel.SetSaoMiaoTarget() this.offset = new Vec2(randomRange(-200, 200), randomRange(-200, 200)) this.updateSaomiao() this.isOpenSaomiao = true this.closeTip() }) } tipTimer = 0; onTip: boolean = false protected update(dt: number): void { this.showTimer() if (!this.onStep && !this.onTip) this.tipTimer += dt if (!this.onTip && this.tipTimer > 30) { this.startTip() } } startTip() { this.onTip = true AudioMgr.inst.playOneShot(AssetController.instance.xiao) this.tipTimer = 0 this.tipNode1.active = true this.tipNode2.active = true this.tipNode3.active = true this.alienAnim.play() } closeTip() { this.onTip = false this.tipTimer = 0 this.tipNode1.active = false this.tipNode2.active = false this.tipNode3.active = false } updateTargetPos() { let target = this.tutorialTarget if (!target) return; if (target.isDie) { this.saoMiaoTip.active = false this.isOpenSaomiao = false } // 获取 3D 物体的世界坐标 let worldPos = target.centerNode.getWorldPosition(); // 获取主摄像机 let camera = FPSCameraController.getInstance().camera; // 将世界坐标转换为屏幕坐标 let uiPos = camera.convertToUINode(worldPos, this.node); // 设置 UI 节点的位置 this.targetNode.position = uiPos; if (this.targetNode.position.y < 0 && Math.abs(this.targetNode.position.x) < 30) { this.EnterStep3() FPSCameraController.getInstance().setTutorialRotate() } } protected lateUpdate(dt: number): void { this.updateSaomiao() this.updateTargetPos() // this.updateQingBao() } // updateQingBao() { // if (this.qingBaoResultNode.active) { // if (!GameController.getInstance().curLevel.haveQingBaoTarget()) { // this.switchAim(this.isOnAim) // } // } // } updateSaomiao() { let target = GameController.getInstance().curLevel.saoMiaoTarget if (!target) return; if (target.isDie) { this.saoMiaoTip.active = false this.isOpenSaomiao = false } if (!this.isOpenSaomiao) return // 获取 3D 物体的世界坐标 let worldPos = target.centerNode.getWorldPosition(); // 获取主摄像机 let camera = FPSCameraController.getInstance().camera; // 将世界坐标转换为屏幕坐标 let uiPos = camera.convertToUINode(worldPos, this.node); // 设置 UI 节点的位置 // console.log(uiPos) uiPos = new Vec3(uiPos.x + this.offset.x, uiPos.y + this.offset.y, 0) this.saoMiaoTip.position = uiPos; } tutorialTarget: Ragdoll protected onOpen(): void { PlayPanel.instance.switchAim(false) PlayPanel.instance.initEnemies() this.levelIndexLabel.string = `关卡 ${GameController.getInstance().curLevel.levelIndex}` if (GameController.getInstance().curLevel.levelIndex == 1 && !LocalSaveMgr.instance.playerData.haveTutorial) { this.onStep = true } if (this.onStep) { this.EnterStep1() this.tutorialTarget = GameController.getInstance().curLevel.getTutorialAliveEnemey() console.log("Player panel", this.tutorialTarget) } else { this.targetNode.active = false this.finger.active = false this.tutorialLabelNode.active = false } this.qingBaoResultNode.active = false } isOnAim: boolean = false switchAim(onAim: boolean) { this.anim.node.active = !onAim this.fire.node.active = onAim this.isOnAim = onAim this.cancelLabel.active = onAim this.qingBao.node.active = !onAim && !GameController.getInstance().curLevel.haveQingBaoTarget() this.qingBaoResultNode.active = !onAim && GameController.getInstance().curLevel.haveQingBaoTarget() this.saoMiao.node.active = !onAim this.aimContent.active = onAim this.closeAim.active = !onAim GameController.getInstance().curLevel.gunNode.active = !onAim } refreshTargetCount() { let self = PlayPanel.instance let aliveCount = GameController.getInstance().curLevel.aliveCount for (let index = 0; index < self.targetIcons.length; index++) { if (index < aliveCount) { self.targetIcons[index].switchActive(true) } else { self.targetIcons[index].switchActive(false) } } } initEnemies() { let count = GameController.getInstance().curLevel.enemies.length console.log("目标外星人个数", count) for (let index = 0; index < count; index++) { var item = instantiate(this.targetIconPrefab) item.parent = this.targetIconParent let iconItem = item.getComponent(TargetIconItem) this.targetIcons.push(iconItem) iconItem.switchActive(true) } } onClickAim() { AudioMgr.inst.playOneShot(AssetController.instance.click) FPSCameraController.getInstance().onAimButtonClick(true) this.switchAim(true) if (this.onStep) { this.EnterStep2() } } limitFire: boolean = false onClickFire() { if (this.limitFire) return if (this.onStep) { this.finishTutorial() } this.closeTip() AudioMgr.inst.playOneShot(AssetController.instance.fire) let result = FPSCameraController.getInstance().shoot() if (result) { let effect = this.shootAnimations[randomRangeInt(0, this.shootAnimations.length)] effect.node.active = true effect.play() this.scheduleOnce(() => { effect.node.active = false }, 0.8) } let self = this self.limitFire = true tween(this.aimContent) .to(0.05, { scale: new Vec3(1, 1, 1), position: new Vec3(0, 120, 0) }) .to(0.05, { scale: new Vec3(0.8, 0.8, 1), position: new Vec3(0, 0, 0) }) .call(() => { self.limitFire = false }) .start() } downTime: Date startTouchPos: Vec2 = null onTouchStart(event: EventTouch) { FPSCameraController.getInstance().onTouchStart(event) this.downTime = new Date(); this.startTouchPos = event.getLocation() } onTouchMove(event: EventTouch) { FPSCameraController.getInstance().onTouchMove(event) } onTouchEnd(event: EventTouch) { if (this.isOnAim) { let now = new Date(); var offset = now.getTime() - this.downTime.getTime() let moveDetal = Math.abs(this.startTouchPos.x - event.getLocation().x) + Math.abs(this.startTouchPos.y - event.getLocation().y) // console.log("offset", offset, moveDetal) if (!this.onStep) if (offset < 200 && moveDetal < 20) { FPSCameraController.getInstance().onAimButtonClick(false) this.switchAim(false) } } } EnterStep1() { this.targetNode.active = true this.finger.active = true this.tutorialLabel.string = "点击瞄准按钮" this.moveNode.active = false this.tutorialLabelNode.active = true } onStep: boolean = false EnterStep2() { this.finger.active = false this.tutorialLabel.string = "滑动屏幕,瞄准目标" this.moveNode.active = true this.fire.node.active = false this.cancelLabel.active = false } EnterStep3() { this.fire.node.active = true this.finger.active = true this.tutorialLabel.string = "点击开火按钮" this.moveNode.active = false this.cancelLabel.active = false } finishTutorial() { this.targetNode.active = false this.finger.active = false this.tutorialLabelNode.active = false this.tutorialLabel.string = "点击开火按钮" this.moveNode.active = true this.onStep = false this.tutorialTarget = null LocalSaveMgr.instance.completeTutorial() this.cancelLabel.active = true } protected onDestroy(): void { PlayPanel.instance = null EventCenter.onEnemyDie.off(null, this.refreshTargetCount) } }