feat(heroList): 升级按钮交互与提示优化

1.  新增升级按钮点击回弹动画与自动绑定逻辑
2.  替换按钮禁用逻辑为气泡提示反馈
3.  重构smalltip预制体并集成到升级按钮
4.  添加完整的调试日志与异常处理
This commit is contained in:
pan
2026-08-24 15:13:13 +08:00
parent bb098d690b
commit f4c5f0daae
4 changed files with 1174 additions and 16622 deletions

View File

@@ -17,7 +17,7 @@
* - CardLiteComp —— 轻量卡片组件
* - buildSkillDesc(HeroSkillDesc)—— 技能描述生成器
*/
import { _decorator, Animation, AnimationClip, Label, Node, Prefab, Sprite, UITransform, Widget, instantiate, resources } from "cc";
import { _decorator, Animation, AnimationClip, Button, Label, Node, Prefab, Sprite, UITransform, Vec3, Widget, instantiate, resources, tween, Tween } 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 "db://oops-framework/core/Oops";
@@ -54,8 +54,8 @@ export class HerosListComp extends CCComp {
/** 卡牌背景底框节点(按卡池等级切换子节点显示) */
@property(Node)
BG_node=null!
BG_node = null!
@property(Node)
info_node = null!
@@ -75,7 +75,7 @@ export class HerosListComp extends CCComp {
@property(Node)
type_node = null!
/** 升级按钮节点 */
/** 升级按钮节点(smalltip 气泡已作为其子节点挂在下面) */
@property(Node)
upgrade_btn = null!
@@ -95,12 +95,45 @@ export class HerosListComp extends CCComp {
debugMode: boolean = false
start() {
this.bindUpgradeBtn()
this.initCardList()
if (HeroList.length > 0) {
this.onCardSelect(HeroList[0])
}
}
protected onLoad(): void {
// onLoad 阶段属性可能尚未绑定,延迟到 start 再处理
}
/**
* 绑定升级按钮点击事件
* 在 start 中调用,确保 @property 属性已正确赋值
*/
private bindUpgradeBtn() {
mLogger.log(this.debugMode, "HerosListComp", "bindUpgradeBtn", {
hasUpgradeBtn: !!this.upgrade_btn,
upgradeBtnName: this.upgrade_btn?.name,
upgradeBtnActive: this.upgrade_btn?.active,
upgradeBtnValid: this.upgrade_btn?.isValid,
})
if (!this.upgrade_btn) {
mLogger.error(this.debugMode, "HerosListComp", "upgrade_btn 未绑定,请检查编辑器属性面板")
return
}
// 确保 smalltip 气泡初始隐藏,避免遮挡按钮触摸
const tipNode = this.upgrade_btn.getChildByName("smalltip")
if (tipNode) tipNode.active = false
let btn = this.upgrade_btn.getComponent(Button)
if (!btn) btn = this.upgrade_btn.addComponent(Button)
btn.interactable = true
this.upgrade_btn.on(Button.EventType.CLICK, this.onUpgradeClick, this)
mLogger.log(this.debugMode, "HerosListComp", "升级按钮点击事件已绑定")
}
protected onEnable(): void {
oops.message.on(GameEvent.HERO_GROWTH_UPDATE, this.onGrowthUpdate, this)
if (this.cards_node && this.cards_node.children.length > 0) {
@@ -127,9 +160,14 @@ export class HerosListComp extends CCComp {
* 消耗碎片提升英雄局外等级,成功后刷新详情面板
*/
onUpgradeClick() {
mLogger.log(this.debugMode, "HerosListComp", "onUpgradeClick 被触发", { huuid: this.huuid })
const uuid = this.huuid
if (!uuid) return
// 点击动画:按压回弹
this.playUpgradeBtnAnim()
const success = smc.upgradeHero(uuid)
if (success) {
mLogger.log(this.debugMode, "HerosListComp", `英雄 ${uuid} 升级成功`)
@@ -142,9 +180,64 @@ export class HerosListComp extends CCComp {
fragments: growth.fragments,
needFragments: need ?? "MAX",
})
// 碎片不足 / 已满级:气泡提示
const tipText = need === undefined ? "已满级" : "碎片不足"
this.showSmallTip(tipText)
}
}
/**
* 播放升级按钮按压回弹动画
*/
private playUpgradeBtnAnim() {
if (!this.upgrade_btn) {
mLogger.log(this.debugMode, "HerosListComp", "playUpgradeBtnAnim: upgrade_btn 为空")
return
}
mLogger.log(this.debugMode, "HerosListComp", "播放按钮动画")
Tween.stopAllByTarget(this.upgrade_btn)
this.upgrade_btn.setScale(1, 1, 1)
tween(this.upgrade_btn)
.to(0.08, { scale: new Vec3(0.9, 0.9, 1) }, { easing: 'quadOut' })
.to(0.12, { scale: new Vec3(1.1, 1.1, 1) }, { easing: 'quadOut' })
.to(0.08, { scale: new Vec3(1, 1, 1) }, { easing: 'quadIn' })
.start()
}
/**
* 在升级按钮附近弹出 smalltip 气泡提示
* @param text 提示文案
*/
private showSmallTip(text: string) {
// smalltip 作为 upgrade_btn 的子节点挂在下面
const tipNode = this.upgrade_btn?.getChildByName("smalltip")
if (!tipNode || !tipNode.isValid) {
mLogger.log(this.debugMode, "HerosListComp", `未在 upgrade_btn 下找到 smalltip 节点,无法提示:${text}`)
return
}
// 覆盖气泡文字
const label = tipNode.getComponentInChildren(Label)
if (label) label.string = text
tipNode.active = true
Tween.stopAllByTarget(tipNode)
tipNode.setScale(0, 0, 1)
tween(tipNode)
// 弹出动画(微放大再回弹)
.to(0.15, { scale: new Vec3(1.1, 1.1, 1) }, { easing: 'quadOut' })
.to(0.05, { scale: new Vec3(1, 1, 1) })
// 停留 1 秒
.delay(1)
// 缩小消失
.to(0.15, { scale: new Vec3(0, 0, 1) }, { easing: 'quadIn' })
.call(() => {
if (tipNode && tipNode.isValid) tipNode.active = false
})
.start()
}
// ======================== 卡片列表 ========================
private initCardList() {
@@ -326,12 +419,12 @@ export class HerosListComp extends CCComp {
chipComp.setHeroByUuid(uuid)
}
// 升级按钮状态:未满级且碎片足够时可用
// 升级按钮状态:始终可点击,点击后由 onUpgradeClick 判断碎片是否足够
// 碎片不足时按钮保持可点,通过气泡提示用户(而非禁用按钮)
if (this.upgrade_btn) {
const canUpgrade = !isMaxLv && growth.fragments >= needFragments
this.upgrade_btn.active = true
const btnComp = this.upgrade_btn.getComponent("cc.Button") as import("cc").Button | null
if (btnComp) btnComp.interactable = canUpgrade
if (btnComp) btnComp.interactable = true
}
}