/** * @file MissionHomeComp.ts * @description 任务主页组件(UI 视图层) * * 职责: * 1. 作为玩家进入游戏后第一个看到的界面(主菜单/大厅)。 * 2. 提供"开始任务"按钮,触发 GameEvent.MissionStart 进入战斗。 * 3. 提供"排行榜"按钮,打开 RanksComp 弹窗。 * 4. 监听 MissionEnd 事件,任务结束后自动切回主页。 * * 关键设计: * - start_mission() 分发 MissionStart 事件并隐藏自身节点。 * - mission_end() 响应后重新显示主页。 * - isWxClient() 检测是否运行在微信小游戏环境。 * * 依赖: * - GameEvent.MissionStart / MissionEnd —— 游戏生命周期事件 * - UIID.Mission —— 战斗界面 ID */ import { _decorator, instantiate, Label, Prefab, resources, Sprite, SpriteAtlas, Tween, tween, UITransform, Vec3, Node } from "cc"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { smc } from "../common/SingletonModuleComp"; import { GameEvent } from "../common/config/GameEvent"; import { Timer } from "db://oops-framework/core/common/timer/Timer"; import { mLogger } from "../common/Logger"; import { UIID } from "../common/config/GameUIConfig"; const { ccclass, property } = _decorator; /** * MissionHomeComp —— 任务主页视图组件 * * 游戏大厅界面,提供开始战斗和查看排行的入口。 */ @ccclass('MissionHomeComp') @ecs.register('MissionHome', false) export class MissionHomeComp extends CCComp { /** 调试日志开关 */ debugMode: boolean = false; /** 开始游戏按钮节点(预留) */ @property(Node) start_btn = null! /** 主页按钮节点(预留) */ @property(Node) home_btn = null! /** 英雄图鉴按钮节点(预留) */ @property(Node) hero_btn = null! /** 排行榜按钮节点 */ @property(Node) rank_btn = null! /** 天赋按钮节点 */ @property(Node) tals_btn = null! @property(Node) heros_page = null! @property(Node) talents_page = null! @property(Node) ranks_page = null! // ======================== 新手指引(开始按钮缩放脉动 + 气泡) ======================== /** 脉动放大倍率 */ private static readonly PULSE_SCALE: number = 1.15; /** 气泡文案 */ private static readonly TIP_TEXT: string = "点击开始游戏"; /** 气泡弹出/停留/收起节奏(秒) */ private static readonly TIP_STAY: number = 3.4; /** 指引进行中标记(避免重复启动) */ private guiding: boolean = false; /** 气泡原文缓存(指引结束后还原) */ private guideTipOriginText: string = ""; /** 注册任务结束事件 */ protected onLoad(): void { this.on(GameEvent.MissionEnd, this.mission_end, this) } /** 启动时显示主页 */ start() { this.home_active() // 新玩家:start_btn 缩放脉动 + smalltip 气泡引导点击开始(仅 1 次) if (!smc.data.tip_done.start) { this.playStartGuide(); } } onEnable() { } update(dt: number) { } /** * 开始任务: * 1. 打印日志。 * 2. 分发 MissionStart 事件,驱动 MissionComp / MissionCardComp 初始化战斗。 * 3. 隐藏主页节点。 * 4. 隐藏 mapLayer 下的 main 节点。 */ start_mission() { mLogger.log(this.debugMode, 'MissionHomeComp', "start_mission") // 点击开始后完成指引:停止缩放脉动/气泡并记录完成标记 this.stopStartGuide(); smc.data.tip_done.start = true; oops.gui.open(UIID.Mission) this.node.active = false; // 隐藏 mapLayer 下的 main 节点 } // ======================== 新手指引实现 ======================== /** 启动指引:button 子节点循环缩放脉动 + smalltip 气泡弹出 */ private playStartGuide() { const btn = this.start_btn; if (!btn || !btn.isValid) return; if (this.guiding) return; this.guiding = true; // start_btn 下名为 "Button" 的子节点循环缩放脉动(scale 往复,不触碰 position,避免与布局冲突) const pulseNode = this.getPulseNode(); if (pulseNode) { Tween.stopAllByTarget(pulseNode); pulseNode.setScale(1, 1, 1); tween(pulseNode) .repeatForever( tween(pulseNode) .to(0.3, { scale: new Vec3(MissionHomeComp.PULSE_SCALE, MissionHomeComp.PULSE_SCALE, 1) }, { easing: 'sineInOut' }) .to(0.3, { scale: new Vec3(1, 1, 1) }, { easing: 'sineInOut' }) ) .start(); } // smalltip 气泡:缓存原文后覆盖文案,弹出后保持显示直至点击 const tip = btn.getChildByName("smalltip"); if (tip) { const label = tip.getComponentInChildren(Label); if (label) { this.guideTipOriginText = label.string; label.string = MissionHomeComp.TIP_TEXT; } tip.active = true; Tween.stopAllByTarget(tip); tip.setScale(0, 0, 1); tween(tip) .to(0.15, { scale: new Vec3(1.1, 1.1, 1) }, { easing: 'quadOut' }) .to(0.05, { scale: new Vec3(1, 1, 1) }) .start(); } } /** 停止指引:脉动复位、气泡收起并还原原文 */ private stopStartGuide() { if (!this.guiding) return; this.guiding = false; const btn = this.start_btn; if (btn && btn.isValid) { const pulseNode = this.getPulseNode(); if (pulseNode && pulseNode.isValid) { Tween.stopAllByTarget(pulseNode); pulseNode.setScale(1, 1, 1); } const tip = btn.getChildByName("smalltip"); if (tip && tip.isValid) { Tween.stopAllByTarget(tip); const label = tip.getComponentInChildren(Label); if (label) label.string = this.guideTipOriginText; tip.active = false; } } } /** * 获取 start_btn 下名为 "Button" 的子节点(即要播放缩放脉动的节点)。 * @returns Button 子节点;未找到返回 null */ private getPulseNode(): Node | null { const btn = this.start_btn; if (!btn || !btn.isValid) return null; const node = btn.getChildByName("Button"); return node && node.isValid ? node : null; } private showPage(page: Node) { this.heros_page.active = (page === this.heros_page) this.talents_page.active = (page === this.talents_page) this.ranks_page.active = (page === this.ranks_page) this.setBarActive(this.hero_btn, page === this.heros_page) this.setBarActive(this.tals_btn, page === this.talents_page) this.setBarActive(this.rank_btn, page === this.ranks_page) this.setBarActive(this.home_btn, false) } private hideAllPages() { this.heros_page.active = false this.talents_page.active = false this.ranks_page.active = false this.setBarActive(this.home_btn, true) this.setBarActive(this.hero_btn, false) this.setBarActive(this.tals_btn, false) this.setBarActive(this.rank_btn, false) } private setBarActive(btn: Node, active: boolean) { if (!btn) return const child = btn.getChildByName("active") if (child) child.active = active } openRanks() { this.showPage(this.ranks_page) } openHero() { this.showPage(this.heros_page) } openTalents() { this.showPage(this.talents_page) } /** 任务结束回调:重新显示主页 */ mission_end() { mLogger.log(this.debugMode, 'MissionHomeComp', "[MissionHomeComp]=>mission_end") this.home_active() } /** 激活主页显示:刷新数据并显示节点 */ home_active() { // 任务结束回到主页:确保指引动画与气泡无残留 this.stopStartGuide(); this.uodate_data() this.hideAllPages() this.node.active = true // 播放主页背景音乐,恢复默认音量 oops.audio.volumeMusic = 1.0; oops.audio.playerMusicLoop("music/home") } /** 更新主页显示数据(预留) */ uodate_data() { } /** * 判断是否运行在微信小游戏环境。 * @returns true = 微信小游戏环境 */ isWxClient() { return typeof wx !== 'undefined' && typeof (wx as any).getSystemInfoSync === 'function'; } /** ECS 组件移除时销毁节点 */ reset() { // 销毁前清理指引 tween,防止回调残留 this.stopStartGuide(); this.node.destroy(); } }