refactor(card): 重构卡牌触摸交互逻辑,替换长按为点击触发信息面板

1. 移除长按相关逻辑,改用点击触发英雄卡信息面板
2. 新增卡牌点击选中联动机制,统一管理召唤按钮显示
3. 调整触摸位移阈值,优化点击和拖拽的判定逻辑
4. 新增卡牌选中事件,实现多卡牌间的UI联动
5. 修复预制体默认激活状态,统一初始UI状态
This commit is contained in:
panFD
2026-06-19 15:51:17 +08:00
parent c30900e508
commit 17452167c3
3 changed files with 73 additions and 42 deletions

View File

@@ -5686,7 +5686,7 @@
"__id__": 314
}
],
"_active": true,
"_active": false,
"_components": [
{
"__id__": 320

View File

@@ -20,6 +20,8 @@ export enum GameEvent {
CastSkill = "CastSkill",
CardsClose = "CardsClose",
CardRefresh = "CardRefresh",
/** 单张卡牌被点击选中payload 为被点击的 CardComp 实例,用于其他卡牌联动隐藏 call_btn */
CardSelected = "CardSelected",
UseHeroCard = "UseHeroCard",
UseSkillCard = "UseSkillCard",
UseSpecialCard = "UseSpecialCard",

View File

@@ -135,10 +135,8 @@ export class CardComp extends CCComp {
* 防止快速切卡时旧回调错误覆盖新图标。
*/
private iconVisualToken: number = 0;
/** 是否触发了长按 */
private isLongPressed: boolean = false;
/** 长按触发时间(秒) */
private readonly LONG_PRESS_DURATION: number = 0.5;
/** 视为"点击"的最大位移阈值(像素),小于该值视为点击而非拖拽 */
private readonly tapMoveThreshold: number = 10;
// ======================== 生命周期 ========================
@@ -154,7 +152,8 @@ export class CardComp extends CCComp {
this.opacityComp.opacity = 255;
this.updateLockUI();
this.applyEmptyUI();
// call_btn 默认隐藏,仅在点击本卡时显示
this.hideCallBtn();
}
/** 组件销毁时解绑所有事件,防止残留回调 */
@@ -448,6 +447,7 @@ export class CardComp extends CCComp {
this.node.setScale(new Vec3(1, 1, 1));
this.updateLockUI();
this.applyEmptyUI();
this.hideCallBtn();
this.node.active = false;
}
@@ -461,6 +461,10 @@ export class CardComp extends CCComp {
this.node.on(NodeEventType.TOUCH_CANCEL, this.onCardTouchCancel, this);
this.Lock?.on(NodeEventType.TOUCH_END, this.onToggleLock, this);
this.unLock?.on(NodeEventType.TOUCH_END, this.onToggleLock, this);
// 召唤按钮点击 = 使用卡牌
this.call_btn?.on(NodeEventType.TOUCH_END, this.onCallBtnClick, this);
// 监听跨卡联动:其他卡牌被点击时隐藏本卡 call_btn
oops.message.on(GameEvent.CardSelected, this.onOtherCardSelected, this);
}
/** 解绑触控,防止节点销毁后残留回调 */
@@ -477,19 +481,20 @@ export class CardComp extends CCComp {
if (this.unLock && this.unLock.isValid) {
this.unLock.off(NodeEventType.TOUCH_END, this.onToggleLock, this);
}
if (this.call_btn && this.call_btn.isValid) {
this.call_btn.off(NodeEventType.TOUCH_END, this.onCallBtnClick, this);
}
oops.message.off(GameEvent.CardSelected, this.onOtherCardSelected, this);
}
// ======================== 触摸交互 ========================
/** 触摸开始:记录起点 Y,进入拖拽状态 */
/** 触摸开始:记录起点坐标,进入拖拽状态 */
private onCardTouchStart(event: EventTouch) {
if (!this.cardData || this.isUsing) return;
this.touchStartY = event.getUILocation().y;
this.touchStartX = event.getUILocation().x;
this.isDragging = true;
this.isLongPressed = false;
this.unschedule(this.onLongPress);
this.scheduleOnce(this.onLongPress, this.LONG_PRESS_DURATION);
}
/**
@@ -499,25 +504,15 @@ 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.isLongPressed = false;
oops.gui.remove(UIID.HInfo);
}
}
this.node.setPosition(this.restPosition.x, this.restPosition.y + deltaY, this.restPosition.z);
}
/**
* 触摸结束:
* - 技能卡:点击即可使用
* - 英雄卡/其他:上拉距离 >= dragUseThreshold → 视为"使用卡牌"
* - 否则视为"点击"或者"长按结束"
* - 上拉距离 >= dragUseThreshold → 视为"使用卡牌"
* - 位移很小(点击)→ 触发 onCardTap显示 call_btn / 打开信息面板 / 通知其他卡牌联动
*/
private onCardTouchEnd(event: EventTouch) {
this.unschedule(this.onLongPress);
if (!this.isDragging || !this.cardData || this.isUsing) return;
const endY = event.getUILocation().y;
const endX = event.getUILocation().x;
@@ -525,11 +520,6 @@ export class CardComp extends CCComp {
const deltaX = endX - this.touchStartX;
this.isDragging = false;
if (this.isLongPressed) {
this.isLongPressed = false;
oops.gui.remove(UIID.HInfo);
}
// 英雄卡保持上划使用
if (deltaY >= this.dragUseThreshold) {
const used = this.useCard();
@@ -539,31 +529,70 @@ export class CardComp extends CCComp {
return;
}
// 位移小于阈值视为"点击"
if (Math.abs(deltaY) < this.tapMoveThreshold && Math.abs(deltaX) < this.tapMoveThreshold) {
this.onCardTap();
return;
}
this.playReboundAnim();
}
/** 触摸取消:回弹至原位 */
private onCardTouchCancel() {
this.unschedule(this.onLongPress);
if (!this.isDragging || this.isUsing) return;
this.isDragging = false;
if (this.isLongPressed) {
this.isLongPressed = false;
oops.gui.remove(UIID.HInfo);
}
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.base_pool_lv ?? this.cardData.pool_lv ?? 1);
oops.gui.remove(UIID.HInfo);
oops.gui.open(UIID.HInfo, { heroUuid, heroLv, poolLv });
/**
* 卡牌被点击(非拖拽):
* 1. 显示本卡的召唤按钮 call_btn。
* 2. 派发 CardSelected 事件,其他卡牌收到后隐藏各自的 call_btn。
* 3. 英雄卡打开 HInfo 信息面板(点击查看,替代原长按)。
*/
private onCardTap() {
this.playReboundAnim();
this.showCallBtn();
oops.message.dispatchEvent(GameEvent.CardSelected, this);
// 英雄卡打开信息面板(点击替代长按)
if (this.card_type === CardType.Hero) {
const heroUuid = this.card_uuid;
const heroLv = Math.max(1, this.cardData?.hero_lv ?? 1);
const poolLv = Math.max(1, this.cardData?.base_pool_lv ?? this.cardData?.pool_lv ?? 1);
oops.gui.remove(UIID.HInfo);
oops.gui.open(UIID.HInfo, { heroUuid, heroLv, poolLv });
}
}
/**
* 其他卡牌被点击时联动隐藏本卡 call_btn。
* @param event 事件名ListenerFunc 约定的第一个参数)
* @param source 被点击的 CardComp 实例
*/
private onOtherCardSelected(event: string, source: CardComp) {
if (source === this) return;
this.hideCallBtn();
}
/** 显示召唤按钮 */
private showCallBtn() {
if (this.call_btn && this.call_btn.isValid) {
this.call_btn.active = true;
}
}
/** 隐藏召唤按钮 */
private hideCallBtn() {
if (this.call_btn && this.call_btn.isValid) {
this.call_btn.active = false;
}
}
/** 召唤按钮点击回调:阻止冒泡后触发使用卡牌 */
private onCallBtnClick(event: EventTouch) {
event.propagationStopped = true;
this.useCard();
}
/**