1. 替换原有的角度抖动动画为缩放脉动效果,调整了动画时长与参数 2. 统一重构了MissionCardComp和MissionHomeComp中的指引相关代码 3. 修正了代码中的空格、命名等格式问题
274 lines
9.0 KiB
TypeScript
274 lines
9.0 KiB
TypeScript
/**
|
||
* @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();
|
||
}
|
||
} |