From 4d2e42953b9c45e84f605e3fb75198421dbe6a26 Mon Sep 17 00:00:00 2001 From: panw Date: Mon, 25 May 2026 15:47:54 +0800 Subject: [PATCH] =?UTF-8?q?refactor(map,gui):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=8D=A1=E7=89=8C=E5=92=8C=E8=8B=B1=E9=9B=84=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E9=80=BB=E8=BE=91=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E9=9D=99=E6=80=81=E9=A2=84=E8=A7=88=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 移除卡牌的长按放大逻辑,改为长按直接打开英雄静态预览面板 2. 重构HInfoComp,新增静态预览模式支持,无需绑定实体数据 3. 调整card和hnode预制体的缩放参数 4. 简化卡牌UI的尺寸和位置计算逻辑,移除放大状态的额外处理 --- assets/resources/gui/element/card.prefab | 4 +- assets/resources/gui/element/hnode.prefab | 4 +- assets/script/game/map/CardComp.ts | 189 +++++----------------- assets/script/game/map/HInfoComp.ts | 94 ++++++++++- 4 files changed, 126 insertions(+), 165 deletions(-) diff --git a/assets/resources/gui/element/card.prefab b/assets/resources/gui/element/card.prefab index 790204b9..de344708 100644 --- a/assets/resources/gui/element/card.prefab +++ b/assets/resources/gui/element/card.prefab @@ -2003,8 +2003,8 @@ }, "_lscale": { "__type__": "cc.Vec3", - "x": 1, - "y": 1, + "x": -1.5, + "y": 1.5, "z": 1 }, "_mobility": 0, diff --git a/assets/resources/gui/element/hnode.prefab b/assets/resources/gui/element/hnode.prefab index adeaf7dd..5284a9ea 100644 --- a/assets/resources/gui/element/hnode.prefab +++ b/assets/resources/gui/element/hnode.prefab @@ -2521,8 +2521,8 @@ }, "_lscale": { "__type__": "cc.Vec3", - "x": 1, - "y": 1, + "x": -1.5, + "y": 1.5, "z": 1 }, "_mobility": 0, diff --git a/assets/script/game/map/CardComp.ts b/assets/script/game/map/CardComp.ts index a9273b0e..5ea15ceb 100644 --- a/assets/script/game/map/CardComp.ts +++ b/assets/script/game/map/CardComp.ts @@ -135,10 +135,6 @@ export class CardComp extends CCComp { * 防止快速切卡时旧回调错误覆盖新图标。 */ private iconVisualToken: number = 0; - /** 是否处于放大状态 */ - private isEnlarged: boolean = false; - /** 长按定时器标记 */ - private longPressTimer: any = null; /** 是否触发了长按 */ private isLongPressed: boolean = false; /** 长按触发时间(秒) */ @@ -270,7 +266,6 @@ export class CardComp extends CCComp { this.card_cost = Math.floor(baseCost); this.node.active = true; - this.isEnlarged = false; this.applyCardUI(); this.playRefreshAnim(); mLogger.log(this.debugMode, "CardComp", "card updated", { @@ -404,30 +399,8 @@ export class CardComp extends CCComp { this.hasFixedBasePosition = true; } this.restPosition = new Vec3(x, this.fixedBaseY, this.fixedBaseZ); - // 拖拽/使用中不立即移动,等状态结束后归位 if (!this.isDragging && !this.isUsing) { - // ---- 放大时的位置偏移和层级调整 ---- - let targetX = this.restPosition.x; - let targetY = this.restPosition.y; - - if (this.isEnlarged) { - // 层级提高,凸显在最外层 - this.node.setSiblingIndex(999); - // x轴偏移逻辑 - if (this.restPosition.x < -200) { // -260 - targetX = this.restPosition.x + 30; - } else if (this.restPosition.x > 80) { // 260 -> 86也算 - targetX = this.restPosition.x - 30; - } - // y轴变大时 +35 - targetY = this.restPosition.y + 35; - } else { - // 恢复层级(可以根据实际情况调整,默认层级在父节点管理下应该会恢复) - // 如果需要严格恢复,这里可以不设置或者使用原始的siblingIndex,但目前不影响,只要放大的是最高就行 - // 如果有兄弟节点层级问题,可以在 MissionCardComp 中处理,这里先只管提高 - } - - this.node.setPosition(targetX, targetY, this.restPosition.z); + this.node.setPosition(this.restPosition); } } @@ -448,7 +421,6 @@ export class CardComp extends CCComp { this.isLocked = false; this.isDragging = false; this.isUsing = false; - this.isEnlarged = false; this.node.setPosition(this.restPosition); this.node.setScale(new Vec3(1, 1, 1)); this.updateLockUI(); @@ -474,7 +446,6 @@ export class CardComp extends CCComp { this.card_type = CardType.Hero; this.isLocked = false; this.isDragging = false; - this.isEnlarged = false; this.node.setPosition(this.restPosition); this.node.setScale(new Vec3(1, 1, 1)); this.updateLockUI(); @@ -518,21 +489,10 @@ export class CardComp extends CCComp { this.touchStartY = event.getUILocation().y; this.isDragging = true; this.isLongPressed = false; - - // 开启长按定时器 this.unschedule(this.onLongPress); this.scheduleOnce(this.onLongPress, this.LONG_PRESS_DURATION); } - /** 长按触发逻辑 */ - private onLongPress() { - if (!this.isDragging || this.isUsing) return; - this.isLongPressed = true; - this.isEnlarged = true; - this.applyCardUI(); - this.playReboundAnim(); - } - /** * 触摸移动:跟随手指向上偏移卡牌(仅允许向上拖拽,deltaY < 0 被 clamp 为 0) */ @@ -540,28 +500,14 @@ export class CardComp extends CCComp { if (!this.isDragging || !this.cardData || this.isUsing) return; const currentY = event.getUILocation().y; const deltaY = Math.max(0, currentY - this.touchStartY); - - // 拖拽距离超过一定阈值,取消长按 if (deltaY > 10) { this.unschedule(this.onLongPress); - if (this.isLongPressed && !this.isEnlarged) { - // 如果已经触发了长按但想取消放大(比如拖拽时恢复),视情况处理 - // 这里我们认为一旦拖动就取消未触发的长按,如果已经触发放大则保持放大直到松手 + if (this.isLongPressed) { + this.isLongPressed = false; + oops.gui.remove(UIID.HInfo); } } - - let baseX = this.restPosition.x; - let baseY = this.restPosition.y; - if (this.isEnlarged) { - if (this.restPosition.x <= -200) { - baseX = this.restPosition.x + 30; - } else if (this.restPosition.x >= 80) { - baseX = this.restPosition.x - 30; - } - baseY = this.restPosition.y + 35; - } - - this.node.setPosition(baseX, baseY + deltaY, this.restPosition.z); + this.node.setPosition(this.restPosition.x, this.restPosition.y + deltaY, this.restPosition.z); } /** @@ -575,34 +521,20 @@ export class CardComp extends CCComp { const endY = event.getUILocation().y; const deltaY = endY - this.touchStartY; this.isDragging = false; + + if (this.isLongPressed) { + this.isLongPressed = false; + oops.gui.remove(UIID.HInfo); + } if (deltaY >= this.dragUseThreshold) { const used = this.useCard(); if (!used) { - // 使用失败(如金币不足),恢复未放大状态并回弹 - this.isEnlarged = false; - this.isLongPressed = false; - const currentPos = this.node.position.clone(); - this.applyCardUI(); - this.node.setPosition(currentPos); this.playReboundAnim(); - } else { - // 使用成功,恢复非放大状态 - this.isEnlarged = false; - this.isLongPressed = false; } return; } - // 无论是点击还是长按松手,都恢复正常大小 - if (this.isEnlarged) { - this.isEnlarged = false; - const currentPos = this.node.position.clone(); - this.applyCardUI(); - this.node.setPosition(currentPos); - } - - this.isLongPressed = false; this.playReboundAnim(); } @@ -611,18 +543,25 @@ export class CardComp extends CCComp { this.unschedule(this.onLongPress); if (!this.isDragging || this.isUsing) return; this.isDragging = false; - - if (this.isEnlarged) { - this.isEnlarged = false; - const currentPos = this.node.position.clone(); - this.applyCardUI(); - this.node.setPosition(currentPos); + if (this.isLongPressed) { + this.isLongPressed = false; + oops.gui.remove(UIID.HInfo); } - - this.isLongPressed = false; this.playReboundAnim(); } + /** 长按触发:英雄卡打开英雄信息预览面板 */ + private onLongPress() { + if (!this.isDragging || this.isUsing) return; + if (!this.cardData || this.card_type !== CardType.Hero) return; + this.isLongPressed = true; + const heroUuid = this.card_uuid; + const heroLv = Math.max(1, this.cardData.hero_lv ?? 1); + const poolLv = Math.max(1, this.cardData.pool_lv ?? 1); + oops.gui.remove(UIID.HInfo); + oops.gui.open(UIID.HInfo, { heroUuid, heroLv, poolLv }); + } + /** * 点击锁控件:切换锁态(空槽不允许锁定)。 * 阻止事件冒泡,避免触发卡面的点击使用。 @@ -671,18 +610,7 @@ export class CardComp extends CCComp { this.iconVisualToken += 1; if (this.opacityComp) this.opacityComp.opacity = 255; - let targetX = this.restPosition.x; - let targetY = this.restPosition.y; - if (this.isEnlarged) { - this.node.setSiblingIndex(99); // 变大时提到最前 - if (this.restPosition.x <= -200) { // -260 - targetX = this.restPosition.x + 30; - } else if (this.restPosition.x >= 80) { // 260 -> 86也算 - targetX = this.restPosition.x - 30; - } - targetY = this.restPosition.y + 35; // y轴增加 - } - this.node.setPosition(targetX, targetY, this.restPosition.z); + this.node.setPosition(this.restPosition.x, this.restPosition.y, this.restPosition.z); // ---- 卡牌种类标识(近战 / 远程 / 辅助等) ---- const kindName = CKind[this.cardData.kind]; @@ -725,23 +653,22 @@ export class CardComp extends CCComp { // ---- 按卡牌类型渲染具体内容 ---- const uiTrans = this.node.getComponent(UITransform); if (uiTrans) { - uiTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230); + uiTrans.setContentSize(170, 230); const widget = this.node.getComponent(Widget); if (widget) widget.updateAlignment(); } - // 同时修改背景节点和边框节点的尺寸 if (this.BG_node) { const bgTrans = this.BG_node.getComponent(UITransform); if (bgTrans) { - bgTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230); + bgTrans.setContentSize(170, 230); const widget = this.BG_node.getComponent(Widget); if (widget) widget.updateAlignment(); } this.BG_node.children.forEach(child => { const childTrans = child.getComponent(UITransform); if (childTrans) { - childTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230); + childTrans.setContentSize(170, 230); const widget = child.getComponent(Widget); if (widget) widget.updateAlignment(); } @@ -796,7 +723,7 @@ export class CardComp extends CCComp { if (hbNode) { const hbTrans = hbNode.getComponent(UITransform); if (hbTrans) { - hbTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 340 : 230); + hbTrans.setContentSize(170, 230); const widget = hbNode.getComponent(Widget); if (widget) widget.updateAlignment(); } @@ -840,12 +767,7 @@ export class CardComp extends CCComp { this.ap_node.active = true; this.hp_node.active = true; - - if (this.isEnlarged) { - this.oinfo_node.active = true; - } else { - this.oinfo_node.active = false; - } + if (this.oinfo_node) this.oinfo_node.active = false; }else if(this.card_type===CardType.Skill){ if (this.lvl_node) this.lvl_node.node.active = false; // 技能卡:显示技能名 + 品质后缀 + 描述 @@ -858,12 +780,7 @@ export class CardComp extends CCComp { this.ap_node.active = false; this.hp_node.active = false; - - if (this.isEnlarged) { - this.oinfo_node.active = true; - } else { - this.oinfo_node.active = false; - } + if (this.oinfo_node) this.oinfo_node.active = false; }else{ if (this.lvl_node) this.lvl_node.node.active = false; // 特殊卡(升级 / 刷新):显示卡名 + 品质后缀 + 描述 @@ -877,12 +794,7 @@ export class CardComp extends CCComp { this.ap_node.active = false; this.hp_node.active = false; - - if (this.isEnlarged) { - this.oinfo_node.active = true; - } else { - this.oinfo_node.active = false; - } + if (this.oinfo_node) this.oinfo_node.active = false; } // ---- 费用标签 ---- @@ -894,10 +806,9 @@ export class CardComp extends CCComp { } } - // ---- 名字节点位置调整 ---- if (this.name_node) { const currentPos = this.name_node.position; - this.name_node.setPosition(currentPos.x, this.isEnlarged ? 8 : -70, currentPos.z); + this.name_node.setPosition(currentPos.x, -70, currentPos.z); } // ---- 图标 ---- @@ -933,23 +844,12 @@ export class CardComp extends CCComp { .start(); } - /** 回弹动画:从当前位置平滑回到静止位或放大位置并恢复缩放 */ + /** 回弹动画:从当前位置平滑回到静止位并恢复缩放 */ private playReboundAnim() { Tween.stopAllByTarget(this.node); - let targetX = this.restPosition.x; - let targetY = this.restPosition.y; - if (this.isEnlarged) { - if (this.restPosition.x <= -200) { - targetX = this.restPosition.x + 30; - } else if (this.restPosition.x >= 80) { - targetX = this.restPosition.x - 30; - } - targetY = this.restPosition.y + 35; - } - tween(this.node) .to(0.12, { - position: new Vec3(targetX, targetY, this.restPosition.z), + position: new Vec3(this.restPosition.x, this.restPosition.y, this.restPosition.z), scale: new Vec3(1, 1, 1) }) .start(); @@ -1138,23 +1038,6 @@ export class CardComp extends CCComp { return `${uuid}`; } - /** - * 打开英雄信息弹窗(IBox)。 - * 仅当当前卡为英雄卡且 HeroInfo 有效时生效。 - */ - private openHeroInfoIBox() { - if (!this.cardData) return; - if (this.cardData.type !== CardType.Hero) return; - const hero = HeroInfo[this.cardData.uuid]; - if (!hero) return; - const heroLv = Math.max(1, Math.floor(this.cardData.hero_lv ?? hero.lv ?? 1)); - oops.gui.remove(UIID.IBox); - oops.gui.open(UIID.IBox, { - heroUuid: this.cardData.uuid, - heroLv - }); - } - /** * 为英雄卡图标加载并播放 idle 动画。 * 使用 token 做竞态保护,确保异步回调时不会覆盖已更新的图标。 diff --git a/assets/script/game/map/HInfoComp.ts b/assets/script/game/map/HInfoComp.ts index 303e00e3..c7e49774 100644 --- a/assets/script/game/map/HInfoComp.ts +++ b/assets/script/game/map/HInfoComp.ts @@ -81,6 +81,12 @@ export class HInfoComp extends CCComp { private eid: number = 0; /** 绑定的英雄属性数据模型引用 */ private model: HeroAttrsComp | null = null; + /** 是否为静态预览模式(卡牌点击查看,非场上英雄) */ + private isPreview: boolean = false; + /** 静态预览数据 */ + private previewUuid: number = 0; + private previewLv: number = 1; + private previewPoolLv: number = 1; /** 英雄名字标签缓存引用 */ private nameLabel: Label | null = null; /** AP 标签缓存引用 */ @@ -103,7 +109,11 @@ export class HInfoComp extends CCComp { this.bindEvents(); } - onAdded(args: { eid: number }) { + onAdded(args: { eid?: number; heroUuid?: number; heroLv?: number; poolLv?: number }) { + if (args?.heroUuid) { + this.bindPreviewData(args.heroUuid, args.heroLv ?? 1, args.poolLv ?? 1); + return; + } const eid = args?.eid ?? 0; if (!eid) return; @@ -122,11 +132,55 @@ export class HInfoComp extends CCComp { } } + /** + * 静态预览模式刷新:从 HeroInfo 配置读取初始数据。 + */ + private refreshPreview() { + const heroUuid = this.previewUuid; + const hero = HeroInfo[heroUuid]; + if (!hero) return; + const heroLv = Math.max(1, this.previewLv); + + if (this.lv_node) { + this.lv_node.string = `Lv.${heroLv}`; + this.lv_node.color = getLvColor(heroLv); + } + + if (this.pool_lvnode) { + const poolLvStr = `lv${this.previewPoolLv}`; + this.pool_lvnode.children.forEach(child => { + child.active = (child.name === poolLvStr); + }); + } + + if (heroUuid !== this.iconHeroUuid) { + this.iconHeroUuid = heroUuid; + this.iconVisualToken += 1; + this.updateHeroAnimation(this.icon_node, heroUuid, this.iconVisualToken); + } + + if (this.nameLabel) { + this.nameLabel.string = hero.name ?? ""; + } + + if (this.apLabel) { + const ap = Math.max(0, Math.floor((hero.ap ?? 0) * heroLv)); + this.apLabel.string = `${ap}`; + if (this.apPlusLabel) this.apPlusLabel.node.active = false; + } + if (this.hpLabel) { + const hp = Math.max(0, Math.floor((hero.hp ?? 0) * heroLv)); + this.hpLabel.string = `${hp}`; + if (this.hpPlusLabel) this.hpPlusLabel.node.active = false; + } + } + /** 是否正在关闭中,防止重复调用 remove */ private isClosing: boolean = false; update(dt: number) { if (this.isClosing) return; + if (this.isPreview) return; if (!this.isModelAlive()) { this.isClosing = true; oops.gui.remove(UIID.HInfo); @@ -148,6 +202,24 @@ export class HInfoComp extends CCComp { bindData(eid: number, model: HeroAttrsComp) { this.eid = eid; this.model = model; + this.isPreview = false; + if (this.sell_node) this.sell_node.active = true; + if (this.close_node) this.close_node.active = true; + this.cacheLabels(); + this.refresh(); + } + + /** + * 绑定静态预览数据(卡牌点击查看)。 + * 通过 HeroInfo 配置 × 等级计算初始值,不绑定 ECS 实体。 + */ + bindPreviewData(heroUuid: number, heroLv: number, poolLv: number) { + this.isPreview = true; + this.previewUuid = heroUuid; + this.previewLv = heroLv; + this.previewPoolLv = poolLv; + if (this.sell_node) this.sell_node.active = false; + if (this.close_node) this.close_node.active = false; this.cacheLabels(); this.refresh(); } @@ -159,6 +231,10 @@ export class HInfoComp extends CCComp { * 3. 更新 AP / HP 数值标签。 */ refresh() { + if (this.isPreview) { + this.refreshPreview(); + return; + } if (!this.model) return; // ---- 卡牌等级显示 ---- @@ -287,9 +363,11 @@ export class HInfoComp extends CCComp { const path = `game/heros/hero/${hero.path}/idle`; resources.load(path, AnimationClip, (err, clip) => { if (err || !clip) return; - // 竞态保护 - if (token !== this.iconVisualToken || !this.model || this.model.hero_uuid !== uuid) { - return; + if (token !== this.iconVisualToken) return; + if (this.isPreview) { + if (this.previewUuid !== uuid) return; + } else { + if (!this.model || this.model.hero_uuid !== uuid) return; } this.clearAnimationClips(anim); anim.addClip(clip); @@ -397,9 +475,9 @@ export class HInfoComp extends CCComp { this.iconHeroUuid = 0; this.model = null; this.eid = 0; - // 弹窗节点的生命周期由 oops.gui 统一管理,此处不再主动销毁节点 - // if (this.node && this.node.isValid) { - // this.node.destroy(); - // } + this.isPreview = false; + this.previewUuid = 0; + this.previewLv = 1; + this.previewPoolLv = 1; } }