Compare commits
2 Commits
95969781fb
...
1d4769b080
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d4769b080 | ||
|
|
fb3cf4c619 |
6222
assets/resources/gui/auie/more.prefab
Normal file
6222
assets/resources/gui/auie/more.prefab
Normal file
File diff suppressed because it is too large
Load Diff
13
assets/resources/gui/auie/more.prefab.meta
Normal file
13
assets/resources/gui/auie/more.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"ver": "1.1.50",
|
||||||
|
"importer": "prefab",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "1bc6274e-7a88-46ee-9965-7b3bb2ac13c4",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {
|
||||||
|
"syncNodeName": "more"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,8 @@ export enum UIID {
|
|||||||
Guide2,
|
Guide2,
|
||||||
Guide3,
|
Guide3,
|
||||||
Guide4,
|
Guide4,
|
||||||
|
/** 英雄信息弹窗(长按英雄盒查看次级属性与技能描述) */
|
||||||
|
HeroMore,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 打开界面方式的配置数据 */
|
/** 打开界面方式的配置数据 */
|
||||||
@@ -51,4 +53,5 @@ export var UIConfigData: { [key: number]: UIConfig } = {
|
|||||||
[UIID.Guide2]: { layer: LayerType.PopUp, prefab: "gui/element/guide2" },
|
[UIID.Guide2]: { layer: LayerType.PopUp, prefab: "gui/element/guide2" },
|
||||||
[UIID.Guide3]: { layer: LayerType.PopUp, prefab: "gui/element/guide3" },
|
[UIID.Guide3]: { layer: LayerType.PopUp, prefab: "gui/element/guide3" },
|
||||||
[UIID.Guide4]: { layer: LayerType.PopUp, prefab: "gui/element/guide4" },
|
[UIID.Guide4]: { layer: LayerType.PopUp, prefab: "gui/element/guide4" },
|
||||||
|
[UIID.HeroMore]: { layer: LayerType.PopUp, prefab: "gui/auie/more" },
|
||||||
}
|
}
|
||||||
@@ -3,16 +3,17 @@
|
|||||||
* @description 英雄面板单个英雄盒组件(UI 视图层)
|
* @description 英雄面板单个英雄盒组件(UI 视图层)
|
||||||
*
|
*
|
||||||
* 职责:
|
* 职责:
|
||||||
* 1. 同步显示一个已登场英雄的详细信息(图标 / 名称 / 等级 / AP / HP / 描述)。
|
* 1. 同步显示一个已登场英雄的基础信息(图标 / 名称 / 等级 / AP / HP)。
|
||||||
* 2. 由 MissionCardComp 实例化到 herosBoxNode 下,最多 3 个,与场上英雄一一对应。
|
* 2. 长按英雄盒弹出信息面板(UIID.HeroMore → more.prefab),展示次级属性与技能描述。
|
||||||
* 3. 英雄卖出 / 死亡后槽位保留,通过 applyEmpty() 显示为空英雄信息。
|
* 3. 由 MissionCardComp 实例化到 herosBoxNode 下,最多 3 个,与场上英雄一一对应。
|
||||||
|
* 4. 英雄卖出 / 死亡后槽位保留,通过 applyEmpty() 显示为空英雄信息。
|
||||||
*
|
*
|
||||||
* 依赖:
|
* 依赖:
|
||||||
* - HeroAttrsComp —— 英雄属性数据模型
|
* - HeroAttrsComp —— 英雄属性数据模型
|
||||||
* - HeroInfo(heroSet)—— 英雄静态配置
|
* - HeroInfo(heroSet)—— 英雄静态配置
|
||||||
*/
|
*/
|
||||||
import { mLogger } from "../common/Logger";
|
import { mLogger } from "../common/Logger";
|
||||||
import { _decorator, Node, Sprite, Label, RichText, resources, AnimationClip, SpriteFrame, NodeEventType, Tween, tween, Vec3, Color } from "cc";
|
import { _decorator, Node, Sprite, Label, resources, AnimationClip, SpriteFrame, NodeEventType, EventTouch, Tween, tween, Vec3, Color } 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 { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
@@ -21,7 +22,7 @@ import { HeroInfo } from "../common/config/heroSet";
|
|||||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||||
import { Hero } from "../hero/Hero";
|
import { Hero } from "../hero/Hero";
|
||||||
import { FacSet, FightSet, getLvColor } from "../common/config/GameSet";
|
import { FacSet, FightSet, getLvColor } from "../common/config/GameSet";
|
||||||
import { buildSkillDesc, buildSkillDescRich, ISkillDescSource } from "../common/config/HeroSkillDesc";
|
import { buildSkillDesc, ISkillDescSource } from "../common/config/HeroSkillDesc";
|
||||||
import { MissionEconomy } from "./MissionEconomy";
|
import { MissionEconomy } from "./MissionEconomy";
|
||||||
import { UIID } from "../common/config/GameUIConfig";
|
import { UIID } from "../common/config/GameUIConfig";
|
||||||
|
|
||||||
@@ -75,12 +76,6 @@ export class HeroBoxComp extends CCComp {
|
|||||||
@property(Label)
|
@property(Label)
|
||||||
private upgrade_price_label: Label = null;
|
private upgrade_price_label: Label = null;
|
||||||
|
|
||||||
/** 技能描述富文本(多行,展示当前等级触发技能,数值高亮) */
|
|
||||||
@property(RichText)
|
|
||||||
private skill_label: RichText = null;
|
|
||||||
/** 技能描述富文本内容缓存(避免 refresh 降频内重复重解析排版) */
|
|
||||||
private skillDescCache: string = "";
|
|
||||||
|
|
||||||
// ======================== 战斗信息标签(总值 + 附加值) ========================
|
// ======================== 战斗信息标签(总值 + 附加值) ========================
|
||||||
|
|
||||||
/** 攻击总值 */
|
/** 攻击总值 */
|
||||||
@@ -95,63 +90,6 @@ export class HeroBoxComp extends CCComp {
|
|||||||
/** 生命附加值 */
|
/** 生命附加值 */
|
||||||
@property(Label)
|
@property(Label)
|
||||||
private hp_plus_label: Label = null;
|
private hp_plus_label: Label = null;
|
||||||
/** 暴击率总值(百分点) */
|
|
||||||
@property(Label)
|
|
||||||
private crt_all_label: Label = null;
|
|
||||||
/** 暴击率附加值 */
|
|
||||||
@property(Label)
|
|
||||||
private crt_plus_label: Label = null;
|
|
||||||
/** 击退率总值(百分点) */
|
|
||||||
@property(Label)
|
|
||||||
private knockback_all_label: Label = null;
|
|
||||||
/** 击退率附加值 */
|
|
||||||
@property(Label)
|
|
||||||
private knockback_plus_label: Label = null;
|
|
||||||
/** 风怒率总值(百分点) */
|
|
||||||
@property(Label)
|
|
||||||
private wfuny_all_label: Label = null;
|
|
||||||
/** 风怒率附加值 */
|
|
||||||
@property(Label)
|
|
||||||
private wfuny_plus_label: Label = null;
|
|
||||||
/** 击晕率总值(百分点) */
|
|
||||||
@property(Label)
|
|
||||||
private stun_all_label: Label = null;
|
|
||||||
/** 击晕率附加值 */
|
|
||||||
@property(Label)
|
|
||||||
private stun_plus_label: Label = null;
|
|
||||||
/** 攻击速度总值(攻击间隔,秒;速度提升时间隔变短,附加值为负) */
|
|
||||||
@property(Label)
|
|
||||||
private speed_all_label: Label = null;
|
|
||||||
/** 攻击速度附加值 */
|
|
||||||
@property(Label)
|
|
||||||
private speed_plus_label: Label = null;
|
|
||||||
/** 冰冻率总值(百分点) */
|
|
||||||
@property(Label)
|
|
||||||
private freeze_all_label: Label = null;
|
|
||||||
/** 冰冻率附加值 */
|
|
||||||
@property(Label)
|
|
||||||
private freeze_plus_label: Label = null;
|
|
||||||
/** 穿透率总值(百分点) */
|
|
||||||
@property(Label)
|
|
||||||
private puncture_all_label: Label = null;
|
|
||||||
/** 穿透率附加值 */
|
|
||||||
@property(Label)
|
|
||||||
private puncture_plus_label: Label = null;
|
|
||||||
/** 暴击伤害总值(额外暴伤,百分点) */
|
|
||||||
@property(Label)
|
|
||||||
private crit_dmg_all_label: Label = null;
|
|
||||||
/** 暴击伤害附加值 */
|
|
||||||
@property(Label)
|
|
||||||
private crit_dmg_plus_label: Label = null;
|
|
||||||
|
|
||||||
// ======================== 更多属性折叠栏 ========================
|
|
||||||
|
|
||||||
/** 次级属性栏容器(默认隐藏,长按更多按钮时显示) */
|
|
||||||
@property(Node)
|
|
||||||
private attr_box: Node = null;
|
|
||||||
/** 更多按钮(长按显示次级属性栏,放开隐藏) */
|
|
||||||
@property(Node)
|
|
||||||
private more_btn: Node = null;
|
|
||||||
|
|
||||||
// ======================== 运行时状态 ========================
|
// ======================== 运行时状态 ========================
|
||||||
|
|
||||||
@@ -164,16 +102,24 @@ export class HeroBoxComp extends CCComp {
|
|||||||
/** 当前显示的英雄 UUID(避免相同 UUID 重复加载动画) */
|
/** 当前显示的英雄 UUID(避免相同 UUID 重复加载动画) */
|
||||||
private iconHeroUuid: number = 0;
|
private iconHeroUuid: number = 0;
|
||||||
|
|
||||||
|
/** 长按判定:当前按下触摸 ID(-1 表示无) */
|
||||||
|
private longPressTouchId: number = -1;
|
||||||
|
/** 长按判定:按下起点 UI 坐标 */
|
||||||
|
private longPressStartX: number = 0;
|
||||||
|
private longPressStartY: number = 0;
|
||||||
|
/** 长按触发时长(秒) */
|
||||||
|
private static readonly LONG_PRESS_DURATION = 0.5;
|
||||||
|
/** 长按最大位移容差(像素),超出则取消判定 */
|
||||||
|
private static readonly LONG_PRESS_SLOP = 30;
|
||||||
|
|
||||||
// ======================== 生命周期 ========================
|
// ======================== 生命周期 ========================
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
// 次级属性栏默认隐藏,长按更多按钮时显示,放开隐藏
|
// 长按英雄盒任意区域弹出信息面板(UIID.HeroMore → more.prefab);出售/升级按钮上的触摸不触发
|
||||||
if (this.attr_box && this.attr_box.isValid) {
|
this.node.on(NodeEventType.TOUCH_START, this.onBoxTouchStart, this);
|
||||||
this.attr_box.active = false;
|
this.node.on(NodeEventType.TOUCH_MOVE, this.onBoxTouchMove, this);
|
||||||
}
|
this.node.on(NodeEventType.TOUCH_END, this.onBoxTouchEnd, this);
|
||||||
this.more_btn?.on(NodeEventType.TOUCH_START, this.onMoreBtnTouchStart, this);
|
this.node.on(NodeEventType.TOUCH_CANCEL, this.onBoxTouchCancel, this);
|
||||||
this.more_btn?.on(NodeEventType.TOUCH_END, this.onMoreBtnTouchEnd, this);
|
|
||||||
this.more_btn?.on(NodeEventType.TOUCH_CANCEL, this.onMoreBtnTouchEnd, this);
|
|
||||||
// 空槽位点击 → 打开英雄卡池
|
// 空槽位点击 → 打开英雄卡池
|
||||||
this.noHero?.on(NodeEventType.TOUCH_END, this.onNoHeroClick, this);
|
this.noHero?.on(NodeEventType.TOUCH_END, this.onNoHeroClick, this);
|
||||||
// 出售按钮点击 → 出售当前绑定英雄
|
// 出售按钮点击 → 出售当前绑定英雄
|
||||||
@@ -183,10 +129,11 @@ export class HeroBoxComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDestroy() {
|
onDestroy() {
|
||||||
if (this.more_btn && this.more_btn.isValid) {
|
if (this.node && this.node.isValid) {
|
||||||
this.more_btn.off(NodeEventType.TOUCH_START, this.onMoreBtnTouchStart, this);
|
this.node.off(NodeEventType.TOUCH_START, this.onBoxTouchStart, this);
|
||||||
this.more_btn.off(NodeEventType.TOUCH_END, this.onMoreBtnTouchEnd, this);
|
this.node.off(NodeEventType.TOUCH_MOVE, this.onBoxTouchMove, this);
|
||||||
this.more_btn.off(NodeEventType.TOUCH_CANCEL, this.onMoreBtnTouchEnd, this);
|
this.node.off(NodeEventType.TOUCH_END, this.onBoxTouchEnd, this);
|
||||||
|
this.node.off(NodeEventType.TOUCH_CANCEL, this.onBoxTouchCancel, this);
|
||||||
}
|
}
|
||||||
if (this.noHero && this.noHero.isValid) {
|
if (this.noHero && this.noHero.isValid) {
|
||||||
this.noHero.off(NodeEventType.TOUCH_END, this.onNoHeroClick, this);
|
this.noHero.off(NodeEventType.TOUCH_END, this.onNoHeroClick, this);
|
||||||
@@ -376,24 +323,76 @@ export class HeroBoxComp extends CCComp {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 更多按钮按下:显示次级属性栏 */
|
/**
|
||||||
private onMoreBtnTouchStart() {
|
* 英雄盒按下:记录起点并启动长按判定。
|
||||||
oops.audio.playEffect("music/button");
|
* 出售/升级按钮上的触摸不触发长按(按钮有各自的 TOUCH_END 行为)。
|
||||||
this.setAttrBoxVisible(true);
|
*/
|
||||||
|
private onBoxTouchStart(event: EventTouch) {
|
||||||
|
if (!this.hasHero) return;
|
||||||
|
const target = event.target as Node | null;
|
||||||
|
if (this.isNodeWithin(target, this.sell_btn) || this.isNodeWithin(target, this.upgrade_btn)) return;
|
||||||
|
this.longPressTouchId = event.touch?.getID() ?? -1;
|
||||||
|
const pos = event.getUILocation();
|
||||||
|
this.longPressStartX = pos.x;
|
||||||
|
this.longPressStartY = pos.y;
|
||||||
|
this.scheduleOnce(this.onLongPressFired, HeroBoxComp.LONG_PRESS_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 更多按钮放开/取消:隐藏次级属性栏 */
|
/** 英雄盒拖动:位移超出容差取消长按判定 */
|
||||||
private onMoreBtnTouchEnd() {
|
private onBoxTouchMove(event: EventTouch) {
|
||||||
this.setAttrBoxVisible(false);
|
if (this.longPressTouchId < 0) return;
|
||||||
}
|
if ((event.touch?.getID() ?? -1) !== this.longPressTouchId) return;
|
||||||
|
const pos = event.getUILocation();
|
||||||
/** 切换次级属性栏容器可见性 */
|
const dx = pos.x - this.longPressStartX;
|
||||||
private setAttrBoxVisible(visible: boolean) {
|
const dy = pos.y - this.longPressStartY;
|
||||||
if (this.attr_box && this.attr_box.isValid) {
|
if (dx * dx + dy * dy > HeroBoxComp.LONG_PRESS_SLOP * HeroBoxComp.LONG_PRESS_SLOP) {
|
||||||
this.attr_box.active = visible;
|
this.cancelLongPress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 英雄盒抬起:取消未触发的长按判定 */
|
||||||
|
private onBoxTouchEnd(event: EventTouch) {
|
||||||
|
if (this.longPressTouchId < 0) return;
|
||||||
|
if ((event.touch?.getID() ?? -1) !== this.longPressTouchId) return;
|
||||||
|
this.cancelLongPress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 英雄盒触摸被系统打断:取消长按判定 */
|
||||||
|
private onBoxTouchCancel(event: EventTouch) {
|
||||||
|
this.onBoxTouchEnd(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取消长按判定(复位触摸 ID 并移除定时) */
|
||||||
|
private cancelLongPress() {
|
||||||
|
this.longPressTouchId = -1;
|
||||||
|
this.unschedule(this.onLongPressFired);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 长按触发:弹出英雄信息面板(more.prefab,展示次级属性与技能描述) */
|
||||||
|
private onLongPressFired() {
|
||||||
|
this.longPressTouchId = -1;
|
||||||
|
if (!this.hasHero || !(this.model as any)?.ent) return;
|
||||||
|
oops.audio.playEffect("music/button");
|
||||||
|
oops.gui.open(UIID.HeroMore, { eid: this.eid });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断触摸目标节点是否位于指定祖先节点(含自身)之内。
|
||||||
|
* @param target 触摸命中的节点(可能为空)
|
||||||
|
* @param ancestor 祖先节点(可能为空,空时返回 false)
|
||||||
|
* @returns 命中节点是否为 ancestor 的后代或自身
|
||||||
|
*/
|
||||||
|
private isNodeWithin(target: Node | null, ancestor: Node | null): boolean {
|
||||||
|
if (!target || !ancestor || !ancestor.isValid) return false;
|
||||||
|
let cur: Node | null = target;
|
||||||
|
while (cur) {
|
||||||
|
if (cur === ancestor) return true;
|
||||||
|
if (cur === this.node) break;
|
||||||
|
cur = cur.parent;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** ECS 组件移除时销毁节点 */
|
/** ECS 组件移除时销毁节点 */
|
||||||
reset() {
|
reset() {
|
||||||
if (this.node && this.node.isValid) {
|
if (this.node && this.node.isValid) {
|
||||||
@@ -470,22 +469,10 @@ export class HeroBoxComp extends CCComp {
|
|||||||
}
|
}
|
||||||
this.clearStatRow(this.ap_all_label, this.ap_plus_label);
|
this.clearStatRow(this.ap_all_label, this.ap_plus_label);
|
||||||
this.clearStatRow(this.hp_all_label, this.hp_plus_label);
|
this.clearStatRow(this.hp_all_label, this.hp_plus_label);
|
||||||
this.clearStatRow(this.crt_all_label, this.crt_plus_label);
|
|
||||||
this.clearStatRow(this.knockback_all_label, this.knockback_plus_label);
|
|
||||||
this.clearStatRow(this.wfuny_all_label, this.wfuny_plus_label);
|
|
||||||
this.clearStatRow(this.stun_all_label, this.stun_plus_label);
|
|
||||||
this.clearStatRow(this.speed_all_label, this.speed_plus_label);
|
|
||||||
this.clearStatRow(this.freeze_all_label, this.freeze_plus_label);
|
|
||||||
this.clearStatRow(this.puncture_all_label, this.puncture_plus_label);
|
|
||||||
this.clearStatRow(this.crit_dmg_all_label, this.crit_dmg_plus_label);
|
|
||||||
if (this.skill_label && this.skill_label.isValid) {
|
|
||||||
this.skill_label.string = "";
|
|
||||||
}
|
|
||||||
this.skillDescCache = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新显示:同步英雄等级、名称、AP / HP、描述与图标。
|
* 刷新显示:同步英雄等级、名称、AP / HP 与图标(次级属性与技能描述已分离至 HeroMoreCom 弹窗)。
|
||||||
* 绑定的英雄实体已失效时自动切换为空状态。
|
* 绑定的英雄实体已失效时自动切换为空状态。
|
||||||
*/
|
*/
|
||||||
public refresh() {
|
public refresh() {
|
||||||
@@ -537,48 +524,6 @@ export class HeroBoxComp extends CCComp {
|
|||||||
const baseHp = Math.max(0, Math.floor(this.model.base_hp ?? 0));
|
const baseHp = Math.max(0, Math.floor(this.model.base_hp ?? 0));
|
||||||
this.setStatRow(this.hp_all_label, this.hp_plus_label, finalHp, finalHp - baseHp);
|
this.setStatRow(this.hp_all_label, this.hp_plus_label, finalHp, finalHp - baseHp);
|
||||||
|
|
||||||
// ---- 暴击率 / 击退率 / 风怒率 / 击晕率(百分点,附加 = 最终 - 基础配置值) ----
|
|
||||||
const finalCrit = this.model.getFinalCritical();
|
|
||||||
this.setStatRow(this.crt_all_label, this.crt_plus_label, finalCrit, finalCrit - (this.model.critical ?? 0), "%");
|
|
||||||
|
|
||||||
const finalKnockback = this.model.getFinalKnockbackChance();
|
|
||||||
this.setStatRow(this.knockback_all_label, this.knockback_plus_label, finalKnockback, finalKnockback - (this.model.knockback_chance ?? 0), "%");
|
|
||||||
|
|
||||||
const finalWfuny = this.model.getFinalWindFury();
|
|
||||||
this.setStatRow(this.wfuny_all_label, this.wfuny_plus_label, finalWfuny, finalWfuny - (this.model.wfuny ?? 0), "%");
|
|
||||||
|
|
||||||
const finalStun = this.model.getFinalStunChance();
|
|
||||||
this.setStatRow(this.stun_all_label, this.stun_plus_label, finalStun, finalStun - (this.model.stun_chance ?? 0), "%");
|
|
||||||
|
|
||||||
// ---- 攻击速度(攻击间隔,秒;速度提升 → 间隔变短,附加值为负) ----
|
|
||||||
const skillIds = this.model.getSkillIds();
|
|
||||||
const displaySkillId = skillIds[1] ?? skillIds[0] ?? 0;
|
|
||||||
const baseCd = displaySkillId ? (this.model.skills[displaySkillId]?.cd ?? 0) : 0;
|
|
||||||
const finalCd = displaySkillId ? this.model.getEffectiveSkillCd(displaySkillId) : 0;
|
|
||||||
this.setStatRow(this.speed_all_label, this.speed_plus_label, finalCd, finalCd - baseCd, "s", 1);
|
|
||||||
|
|
||||||
// ---- 冰冻率 / 穿透率(百分点,附加 = 最终 - 基础配置值) ----
|
|
||||||
const finalFreeze = this.model.getFinalFreezeChance();
|
|
||||||
this.setStatRow(this.freeze_all_label, this.freeze_plus_label, finalFreeze, finalFreeze - (this.model.freeze_chance ?? 0), "%");
|
|
||||||
|
|
||||||
const finalPuncture = this.model.getFinalPunctureChance();
|
|
||||||
this.setStatRow(this.puncture_all_label, this.puncture_plus_label, finalPuncture, finalPuncture - (this.model.puncture_chance ?? 0), "%");
|
|
||||||
|
|
||||||
// ---- 暴击伤害(额外暴伤,百分点,附加 = 最终 - 基础配置值) ----
|
|
||||||
const finalCritDmg = this.model.getFinalCritDamage();
|
|
||||||
this.setStatRow(this.crit_dmg_all_label, this.crit_dmg_plus_label, finalCritDmg, finalCritDmg - (this.model.crit_damage ?? 0), "%");
|
|
||||||
|
|
||||||
// ---- 技能描述(全档位合并:每技能一行,等级置前 + 已/未激活状态,数值富文本高亮) ----
|
|
||||||
if (this.skill_label && this.skill_label.isValid) {
|
|
||||||
// 缓存未变则跳过重设,避免 refresh 降频内 RichText 重复重解析排版
|
|
||||||
// showName=false 不显示技能名仅保留效果;showLocked=true 合并全档位(未来档数值灰色拼接,未激活技能整行灰色)
|
|
||||||
const desc = buildSkillDescRich(this.model, false, true);
|
|
||||||
if (desc !== this.skillDescCache) {
|
|
||||||
this.skillDescCache = desc;
|
|
||||||
this.skill_label.string = desc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 图标(UUID 变化时重新加载首帧动画) ----
|
// ---- 图标(UUID 变化时重新加载首帧动画) ----
|
||||||
if (hero && heroUuid !== this.iconHeroUuid) {
|
if (hero && heroUuid !== this.iconHeroUuid) {
|
||||||
this.iconHeroUuid = heroUuid;
|
this.iconHeroUuid = heroUuid;
|
||||||
|
|||||||
252
assets/script/game/map/HeroMoreCom.ts
Normal file
252
assets/script/game/map/HeroMoreCom.ts
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
/**
|
||||||
|
* @file HeroMoreCom.ts
|
||||||
|
* @description 英雄信息弹窗组件(UI 视图层)
|
||||||
|
*
|
||||||
|
* 职责:
|
||||||
|
* 1. 展示单个英雄的次级属性(攻速/击晕/暴击/爆伤/冰冻/击退/穿透/风怒)与技能描述,
|
||||||
|
* 展示内容由原 HeroBoxComp 分离而来,计算口径与其完全一致。
|
||||||
|
* 2. 由 HeroBoxComp 长按英雄盒时通过 oops.gui.open(UIID.HeroMore, { eid }) 打开(oops.gui 模块)。
|
||||||
|
* 3. 属性受 buff/光环影响实时变化,打开期间按固定间隔降频刷新;英雄死亡/被卖出自动关闭。
|
||||||
|
* 4. 点击弹窗内任意位置(含全屏透明遮罩)关闭。
|
||||||
|
*
|
||||||
|
* 依赖:
|
||||||
|
* - HeroAttrsComp —— 英雄属性数据模型
|
||||||
|
* - HeroSkillDesc —— 技能描述富文本标准层
|
||||||
|
*/
|
||||||
|
import { _decorator, Component, Label, NodeEventType, RichText } from "cc";
|
||||||
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
|
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||||
|
import { buildSkillDescRich } from "../common/config/HeroSkillDesc";
|
||||||
|
|
||||||
|
const { ccclass, property } = _decorator;
|
||||||
|
|
||||||
|
/** 英雄信息弹窗参数 */
|
||||||
|
export interface HeroMoreParams {
|
||||||
|
/** 英雄 ECS 实体 ID */
|
||||||
|
eid?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HeroMoreCom —— 英雄信息弹窗(more.prefab 根节点组件)
|
||||||
|
*
|
||||||
|
* 与长按的英雄盒一一绑定(eid),英雄移除后自动关闭。
|
||||||
|
* 序列化引用(8 行属性 Label + 技能 RichText)直接写在 more.prefab 中。
|
||||||
|
*/
|
||||||
|
@ccclass('HeroMoreCom')
|
||||||
|
export class HeroMoreCom extends Component {
|
||||||
|
// ======================== 编辑器绑定节点(more.prefab attr_more 下 8 行) ========================
|
||||||
|
|
||||||
|
/** 攻速(攻击间隔,秒)总值 */
|
||||||
|
@property(Label)
|
||||||
|
private speed_all_label: Label = null;
|
||||||
|
/** 攻速附加值 */
|
||||||
|
@property(Label)
|
||||||
|
private speed_plus_label: Label = null;
|
||||||
|
/** 击晕率总值(百分点) */
|
||||||
|
@property(Label)
|
||||||
|
private stun_all_label: Label = null;
|
||||||
|
/** 击晕率附加值 */
|
||||||
|
@property(Label)
|
||||||
|
private stun_plus_label: Label = null;
|
||||||
|
/** 暴击率总值(百分点) */
|
||||||
|
@property(Label)
|
||||||
|
private crt_all_label: Label = null;
|
||||||
|
/** 暴击率附加值 */
|
||||||
|
@property(Label)
|
||||||
|
private crt_plus_label: Label = null;
|
||||||
|
/** 暴击伤害总值(额外暴伤,百分点) */
|
||||||
|
@property(Label)
|
||||||
|
private crit_dmg_all_label: Label = null;
|
||||||
|
/** 暴击伤害附加值 */
|
||||||
|
@property(Label)
|
||||||
|
private crit_dmg_plus_label: Label = null;
|
||||||
|
/** 冰冻率总值(百分点) */
|
||||||
|
@property(Label)
|
||||||
|
private freeze_all_label: Label = null;
|
||||||
|
/** 冰冻率附加值 */
|
||||||
|
@property(Label)
|
||||||
|
private freeze_plus_label: Label = null;
|
||||||
|
/** 击退率总值(百分点) */
|
||||||
|
@property(Label)
|
||||||
|
private knockback_all_label: Label = null;
|
||||||
|
/** 击退率附加值 */
|
||||||
|
@property(Label)
|
||||||
|
private knockback_plus_label: Label = null;
|
||||||
|
/** 穿透率总值(百分点) */
|
||||||
|
@property(Label)
|
||||||
|
private puncture_all_label: Label = null;
|
||||||
|
/** 穿透率附加值 */
|
||||||
|
@property(Label)
|
||||||
|
private puncture_plus_label: Label = null;
|
||||||
|
/** 风怒率总值(百分点) */
|
||||||
|
@property(Label)
|
||||||
|
private wfuny_all_label: Label = null;
|
||||||
|
/** 风怒率附加值 */
|
||||||
|
@property(Label)
|
||||||
|
private wfuny_plus_label: Label = null;
|
||||||
|
/** 技能描述富文本(全档位合并,数值高亮) */
|
||||||
|
@property(RichText)
|
||||||
|
private skill_label: RichText = null;
|
||||||
|
|
||||||
|
// ======================== 运行时状态 ========================
|
||||||
|
|
||||||
|
/** 绑定的英雄属性数据模型引用 */
|
||||||
|
private model: HeroAttrsComp | null = null;
|
||||||
|
/** 是否正在关闭(防止重复调用 remove) */
|
||||||
|
private isClosing: boolean = false;
|
||||||
|
/** 刷新计时累计(降频刷新实时属性) */
|
||||||
|
private refreshElapsed: number = 0;
|
||||||
|
/** 实时刷新间隔(秒) */
|
||||||
|
private static readonly REFRESH_INTERVAL = 0.25;
|
||||||
|
/** 技能描述富文本内容缓存(避免降频内重复重解析排版) */
|
||||||
|
private skillDescCache: string = "";
|
||||||
|
|
||||||
|
// ======================== 生命周期 ========================
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
// 全屏透明遮罩为根节点子节点,点击任意处触摸事件冒泡至此 → 关闭弹窗
|
||||||
|
this.node.on(NodeEventType.TOUCH_END, this.onTouchEndClose, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy() {
|
||||||
|
if (this.node && this.node.isValid) {
|
||||||
|
this.node.off(NodeEventType.TOUCH_END, this.onTouchEndClose, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* oops.gui 添加到舞台回调(框架约定入口)。
|
||||||
|
* @param params 见 HeroMoreParams;eid 对应实体不存在时立即关闭
|
||||||
|
*/
|
||||||
|
onAdded(params: HeroMoreParams = {}) {
|
||||||
|
this.isClosing = false;
|
||||||
|
this.refreshElapsed = 0;
|
||||||
|
this.skillDescCache = "";
|
||||||
|
|
||||||
|
const eid = params?.eid ?? 0;
|
||||||
|
if (!eid) {
|
||||||
|
this.closeSelf();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 按 eid 查找英雄实体(与 HInfoComp 口径一致)
|
||||||
|
let foundModel: HeroAttrsComp | null = null;
|
||||||
|
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||||||
|
if (entity.eid === eid) {
|
||||||
|
foundModel = entity.get(HeroAttrsComp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!foundModel) {
|
||||||
|
this.closeSelf();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.model = foundModel;
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帧更新:降频刷新实时属性;英雄实体失效(死亡/卖出)时自动关闭。
|
||||||
|
*/
|
||||||
|
update(dt: number) {
|
||||||
|
if (this.isClosing || !this.model) return;
|
||||||
|
if (!(this.model as any).ent) {
|
||||||
|
this.closeSelf();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.refreshElapsed += dt;
|
||||||
|
if (this.refreshElapsed < HeroMoreCom.REFRESH_INTERVAL) return;
|
||||||
|
this.refreshElapsed = 0;
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 显示刷新 ========================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新显示:8 行次级属性(总值 + 附加值)+ 技能描述富文本。
|
||||||
|
* 附加 = 最终生效值 - 基础配置值,与原 HeroBoxComp 口径一致。
|
||||||
|
*/
|
||||||
|
private refresh() {
|
||||||
|
if (!this.model) return;
|
||||||
|
|
||||||
|
// ---- 攻速(攻击间隔,秒;速度提升 → 间隔变短,附加值为负) ----
|
||||||
|
const skillIds = this.model.getSkillIds();
|
||||||
|
const displaySkillId = skillIds[1] ?? skillIds[0] ?? 0;
|
||||||
|
const baseCd = displaySkillId ? (this.model.skills[displaySkillId]?.cd ?? 0) : 0;
|
||||||
|
const finalCd = displaySkillId ? this.model.getEffectiveSkillCd(displaySkillId) : 0;
|
||||||
|
this.setStatRow(this.speed_all_label, this.speed_plus_label, finalCd, finalCd - baseCd, "s", 1);
|
||||||
|
|
||||||
|
// ---- 击晕率 / 暴击率 / 暴击伤害(百分点) ----
|
||||||
|
const finalStun = this.model.getFinalStunChance();
|
||||||
|
this.setStatRow(this.stun_all_label, this.stun_plus_label, finalStun, finalStun - (this.model.stun_chance ?? 0), "%");
|
||||||
|
|
||||||
|
const finalCrit = this.model.getFinalCritical();
|
||||||
|
this.setStatRow(this.crt_all_label, this.crt_plus_label, finalCrit, finalCrit - (this.model.critical ?? 0), "%");
|
||||||
|
|
||||||
|
const finalCritDmg = this.model.getFinalCritDamage();
|
||||||
|
this.setStatRow(this.crit_dmg_all_label, this.crit_dmg_plus_label, finalCritDmg, finalCritDmg - (this.model.crit_damage ?? 0), "%");
|
||||||
|
|
||||||
|
// ---- 冰冻率 / 击退率 / 穿透率 / 风怒率(百分点) ----
|
||||||
|
const finalFreeze = this.model.getFinalFreezeChance();
|
||||||
|
this.setStatRow(this.freeze_all_label, this.freeze_plus_label, finalFreeze, finalFreeze - (this.model.freeze_chance ?? 0), "%");
|
||||||
|
|
||||||
|
const finalKnockback = this.model.getFinalKnockbackChance();
|
||||||
|
this.setStatRow(this.knockback_all_label, this.knockback_plus_label, finalKnockback, finalKnockback - (this.model.knockback_chance ?? 0), "%");
|
||||||
|
|
||||||
|
const finalPuncture = this.model.getFinalPunctureChance();
|
||||||
|
this.setStatRow(this.puncture_all_label, this.puncture_plus_label, finalPuncture, finalPuncture - (this.model.puncture_chance ?? 0), "%");
|
||||||
|
|
||||||
|
const finalWfuny = this.model.getFinalWindFury();
|
||||||
|
this.setStatRow(this.wfuny_all_label, this.wfuny_plus_label, finalWfuny, finalWfuny - (this.model.wfuny ?? 0), "%");
|
||||||
|
|
||||||
|
// ---- 技能描述(全档位合并:每技能一行,已/未激活状态 + 数值富文本高亮) ----
|
||||||
|
if (this.skill_label && this.skill_label.isValid) {
|
||||||
|
// 缓存未变则跳过重设,避免降频内 RichText 重复重解析排版
|
||||||
|
// showName=false 不显示技能名仅保留效果;showLocked=true 合并全档位
|
||||||
|
const desc = buildSkillDescRich(this.model, false, true);
|
||||||
|
if (desc !== this.skillDescCache) {
|
||||||
|
this.skillDescCache = desc;
|
||||||
|
this.skill_label.string = desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入一行属性(总值 + 附加值)。
|
||||||
|
* 总值始终显示(0 不隐藏);附加值为 0 时隐藏,正/负分别显示 (+x) / (x)。
|
||||||
|
*
|
||||||
|
* @param allLabel 总值标签(可为空,空时跳过)
|
||||||
|
* @param plusLabel 附加值标签(可为空)
|
||||||
|
* @param total 总数值(最终生效值)
|
||||||
|
* @param bonus 附加数值(最终值 - 基础值)
|
||||||
|
* @param suffix 数值后缀(如 "%")
|
||||||
|
* @param decimals 保留小数位数(默认 0,取整)
|
||||||
|
*/
|
||||||
|
private setStatRow(allLabel: Label | null, plusLabel: Label | null, total: number, bonus: number, suffix: string = "", decimals: number = 0) {
|
||||||
|
const fmt = (v: number) => decimals > 0 ? v.toFixed(decimals) : `${Math.floor(v)}`;
|
||||||
|
if (allLabel && allLabel.isValid) {
|
||||||
|
allLabel.string = `${fmt(total)}${suffix}`;
|
||||||
|
}
|
||||||
|
if (plusLabel && plusLabel.isValid) {
|
||||||
|
if (bonus !== 0) {
|
||||||
|
plusLabel.string = bonus > 0 ? `(+${fmt(bonus)}${suffix})` : `(${fmt(bonus)}${suffix})`;
|
||||||
|
} else {
|
||||||
|
plusLabel.string = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== 交互 ========================
|
||||||
|
|
||||||
|
/** 点击面板任意处(含遮罩)关闭 */
|
||||||
|
private onTouchEndClose() {
|
||||||
|
oops.audio.playEffect("music/button");
|
||||||
|
this.closeSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭弹窗(交还 oops.gui 缓存,下次打开复用节点并重新触发 onAdded) */
|
||||||
|
private closeSelf() {
|
||||||
|
if (this.isClosing) return;
|
||||||
|
this.isClosing = true;
|
||||||
|
oops.gui.removeByNode(this.node);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
assets/script/game/map/HeroMoreCom.ts.meta
Normal file
9
assets/script/game/map/HeroMoreCom.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "6b2e9f14-7a3c-4d85-9e62-c4f8a1b7d3e0",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -179,14 +179,14 @@ export class MissionCardComp extends CCComp {
|
|||||||
/** 是否已召唤过英雄(用于控制其他面板按钮是否可点击) */
|
/** 是否已召唤过英雄(用于控制其他面板按钮是否可点击) */
|
||||||
private hasCalledHero: boolean = false;
|
private hasCalledHero: boolean = false;
|
||||||
|
|
||||||
// ======================== 面板轮播状态 ========================
|
// ======================== 面板划出状态 ========================
|
||||||
|
|
||||||
/** 面板滑动间距(单面板宽度,运行时由 initPanelLayout 推导) */
|
|
||||||
private panelSlideWidth: number = 720;
|
|
||||||
/** 当前激活的面板索引:0=英雄, 1=装备, 2=技能, 3=药品 */
|
/** 当前激活的面板索引:0=英雄, 1=装备, 2=技能, 3=药品 */
|
||||||
private currentPanelIndex: number = 0;
|
private currentPanelIndex: number = 0;
|
||||||
/** 各面板基准 Y 坐标缓存 */
|
/** 面板显示位置(英雄面板基准位置,运行时从场景读取) */
|
||||||
private panelBaseYs: number[] = [];
|
private panelShowPos: Vec3 = new Vec3();
|
||||||
|
/** 各覆盖面板(装备/技能/药品)的隐藏 Y 坐标(屏幕外底部,从场景初始位置读取) */
|
||||||
|
private panelHideYs: number[] = [];
|
||||||
/** 是否已初始化面板布局 */
|
/** 是否已初始化面板布局 */
|
||||||
private hasInitPanelLayout: boolean = false;
|
private hasInitPanelLayout: boolean = false;
|
||||||
/** 是否已缓存卡牌面板基准缩放 */
|
/** 是否已缓存卡牌面板基准缩放 */
|
||||||
@@ -197,10 +197,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);
|
||||||
/** 卡牌面板显示位置 Y 坐标 */
|
/** 卡牌面板隐藏 Y 坐标(场景初始位置,屏幕外底部,由 initPanelLayout 缓存) */
|
||||||
private readonly cardPanelShowY: number = 0;
|
private cardsPanHideY: number = 0;
|
||||||
/** 卡牌面板隐藏位置 Y 坐标(向下 700) */
|
|
||||||
private readonly cardPanelHideY: number = -700;
|
|
||||||
/** 卡牌面板显示/隐藏动画时长 */
|
/** 卡牌面板显示/隐藏动画时长 */
|
||||||
private readonly cardPanelAnimDuration: number = 0.25;
|
private readonly cardPanelAnimDuration: number = 0.25;
|
||||||
|
|
||||||
@@ -504,7 +502,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 技能面板按钮回调:滑动到技能面板(index=2)。
|
* 技能面板按钮回调:技能面板从底部划出(index=2)。
|
||||||
*/
|
*/
|
||||||
private onShowSkillsClick() {
|
private onShowSkillsClick() {
|
||||||
oops.audio.playEffect("music/button");
|
oops.audio.playEffect("music/button");
|
||||||
@@ -514,7 +512,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
// ======================== 商店面板 ========================
|
// ======================== 商店面板 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商店面板按钮回调:滑动到药品商店面板(index=3)。
|
* 商店面板按钮回调:药品商店面板从底部划出(index=3)。
|
||||||
*/
|
*/
|
||||||
private onShowShopClick() {
|
private onShowShopClick() {
|
||||||
oops.audio.playEffect("music/button");
|
oops.audio.playEffect("music/button");
|
||||||
@@ -854,7 +852,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.playButtonResetAnim(this.cards_chou);
|
this.playButtonResetAnim(this.cards_chou);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 面板轮播系统 ========================
|
// ======================== 面板底部划出系统 ========================
|
||||||
|
|
||||||
/** 面板节点数组(顺序:英雄→装备→技能→药品) */
|
/** 面板节点数组(顺序:英雄→装备→技能→药品) */
|
||||||
private getPanelNodes(): Node[] {
|
private getPanelNodes(): Node[] {
|
||||||
@@ -867,46 +865,51 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化面板轮播布局:
|
* 初始化面板布局:
|
||||||
* 1. 缓存各面板基准 Y 坐标。
|
* 1. 以英雄面板位置为基准显示位置(英雄面板固定不动)。
|
||||||
* 2. 推导面板滑动间距(取场景设计分辨率宽度或默认 720)。
|
* 2. 缓存各覆盖面板(装备/技能/药品)的初始位置作为隐藏位置(屏幕外底部)。
|
||||||
* 3. 将所有面板排列为水平队列,初始显示英雄面板(index=0)。
|
* 3. 将覆盖面板归位到隐藏位置,初始显示英雄面板(index=0)。
|
||||||
*/
|
*/
|
||||||
private initPanelLayout() {
|
private initPanelLayout() {
|
||||||
if (this.hasInitPanelLayout) return;
|
if (this.hasInitPanelLayout) return;
|
||||||
const panels = this.getPanelNodes();
|
const panels = this.getPanelNodes();
|
||||||
this.panelBaseYs = [];
|
this.panelHideYs = [];
|
||||||
|
|
||||||
// 以第一个有效面板的位置为基准,推导滑动间距
|
const heroNode = panels[0];
|
||||||
let refX = 0;
|
if (heroNode && heroNode.isValid) {
|
||||||
for (const p of panels) {
|
heroNode.active = true;
|
||||||
if (p && p.isValid) {
|
this.panelShowPos.set(heroNode.getPosition());
|
||||||
refX = p.getPosition().x;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.panelSlideWidth = Math.abs(refX) * 2 || 720;
|
|
||||||
if (this.panelSlideWidth < 400) this.panelSlideWidth = 720;
|
|
||||||
|
|
||||||
for (let i = 0; i < panels.length; i++) {
|
// 覆盖面板:缓存场景初始位置(屏幕外底部)作为隐藏位置,X 对齐显示位置
|
||||||
|
for (let i = 1; i < panels.length; i++) {
|
||||||
const node = panels[i];
|
const node = panels[i];
|
||||||
if (!node || !node.isValid) {
|
if (!node || !node.isValid) {
|
||||||
this.panelBaseYs.push(0);
|
this.panelHideYs.push(this.panelShowPos.y);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
node.active = true;
|
node.active = true;
|
||||||
const pos = node.getPosition();
|
const pos = node.getPosition();
|
||||||
this.panelBaseYs.push(pos.y);
|
this.panelHideYs.push(pos.y);
|
||||||
// 初始位置:当前面板(英雄)居中,其余按序排列
|
// index 0 为英雄面板,隐藏列表从 index 1 起
|
||||||
const targetX = (i - this.currentPanelIndex) * this.panelSlideWidth;
|
node.setPosition(this.panelShowPos.x, pos.y, pos.z);
|
||||||
node.setPosition(targetX, pos.y, pos.z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 卡牌面板与其他覆盖面板同级:缓存场景初始位置(屏幕外底部)作为隐藏位置,X 对齐显示位置
|
||||||
|
if (this.cardPanNode && this.cardPanNode.isValid) {
|
||||||
|
const pos = this.cardPanNode.getPosition();
|
||||||
|
this.cardsPanHideY = pos.y;
|
||||||
|
this.cardPanNode.setPosition(this.panelShowPos.x, pos.y, pos.z);
|
||||||
|
}
|
||||||
|
|
||||||
this.hasInitPanelLayout = true;
|
this.hasInitPanelLayout = true;
|
||||||
this.updatePanelButtonStates();
|
this.updatePanelButtonStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 滑动到指定面板。
|
* 切换到指定面板:
|
||||||
|
* 英雄面板(index=0)固定不动;
|
||||||
|
* 选中的覆盖面板从底部划入到显示位置,其余覆盖面板划出回底部隐藏位置。
|
||||||
* @param index 0=英雄, 1=装备, 2=技能, 3=药品
|
* @param index 0=英雄, 1=装备, 2=技能, 3=药品
|
||||||
*/
|
*/
|
||||||
private slideToPanel(index: number) {
|
private slideToPanel(index: number) {
|
||||||
@@ -916,14 +919,14 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.currentPanelIndex = index;
|
this.currentPanelIndex = index;
|
||||||
const duration = 0.25;
|
const duration = 0.25;
|
||||||
|
|
||||||
for (let i = 0; i < panels.length; i++) {
|
// 覆盖面板从 index 1 开始(index 0 英雄面板固定不动)
|
||||||
|
for (let i = 1; i < panels.length; i++) {
|
||||||
const node = panels[i];
|
const node = panels[i];
|
||||||
if (!node || !node.isValid) continue;
|
if (!node || !node.isValid) continue;
|
||||||
const targetX = (i - index) * this.panelSlideWidth;
|
const targetY = i === index ? this.panelShowPos.y : (this.panelHideYs[i - 1] ?? node.getPosition().y);
|
||||||
const baseY = this.panelBaseYs[i] ?? node.getPosition().y;
|
|
||||||
Tween.stopAllByTarget(node);
|
Tween.stopAllByTarget(node);
|
||||||
tween(node)
|
tween(node)
|
||||||
.to(duration, { position: new Vec3(targetX, baseY, 0) }, { easing: 'quadOut' })
|
.to(duration, { position: new Vec3(this.panelShowPos.x, targetY, 0) }, { easing: 'quadOut' })
|
||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -960,7 +963,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 英雄面板按钮回调:滑动到英雄面板(index=0)。
|
* 英雄面板按钮回调:收起覆盖面板,回到英雄面板(index=0)。
|
||||||
*/
|
*/
|
||||||
private onShowHerosClick() {
|
private onShowHerosClick() {
|
||||||
oops.audio.playEffect("music/button");
|
oops.audio.playEffect("music/button");
|
||||||
@@ -1020,7 +1023,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 显示卡牌面板:从底部向上滑入到 Y=0
|
* 显示卡牌面板:从底部向上滑入到面板显示位置(panelShowPos)
|
||||||
*/
|
*/
|
||||||
private onShowCardsClick() {
|
private onShowCardsClick() {
|
||||||
oops.audio.playEffect("music/button");
|
oops.audio.playEffect("music/button");
|
||||||
@@ -1028,24 +1031,24 @@ export class MissionCardComp extends CCComp {
|
|||||||
this.cardPanNode.active = true;
|
this.cardPanNode.active = true;
|
||||||
Tween.stopAllByTarget(this.cardPanNode);
|
Tween.stopAllByTarget(this.cardPanNode);
|
||||||
tween(this.cardPanNode)
|
tween(this.cardPanNode)
|
||||||
.to(this.cardPanelAnimDuration, { position: new Vec3(0, this.cardPanelShowY, 0) }, { easing: 'quadOut' })
|
.to(this.cardPanelAnimDuration, { position: new Vec3(this.panelShowPos.x, this.panelShowPos.y, 0) }, { easing: 'quadOut' })
|
||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 隐藏卡牌面板:向下滑出到 Y=-700
|
* 隐藏卡牌面板:向下滑出到其隐藏位置(cardsPanHideY,屏幕外底部)
|
||||||
*/
|
*/
|
||||||
private onCloseCardsClick() {
|
private onCloseCardsClick() {
|
||||||
oops.audio.playEffect("music/button");
|
oops.audio.playEffect("music/button");
|
||||||
this.hideCardsPanel();
|
this.hideCardsPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 隐藏卡牌面板:向下滑出到 Y=-700(不含按钮音效,供系统逻辑复用) */
|
/** 隐藏卡牌面板:向下滑出到隐藏位置(不含按钮音效,供系统逻辑复用) */
|
||||||
private hideCardsPanel() {
|
private hideCardsPanel() {
|
||||||
if (!this.cardPanNode || !this.cardPanNode.isValid) return;
|
if (!this.cardPanNode || !this.cardPanNode.isValid) return;
|
||||||
Tween.stopAllByTarget(this.cardPanNode);
|
Tween.stopAllByTarget(this.cardPanNode);
|
||||||
tween(this.cardPanNode)
|
tween(this.cardPanNode)
|
||||||
.to(this.cardPanelAnimDuration, { position: new Vec3(0, this.cardPanelHideY, 0) }, { easing: 'quadIn' })
|
.to(this.cardPanelAnimDuration, { position: new Vec3(this.panelShowPos.x, this.cardsPanHideY, 0) }, { easing: 'quadIn' })
|
||||||
.call(() => {
|
.call(() => {
|
||||||
if (this.cardPanNode && this.cardPanNode.isValid) {
|
if (this.cardPanNode && this.cardPanNode.isValid) {
|
||||||
this.cardPanNode.active = false;
|
this.cardPanNode.active = false;
|
||||||
@@ -1057,7 +1060,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
// ======================== 装备商店面板 ========================
|
// ======================== 装备商店面板 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 装备面板按钮回调:滑动到装备商店面板(index=1)。
|
* 装备面板按钮回调:装备商店面板从底部划出(index=1)。
|
||||||
*/
|
*/
|
||||||
private onShowEquipsClick() {
|
private onShowEquipsClick() {
|
||||||
oops.audio.playEffect("music/button");
|
oops.audio.playEffect("music/button");
|
||||||
@@ -1451,8 +1454,8 @@ export class MissionCardComp extends CCComp {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 进入准备阶段:
|
* 进入准备阶段:
|
||||||
* - 初始化面板轮播布局(如尚未初始化)。
|
* - 初始化面板布局(如尚未初始化)。
|
||||||
* - 滑动到英雄面板(index=0),作为默认展示。
|
* - 收起覆盖面板回到英雄面板(index=0),作为默认展示。
|
||||||
* - 激活 showHeros 按钮可见性。
|
* - 激活 showHeros 按钮可见性。
|
||||||
* - 显示卡牌面板(从底部滑入)。
|
* - 显示卡牌面板(从底部滑入)。
|
||||||
*/
|
*/
|
||||||
@@ -1469,10 +1472,10 @@ export class MissionCardComp extends CCComp {
|
|||||||
}
|
}
|
||||||
// 刷新所有刷新按钮的费用显示(含刷新石/金币切换与置灰状态)
|
// 刷新所有刷新按钮的费用显示(含刷新石/金币切换与置灰状态)
|
||||||
this.updateDrawCostUI();
|
this.updateDrawCostUI();
|
||||||
// 显示卡牌面板(默认进入准备阶段时展开)
|
// 显示卡牌面板(默认进入准备阶段时展开,位置与其他面板保持一致)
|
||||||
if (this.cardPanNode && this.cardPanNode.isValid) {
|
if (this.cardPanNode && this.cardPanNode.isValid) {
|
||||||
this.cardPanNode.active = true;
|
this.cardPanNode.active = true;
|
||||||
this.cardPanNode.setPosition(0, this.cardPanelShowY, 0);
|
this.cardPanNode.setPosition(this.panelShowPos.x, this.panelShowPos.y, 0);
|
||||||
mLogger.log(this.debugMode, "MissionCardComp", "enterPreparePhase cardPanNode", {
|
mLogger.log(this.debugMode, "MissionCardComp", "enterPreparePhase cardPanNode", {
|
||||||
active: this.cardPanNode.active,
|
active: this.cardPanNode.active,
|
||||||
position: this.cardPanNode.position,
|
position: this.cardPanNode.position,
|
||||||
@@ -1485,7 +1488,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
cards_nodeChildren: this.cards_node?.children.length
|
cards_nodeChildren: this.cards_node?.children.length
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 滑动到英雄面板
|
// 收起覆盖面板,回到英雄面板
|
||||||
this.slideToPanel(0);
|
this.slideToPanel(0);
|
||||||
|
|
||||||
mLogger.log(this.debugMode, "MissionCardComp", "enterPreparePhase herosPanNode", {
|
mLogger.log(this.debugMode, "MissionCardComp", "enterPreparePhase herosPanNode", {
|
||||||
@@ -1493,7 +1496,7 @@ export class MissionCardComp extends CCComp {
|
|||||||
herosPanNodePos: this.herosPanNode?.position,
|
herosPanNodePos: this.herosPanNode?.position,
|
||||||
herosPanNodeWorldPos: this.herosPanNode?.worldPosition,
|
herosPanNodeWorldPos: this.herosPanNode?.worldPosition,
|
||||||
currentPanelIndex: this.currentPanelIndex,
|
currentPanelIndex: this.currentPanelIndex,
|
||||||
panelSlideWidth: this.panelSlideWidth
|
panelShowPos: this.panelShowPos
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user