1. 重构IBoxComp组件,移除旧的标题名称渲染逻辑 2. 添加动态背景节点,根据RichText内容自动调整高度 3. 优化cardbg和herobox预制体样式,调整尺寸与透明度 4. 新增通用背景预制体hbg,支持多配色快速复用 5. 重写ibox预制体结构,适配新的组件逻辑
191 lines
7.2 KiB
TypeScript
191 lines
7.2 KiB
TypeScript
/**
|
||
* @file IBoxComp.ts
|
||
* @description 技能/装备详情弹窗组件(IBox,UI 视图层)
|
||
*
|
||
* 职责:
|
||
* 1. 作为详情弹窗,展示单个技能或装备的 **功能描述详情**。
|
||
* 2. 通过 init(args) 接收技能 UUID、装备 UUID 或运行时技能数据。
|
||
* 3. 使用单个 RichText 以 BBCode 展示名称(蓝色)、CD/持续时间(亮绿色)、结构化效果描述。
|
||
* 4. 点击弹窗任意区域关闭自身。
|
||
*
|
||
* 关键设计:
|
||
* - 复用 HeroSkillDesc 的结构化模板渲染,数值实时从配置读取,杜绝漂移。
|
||
* - 技能:展示名称、等级、CD、效果描述(SkillDescSet 模板)。
|
||
* - 装备:展示名称、全部驻场光环词条(FieldDescSet 模板)。
|
||
* - 不走 oops.gui,由调用方手动实例化并添加到场景,通过 init() 初始化。
|
||
*
|
||
* 依赖:
|
||
* - HeroSkillDesc(buildSingleSkillRich / buildEquipRich)—— 描述生成
|
||
* - SkillSet / FieldSkillSet(SkillSet)—— 技能/光环静态配置
|
||
* - EquipSet(findEquipByUuid)—— 装备静态配置
|
||
*/
|
||
import { _decorator, Node, NodeEventType, RichText, UITransform, Widget } 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 { SkillSet, SkillOverrides } from "../common/config/SkillSet";
|
||
import { buildEquipRich, buildSingleSkillRich } from "../common/config/HeroSkillDesc";
|
||
import { findEquipByUuid } from "../common/config/EquipSet";
|
||
const { ccclass, property } = _decorator;
|
||
|
||
/** IBox 初始化参数 */
|
||
export interface IBoxInitArgs {
|
||
/** SkillSet 技能 UUID(技能详情模式) */
|
||
skillUuid?: number;
|
||
/** 技能等级(默认 1) */
|
||
skillLv?: number;
|
||
/** 技能覆写参数(如药水卡自定义档位) */
|
||
overrides?: SkillOverrides;
|
||
/** EquipSet 装备 UUID(装备详情模式) */
|
||
equipUuid?: number;
|
||
/** 运行时技能数据(运行时技能详情模式,取第一个技能展示) */
|
||
skills?: Record<number, { uuid: number; lv: number; cd: number; ccd: number }>;
|
||
}
|
||
|
||
/**
|
||
* IBoxComp —— 技能/装备详情弹窗视图组件
|
||
*
|
||
* 由调用方通过 instantiate + addChild 手动实例化,然后调用 init(args) 初始化。
|
||
* 示例:
|
||
* const node = instantiate(this.iboxPrefab);
|
||
* this.node.addChild(node);
|
||
* node.getComponent(IBoxComp).init({ skillUuid: 6301, anchorPos: this.node.worldPosition });
|
||
*/
|
||
@ccclass('IBoxComp')
|
||
@ecs.register('IBoxComp', false)
|
||
export class IBoxComp extends CCComp {
|
||
// ======================== 编辑器绑定节点 ========================
|
||
@property(RichText)
|
||
massage_node: RichText = null!
|
||
|
||
/** 背景节点(高度随 RichText 内容动态调整) */
|
||
@property(Node)
|
||
bg_node: Node = null!
|
||
|
||
/** Bg 节点高度上下留白(RichText 顶部间距 + 底部间距) */
|
||
private static readonly BG_PADDING_V: number = 24;
|
||
/** Bg 最小高度(防止内容过少时背景塌缩) */
|
||
private static readonly BG_MIN_HEIGHT: number = 60;
|
||
|
||
/**
|
||
* 初始化并渲染弹窗内容。
|
||
* @param args 见 IBoxInitArgs
|
||
*/
|
||
public init(args: IBoxInitArgs) {
|
||
this.render(args);
|
||
}
|
||
|
||
/** 绑定点击关闭事件 */
|
||
onLoad() {
|
||
this.node.on(NodeEventType.TOUCH_END, this.onTapClose, this);
|
||
}
|
||
|
||
/** 解绑点击事件 */
|
||
onDestroy() {
|
||
super.onDestroy();
|
||
if (this.node && this.node.isValid) {
|
||
this.node.off(NodeEventType.TOUCH_END, this.onTapClose, this);
|
||
}
|
||
}
|
||
|
||
/** ECS 组件移除时销毁节点 */
|
||
reset() {
|
||
this.node.destroy();
|
||
}
|
||
|
||
// ======================== 渲染逻辑 ========================
|
||
|
||
/**
|
||
* 主渲染方法:根据 args 模式分发到技能详情或装备详情。
|
||
*/
|
||
private render(args: IBoxInitArgs) {
|
||
// 装备详情模式
|
||
if (args?.equipUuid !== undefined) {
|
||
this.renderEquip(args.equipUuid);
|
||
} else if (args?.skills) {
|
||
// 运行时技能详情模式(取第一个技能)
|
||
const first = Object.values(args.skills)[0];
|
||
if (first) {
|
||
this.renderSkill(first.uuid, first.lv);
|
||
}
|
||
} else if (args?.skillUuid !== undefined) {
|
||
// 技能详情模式
|
||
this.renderSkill(args.skillUuid, args.skillLv ?? 1, args.overrides);
|
||
} else {
|
||
this.setRichText("暂无信息");
|
||
}
|
||
// RichText 排版需一帧完成,延迟同步 Bg 高度
|
||
this.scheduleOnce(() => this.syncBgHeight(), 0);
|
||
}
|
||
|
||
/**
|
||
* 根据 RichText 实际内容高度,动态调整 Bg 节点高度与位置。
|
||
* RichText 锚点已设为 (0.5, 1),从顶部向下生长;
|
||
* Bg 锚点 (0.5, 0.5),向上居中扩展,顶部对齐 RichText 顶。
|
||
*/
|
||
private syncBgHeight() {
|
||
if (!this.massage_node?.isValid) return;
|
||
const richUT = this.massage_node.getComponent(UITransform);
|
||
if (!richUT) return;
|
||
|
||
const contentH = Math.max(richUT.height, 0);
|
||
const bgH = Math.max(contentH + IBoxComp.BG_PADDING_V, IBoxComp.BG_MIN_HEIGHT);
|
||
|
||
const bgNode = this.bg_node ?? this.node.getChildByName("bg");
|
||
if (!bgNode?.isValid) return;
|
||
const bgUT = bgNode.getComponent(UITransform);
|
||
if (!bgUT) return;
|
||
|
||
// Bg 节点在 prefab 中挂了 Widget(顶底对齐),会覆盖代码设置的高度,先禁用
|
||
const widget = bgNode.getComponent(Widget);
|
||
if (widget) widget.enabled = false;
|
||
|
||
bgUT.height = bgH;
|
||
// Bg 锚点在中心,高度为 bgH 时其中心应位于 RichText 顶部向下 bgH/2 处
|
||
// RichText 锚点 (0.5,1),其顶部 y = pos.y;Bg 中心 y = pos.y - bgH/2
|
||
const richPos = this.massage_node.node.position;
|
||
bgNode.setPosition(bgNode.position.x, richPos.y - bgH / 2, bgNode.position.z);
|
||
}
|
||
|
||
/**
|
||
* 渲染技能详情:名称、等级、CD、结构化效果描述。
|
||
* @param skillUuid SkillSet 技能 UUID
|
||
* @param lv 技能等级
|
||
* @param overrides 可选覆写参数
|
||
*/
|
||
private renderSkill(skillUuid: number, lv: number, overrides?: SkillOverrides) {
|
||
const skill = SkillSet[skillUuid];
|
||
if (!skill) {
|
||
this.setRichText("未知技能");
|
||
return;
|
||
}
|
||
this.setRichText(buildSingleSkillRich(skillUuid, overrides, lv));
|
||
}
|
||
|
||
/**
|
||
* 渲染装备详情:名称、全部驻场光环词条。
|
||
* @param equipUuid EquipSet 装备 UUID
|
||
*/
|
||
private renderEquip(equipUuid: number) {
|
||
const equip = findEquipByUuid(equipUuid);
|
||
if (!equip) {
|
||
this.setRichText("未知装备");
|
||
return;
|
||
}
|
||
this.setRichText(buildEquipRich(equip.field ?? []));
|
||
}
|
||
|
||
/** 将 BBCode 文本写入 RichText */
|
||
private setRichText(text: string) {
|
||
if (this.massage_node && this.massage_node.isValid) {
|
||
this.massage_node.string = text;
|
||
}
|
||
}
|
||
|
||
/** 点击弹窗任意区域关闭自身 */
|
||
private onTapClose() {
|
||
if (this.node && this.node.isValid) {
|
||
this.node.destroy();
|
||
}
|
||
}
|
||
}
|