feat(ui): 增加卡牌点击放大功能并调整动画速度

- 点击卡牌可切换放大/缩小状态,放大时显示详细信息并调整位置层级
- 调整技能准备动画的播放速度和时长以优化视觉效果
- 修复技能触发逻辑,为未处理的类型添加默认动画播放
- 在MissionCardComp中记录卡牌原始定位点用于布局管理
This commit is contained in:
walkpan
2026-04-20 23:36:38 +08:00
parent 0101b40c20
commit 033fd44560
8 changed files with 2573 additions and 1788 deletions

View File

@@ -7,11 +7,11 @@
"embeddedPlayerGroups": [] "embeddedPlayerGroups": []
}, },
"_native": "", "_native": "",
"sample": 30, "sample": 20,
"speed": 1, "speed": 1,
"wrapMode": 1, "wrapMode": 1,
"enableTrsBlending": false, "enableTrsBlending": false,
"_duration": 0.4, "_duration": 0.6,
"_hash": 500763545, "_hash": 500763545,
"_tracks": [ "_tracks": [
{ {
@@ -69,17 +69,17 @@
"__type__": "cc.ObjectCurve", "__type__": "cc.ObjectCurve",
"_times": [ "_times": [
0, 0,
0.03333333333333333, 0.05,
0.06666666666666667,
0.1, 0.1,
0.13333333333333333, 0.15,
0.16666666666666666,
0.2, 0.2,
0.23333333333333334, 0.25,
0.26666666666666666,
0.3, 0.3,
0.3333333333333333, 0.35,
0.36666666666666664 0.4,
0.45,
0.5,
0.55
], ],
"_values": [ "_values": [
{ {

View File

@@ -7,11 +7,11 @@
"embeddedPlayerGroups": [] "embeddedPlayerGroups": []
}, },
"_native": "", "_native": "",
"sample": 30, "sample": 20,
"speed": 1, "speed": 1,
"wrapMode": 1, "wrapMode": 1,
"enableTrsBlending": false, "enableTrsBlending": false,
"_duration": 0.4, "_duration": 0.6,
"_hash": 500763545, "_hash": 500763545,
"_tracks": [ "_tracks": [
{ {
@@ -69,17 +69,17 @@
"__type__": "cc.ObjectCurve", "__type__": "cc.ObjectCurve",
"_times": [ "_times": [
0, 0,
0.03333333333333333, 0.05,
0.06666666666666667,
0.1, 0.1,
0.13333333333333333, 0.15,
0.16666666666666666,
0.2, 0.2,
0.23333333333333334, 0.25,
0.26666666666666666,
0.3, 0.3,
0.3333333333333333, 0.35,
0.36666666666666664 0.4,
0.45,
0.5,
0.55
], ],
"_values": [ "_values": [
{ {

View File

@@ -7,11 +7,11 @@
"embeddedPlayerGroups": [] "embeddedPlayerGroups": []
}, },
"_native": "", "_native": "",
"sample": 30, "sample": 18,
"speed": 1, "speed": 1,
"wrapMode": 1, "wrapMode": 1,
"enableTrsBlending": false, "enableTrsBlending": false,
"_duration": 0.4, "_duration": 0.6666666666666666,
"_hash": 500763545, "_hash": 500763545,
"_tracks": [ "_tracks": [
{ {
@@ -69,17 +69,17 @@
"__type__": "cc.ObjectCurve", "__type__": "cc.ObjectCurve",
"_times": [ "_times": [
0, 0,
0.03333333333333333, 0.05555555555555555,
0.06666666666666667, 0.1111111111111111,
0.1,
0.13333333333333333,
0.16666666666666666, 0.16666666666666666,
0.2, 0.2222222222222222,
0.23333333333333334, 0.2777777777777778,
0.26666666666666666,
0.3,
0.3333333333333333, 0.3333333333333333,
0.36666666666666664 0.3888888888888889,
0.4444444444444444,
0.5,
0.5555555555555556,
0.6111111111111112
], ],
"_values": [ "_values": [
{ {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -195,6 +195,8 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
heroView.playReady("yellow"); heroView.playReady("yellow");
} else if (triggerType === 'dead') { } else if (triggerType === 'dead') {
heroView.playOther("dead"); heroView.playOther("dead");
}else{
heroView.playOther('yellow')
} }
// 如果是敌方攻击技能,必须在战斗中才能释放;友方增益/护盾则允许在非战斗中释放 // 如果是敌方攻击技能,必须在战斗中才能释放;友方增益/护盾则允许在非战斗中释放

View File

@@ -20,7 +20,7 @@
* - smc.vmdata.mission_data —— 读写局内金币 * - smc.vmdata.mission_data —— 读写局内金币
*/ */
import { mLogger } from "../common/Logger"; import { mLogger } from "../common/Logger";
import { _decorator, Animation, AnimationClip, EventTouch, Label, Node, NodeEventType, Sprite, SpriteAtlas, Tween, tween, UIOpacity, Vec3, resources, Light } from "cc"; import { _decorator, Animation, AnimationClip, EventTouch, Label, Node, NodeEventType, Sprite, SpriteAtlas, Tween, tween, UIOpacity, Vec3, resources, Light, UITransform } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { CardConfig, CardType, SpecialRefreshCardList, SpecialUpgradeCardList, CKind, CardPoolList } from "../common/config/CardSet"; import { CardConfig, CardType, SpecialRefreshCardList, SpecialUpgradeCardList, CKind, CardPoolList } from "../common/config/CardSet";
@@ -121,6 +121,8 @@ export class CardComp extends CCComp {
* 防止快速切卡时旧回调错误覆盖新图标。 * 防止快速切卡时旧回调错误覆盖新图标。
*/ */
private iconVisualToken: number = 0; private iconVisualToken: number = 0;
/** 是否处于放大状态 */
private isEnlarged: boolean = false;
// ======================== 生命周期 ======================== // ======================== 生命周期 ========================
@@ -259,6 +261,7 @@ export class CardComp extends CCComp {
this.card_type = data.type; this.card_type = data.type;
this.card_cost = data.cost; this.card_cost = data.cost;
this.node.active = true; this.node.active = true;
this.isEnlarged = false;
this.applyCardUI(); this.applyCardUI();
this.playRefreshAnim(); this.playRefreshAnim();
mLogger.log(this.debugMode, "CardComp", "card updated", { mLogger.log(this.debugMode, "CardComp", "card updated", {
@@ -394,7 +397,28 @@ export class CardComp extends CCComp {
this.restPosition = new Vec3(x, this.fixedBaseY, this.fixedBaseZ); this.restPosition = new Vec3(x, this.fixedBaseY, this.fixedBaseZ);
// 拖拽/使用中不立即移动,等状态结束后归位 // 拖拽/使用中不立即移动,等状态结束后归位
if (!this.isDragging && !this.isUsing) { if (!this.isDragging && !this.isUsing) {
this.node.setPosition(this.restPosition); // ---- 放大时的位置偏移和层级调整 ----
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 > 200) { // 260
targetX = this.restPosition.x - 30;
}
// y轴变大时 +35
targetY = this.restPosition.y + 35;
} else {
// 恢复层级(可以根据实际情况调整,默认层级在父节点管理下应该会恢复)
// 如果需要严格恢复这里可以不设置或者使用原始的siblingIndex但目前不影响只要放大的是最高就行
// 如果有兄弟节点层级问题,可以在 MissionCardComp 中处理,这里先只管提高
}
this.node.setPosition(targetX, targetY, this.restPosition.z);
} }
} }
@@ -415,6 +439,7 @@ export class CardComp extends CCComp {
this.isLocked = false; this.isLocked = false;
this.isDragging = false; this.isDragging = false;
this.isUsing = false; this.isUsing = false;
this.isEnlarged = false;
this.node.setPosition(this.restPosition); this.node.setPosition(this.restPosition);
this.node.setScale(new Vec3(1, 1, 1)); this.node.setScale(new Vec3(1, 1, 1));
this.updateLockUI(); this.updateLockUI();
@@ -440,6 +465,7 @@ export class CardComp extends CCComp {
this.card_type = CardType.Hero; this.card_type = CardType.Hero;
this.isLocked = false; this.isLocked = false;
this.isDragging = false; this.isDragging = false;
this.isEnlarged = false;
this.node.setPosition(this.restPosition); this.node.setPosition(this.restPosition);
this.node.setScale(new Vec3(1, 1, 1)); this.node.setScale(new Vec3(1, 1, 1));
this.updateLockUI(); this.updateLockUI();
@@ -485,13 +511,25 @@ export class CardComp extends CCComp {
if (!this.isDragging || !this.cardData || this.isUsing) return; if (!this.isDragging || !this.cardData || this.isUsing) return;
const currentY = event.getUILocation().y; const currentY = event.getUILocation().y;
const deltaY = Math.max(0, currentY - this.touchStartY); const deltaY = Math.max(0, currentY - this.touchStartY);
this.node.setPosition(this.restPosition.x, this.restPosition.y + deltaY, this.restPosition.z);
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 >= 200) {
baseX = this.restPosition.x - 30;
}
baseY = this.restPosition.y + 35;
}
this.node.setPosition(baseX, baseY + deltaY, this.restPosition.z);
} }
/** /**
* 触摸结束: * 触摸结束:
* - 上拉距离 >= dragUseThreshold → 视为"使用卡牌" * - 上拉距离 >= dragUseThreshold → 视为"使用卡牌"
* - 否则视为"点击"打开英雄信息弹窗(仅英雄卡)并回弹 * - 否则视为"点击"切换放大缩小状态并回弹
*/ */
private onCardTouchEnd(event: EventTouch) { private onCardTouchEnd(event: EventTouch) {
if (!this.isDragging || !this.cardData || this.isUsing) return; if (!this.isDragging || !this.cardData || this.isUsing) return;
@@ -502,10 +540,16 @@ export class CardComp extends CCComp {
this.useCard(); this.useCard();
return; return;
} }
this.openHeroInfoIBox(); this.toggleEnlarge();
this.playReboundAnim(); this.playReboundAnim();
} }
/** 切换卡牌的放大/缩小状态 */
private toggleEnlarge() {
this.isEnlarged = !this.isEnlarged;
this.applyCardUI();
}
/** 触摸取消:回弹至原位 */ /** 触摸取消:回弹至原位 */
private onCardTouchCancel() { private onCardTouchCancel() {
if (!this.isDragging || this.isUsing) return; if (!this.isDragging || this.isUsing) return;
@@ -560,7 +604,19 @@ export class CardComp extends CCComp {
// 递增视觉令牌,用于异步加载竞态保护 // 递增视觉令牌,用于异步加载竞态保护
this.iconVisualToken += 1; this.iconVisualToken += 1;
if (this.opacityComp) this.opacityComp.opacity = 255; if (this.opacityComp) this.opacityComp.opacity = 255;
this.node.setPosition(this.restPosition);
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 >= 200) { // 260
targetX = this.restPosition.x - 30;
}
targetY = this.restPosition.y + 35; // y轴增加
}
this.node.setPosition(targetX, targetY, this.restPosition.z);
// ---- 卡牌种类标识(近战 / 远程 / 辅助等) ---- // ---- 卡牌种类标识(近战 / 远程 / 辅助等) ----
if (this.Ckind_node) { if (this.Ckind_node) {
@@ -593,16 +649,31 @@ export class CardComp extends CCComp {
} }
// ---- 按卡牌类型渲染具体内容 ---- // ---- 按卡牌类型渲染具体内容 ----
const uiTrans = this.node.getComponent(UITransform);
if (uiTrans) {
uiTrans.setContentSize(this.isEnlarged ? 230 : 170, this.isEnlarged ? 300 : 230);
}
if(this.card_type===CardType.Hero){ if(this.card_type===CardType.Hero){
// 英雄卡:显示英雄名 + 星级 + AP/HP // 英雄卡:显示英雄名 + 星级 + AP/HP
const hero = HeroInfo[this.card_uuid]; const hero = HeroInfo[this.card_uuid];
const heroLv = Math.max(1, Math.floor(this.cardData.hero_lv ?? hero?.lv ?? 1)); const heroLv = Math.max(1, Math.floor(this.cardData.hero_lv ?? hero?.lv ?? 1));
const suffix = heroLv >= 2 ? "★".repeat(heroLv - 1) : ""; const suffix = heroLv >= 2 ? "★".repeat(heroLv - 1) : "";
this.setLabel(this.name_node, `${suffix}${hero?.name || ""}${suffix}`); this.setLabel(this.name_node, `${suffix}${hero?.name || ""}${suffix}`);
this.info_node.active = true;
this.oinfo_node.active = false;
this.info_node.getChildByName("ap").getChildByName("val").getComponent(Label).string = `${(hero?.ap ?? 0) * heroLv}`; this.info_node.getChildByName("ap").getChildByName("val").getComponent(Label).string = `${(hero?.ap ?? 0) * heroLv}`;
this.info_node.getChildByName("hp").getChildByName("val").getComponent(Label).string = `${(hero?.hp ?? 0) * heroLv}`; this.info_node.getChildByName("hp").getChildByName("val").getComponent(Label).string = `${(hero?.hp ?? 0) * heroLv}`;
if (this.oinfo_node) {
const infoLabel = this.oinfo_node.getChildByName("info")?.getComponent(Label);
if (infoLabel) infoLabel.string = `${hero?.info || ""}`;
}
if (this.isEnlarged) {
this.info_node.active = true;
this.oinfo_node.active = true;
} else {
this.info_node.active = false;
this.oinfo_node.active = false;
}
}else if(this.card_type===CardType.Skill){ }else if(this.card_type===CardType.Skill){
// 技能卡:显示技能名 + 品质后缀 + 描述 // 技能卡:显示技能名 + 品质后缀 + 描述
const skill = SkillSet[this.card_uuid]; const skill = SkillSet[this.card_uuid];
@@ -610,9 +681,15 @@ export class CardComp extends CCComp {
const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1)); const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1));
const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : ""; const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : "";
this.setLabel(this.name_node, `${spSuffix}${skillCard?.name || skill?.name || ""}${spSuffix}`); this.setLabel(this.name_node, `${spSuffix}${skillCard?.name || skill?.name || ""}${spSuffix}`);
this.info_node.active = false;
this.oinfo_node.active = true;
this.oinfo_node.getChildByName("info").getComponent(Label).string = `${skillCard?.info || skill?.info || ""}`; this.oinfo_node.getChildByName("info").getComponent(Label).string = `${skillCard?.info || skill?.info || ""}`;
if (this.isEnlarged) {
this.info_node.active = false;
this.oinfo_node.active = true;
} else {
this.info_node.active = false;
this.oinfo_node.active = false;
}
}else{ }else{
// 特殊卡(升级 / 刷新):显示卡名 + 品质后缀 + 描述 // 特殊卡(升级 / 刷新):显示卡名 + 品质后缀 + 描述
const specialCard = this.card_type === CardType.SpecialUpgrade const specialCard = this.card_type === CardType.SpecialUpgrade
@@ -621,9 +698,15 @@ export class CardComp extends CCComp {
const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1)); const card_lv = Math.max(1, Math.floor(this.cardData.card_lv ?? 1));
const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : ""; const spSuffix = card_lv >= 2 ? "★".repeat(card_lv - 1) : "";
this.setLabel(this.name_node, `${spSuffix}${specialCard?.name || ""}${spSuffix}`); this.setLabel(this.name_node, `${spSuffix}${specialCard?.name || ""}${spSuffix}`);
this.info_node.active = false;
this.oinfo_node.active = true;
this.oinfo_node.getChildByName("info").getComponent(Label).string = `${specialCard?.info || ""}`; this.oinfo_node.getChildByName("info").getComponent(Label).string = `${specialCard?.info || ""}`;
if (this.isEnlarged) {
this.info_node.active = false;
this.oinfo_node.active = true;
} else {
this.info_node.active = false;
this.oinfo_node.active = false;
}
} }
// ---- 费用标签 ---- // ---- 费用标签 ----
@@ -660,12 +743,23 @@ export class CardComp extends CCComp {
.start(); .start();
} }
/** 回弹动画:从当前位置平滑回到静止位并恢复缩放 */ /** 回弹动画:从当前位置平滑回到静止位或放大位置并恢复缩放 */
private playReboundAnim() { private playReboundAnim() {
Tween.stopAllByTarget(this.node); 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 >= 200) {
targetX = this.restPosition.x - 30;
}
targetY = this.restPosition.y + 35;
}
tween(this.node) tween(this.node)
.to(0.12, { .to(0.12, {
position: this.restPosition, position: new Vec3(targetX, targetY, this.restPosition.z),
scale: new Vec3(1, 1, 1) scale: new Vec3(1, 1, 1)
}) })
.start(); .start();
@@ -703,6 +797,10 @@ export class CardComp extends CCComp {
* 清空名称、费用、信息面板、种类标识、背景底框、边框、图标。 * 清空名称、费用、信息面板、种类标识、背景底框、边框、图标。
*/ */
private applyEmptyUI() { private applyEmptyUI() {
const uiTrans = this.node.getComponent(UITransform);
if (uiTrans) {
uiTrans.setContentSize(170, 230);
}
this.iconVisualToken += 1; this.iconVisualToken += 1;
this.setLabel(this.name_node, ""); this.setLabel(this.name_node, "");
this.setLabel(this.cost_node, ""); this.setLabel(this.cost_node, "");

View File

@@ -137,6 +137,8 @@ export class MissionCardComp extends CCComp {
private cardsShowScale: Vec3 = new Vec3(1, 1, 1); private cardsShowScale: Vec3 = new Vec3(1, 1, 1);
/** 卡牌面板收起态缩放scale=0 隐藏) */ /** 卡牌面板收起态缩放scale=0 隐藏) */
private cardsHideScale: Vec3 = new Vec3(0, 0, 1); private cardsHideScale: Vec3 = new Vec3(0, 0, 1);
/** 卡牌原始定位点 */
private cardsPos = [-260,86,-86,260]
/** /**
* 英雄信息面板映射EID → { node, model, comp } * 英雄信息面板映射EID → { node, model, comp }
* 用于追踪每个出战英雄的面板实例和数据引用 * 用于追踪每个出战英雄的面板实例和数据引用