refactor: 重构英雄列表UI与图标加载逻辑
1. 替换list-me预制体为list_me,统一命名规范 2. 调整ui5.plist的精灵边框参数,修复UI显示精度 3. 重写英雄图标加载逻辑,新增异步加载竞态保护与动画首帧缓存 4. 优化cSkill预制体的布局与样式参数
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,12 +2,12 @@
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "b2a3067a-af88-4ff7-acdd-7474b8a163d8",
|
||||
"uuid": "b0828069-23cf-49f1-be72-7986cc97f33c",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "list-me"
|
||||
"syncNodeName": "list_me"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2370,10 +2370,10 @@
|
||||
"height": 67,
|
||||
"rawWidth": 61,
|
||||
"rawHeight": 67,
|
||||
"borderTop": 33.5,
|
||||
"borderBottom": 33.5,
|
||||
"borderLeft": 30.5,
|
||||
"borderRight": 30.5,
|
||||
"borderTop": 33,
|
||||
"borderBottom": 33,
|
||||
"borderLeft": 30,
|
||||
"borderRight": 30,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
@@ -2462,10 +2462,10 @@
|
||||
"height": 55,
|
||||
"rawWidth": 53,
|
||||
"rawHeight": 55,
|
||||
"borderTop": 27.5,
|
||||
"borderBottom": 27.5,
|
||||
"borderLeft": 27.5,
|
||||
"borderRight": 25.5,
|
||||
"borderTop": 27,
|
||||
"borderBottom": 27,
|
||||
"borderLeft": 27,
|
||||
"borderRight": 25,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
@@ -5130,8 +5130,8 @@
|
||||
"height": 25,
|
||||
"rawWidth": 26,
|
||||
"rawHeight": 25,
|
||||
"borderTop": 12.5,
|
||||
"borderBottom": 12.5,
|
||||
"borderTop": 12,
|
||||
"borderBottom": 12,
|
||||
"borderLeft": 13,
|
||||
"borderRight": 13,
|
||||
"packable": true,
|
||||
@@ -5176,8 +5176,8 @@
|
||||
"height": 25,
|
||||
"rawWidth": 26,
|
||||
"rawHeight": 25,
|
||||
"borderTop": 12.5,
|
||||
"borderBottom": 12.5,
|
||||
"borderTop": 12,
|
||||
"borderBottom": 12,
|
||||
"borderLeft": 13,
|
||||
"borderRight": 13,
|
||||
"packable": true,
|
||||
@@ -5222,8 +5222,8 @@
|
||||
"height": 33,
|
||||
"rawWidth": 30,
|
||||
"rawHeight": 33,
|
||||
"borderTop": 16.5,
|
||||
"borderBottom": 16.5,
|
||||
"borderTop": 16,
|
||||
"borderBottom": 16,
|
||||
"borderLeft": 15,
|
||||
"borderRight": 15,
|
||||
"packable": true,
|
||||
@@ -5314,10 +5314,10 @@
|
||||
"height": 30,
|
||||
"rawWidth": 30,
|
||||
"rawHeight": 30,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"borderTop": 15,
|
||||
"borderBottom": 15,
|
||||
"borderLeft": 15,
|
||||
"borderRight": 15,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
@@ -5360,10 +5360,10 @@
|
||||
"height": 30,
|
||||
"rawWidth": 30,
|
||||
"rawHeight": 30,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"borderTop": 15,
|
||||
"borderBottom": 15,
|
||||
"borderLeft": 15,
|
||||
"borderRight": 15,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
@@ -5774,10 +5774,10 @@
|
||||
"height": 75,
|
||||
"rawWidth": 77,
|
||||
"rawHeight": 75,
|
||||
"borderTop": 37.5,
|
||||
"borderBottom": 37.5,
|
||||
"borderLeft": 37.5,
|
||||
"borderRight": 37.5,
|
||||
"borderTop": 37,
|
||||
"borderBottom": 37,
|
||||
"borderLeft": 37,
|
||||
"borderRight": 37,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
@@ -5820,10 +5820,10 @@
|
||||
"height": 75,
|
||||
"rawWidth": 77,
|
||||
"rawHeight": 75,
|
||||
"borderTop": 38.5,
|
||||
"borderBottom": 38.5,
|
||||
"borderLeft": 37.5,
|
||||
"borderRight": 37.5,
|
||||
"borderTop": 38,
|
||||
"borderBottom": 38,
|
||||
"borderLeft": 37,
|
||||
"borderRight": 37,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* - 英雄卡效果事件为 GameEvent.CallHero,而非 UseSkillCard。
|
||||
*/
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { _decorator, Node, Sprite, Label, NodeEventType, Vec3, Tween, tween, UIOpacity } from "cc";
|
||||
import { _decorator, Node, Sprite, Label, NodeEventType, Vec3, Tween, tween, UIOpacity, SpriteFrame, resources, AnimationClip } 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 { CardConfig, CardType } from "../common/config/CardSet";
|
||||
@@ -91,6 +91,10 @@ export class CHeroComp extends CCComp {
|
||||
private restPosition: Vec3 = new Vec3();
|
||||
/** 透明度组件(用于淡入淡出) */
|
||||
private opacityComp: UIOpacity | null = null;
|
||||
/** 图标视觉令牌(异步加载竞态保护) */
|
||||
private iconVisualToken: number = 0;
|
||||
/** 当前显示的英雄 UUID(避免相同 UUID 重复加载动画) */
|
||||
private iconHeroUuid: number = 0;
|
||||
|
||||
// ======================== 生命周期 ========================
|
||||
|
||||
@@ -299,10 +303,8 @@ export class CHeroComp extends CCComp {
|
||||
|
||||
// 图标
|
||||
if (this.icon_node) {
|
||||
const iconId = this.getIconId(renderUuid);
|
||||
if (iconId) {
|
||||
this.updateIcon(this.icon_node, iconId);
|
||||
}
|
||||
this.iconVisualToken += 1;
|
||||
this.updateHeroAnimation(this.icon_node, renderUuid, this.iconVisualToken);
|
||||
}
|
||||
|
||||
// 购买按钮费用显示
|
||||
@@ -446,30 +448,70 @@ export class CHeroComp extends CCComp {
|
||||
return model.hero_uuid ?? null;
|
||||
}
|
||||
|
||||
/** 获取英雄图标 id:优先卡牌自定义 icon,其次英雄 path,兜底 uuid */
|
||||
private getIconId(renderUuid: number): string {
|
||||
if (!this.cardData) return "";
|
||||
if (this.cardData.icon) return this.cardData.icon;
|
||||
const hero = HeroInfo[renderUuid];
|
||||
return hero?.path || `${renderUuid}`;
|
||||
/**
|
||||
* 为英雄图标加载 idle 动画剪辑,提取首帧 SpriteFrame 静止展示。
|
||||
* 与 HInfoComp 的 updateHeroAnimation 一致,零运行时开销。
|
||||
* @param node 图标节点
|
||||
* @param uuid 英雄 UUID
|
||||
* @param token 视觉令牌
|
||||
*/
|
||||
private updateHeroAnimation(node: Node, uuid: number, token: number) {
|
||||
if (!node || !node.isValid) return;
|
||||
|
||||
// 相同英雄 UUID 不重复加载,避免刷新卡池时闪烁
|
||||
if (this.iconHeroUuid === uuid) return;
|
||||
this.iconHeroUuid = uuid;
|
||||
|
||||
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
||||
if (sprite) sprite.spriteFrame = null;
|
||||
|
||||
const hero = HeroInfo[uuid];
|
||||
if (!hero) {
|
||||
mLogger.log(this.debugMode, "CHeroComp", "updateHeroAnimation hero not found", {
|
||||
nodeName: this.node?.name,
|
||||
uuid
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
/** 更新图标精灵 */
|
||||
private updateIcon(node: Node, iconId: string) {
|
||||
if (!node || !iconId) return;
|
||||
const sprite = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
||||
const frame = smc.uiconsAtlas ? smc.uiconsAtlas.getSpriteFrame(iconId) : null;
|
||||
mLogger.log(this.debugMode, "CHeroComp", "updateIcon", {
|
||||
const path = `game/heros/hero/${hero.path}/idle`;
|
||||
mLogger.log(this.debugMode, "CHeroComp", "updateHeroAnimation load", {
|
||||
nodeName: this.node?.name,
|
||||
iconId,
|
||||
spriteFound: !!sprite,
|
||||
atlasReady: !!smc.uiconsAtlas,
|
||||
frameFound: !!frame
|
||||
uuid,
|
||||
path,
|
||||
token
|
||||
});
|
||||
if (!sprite) return;
|
||||
if (smc.uiconsAtlas) {
|
||||
sprite.spriteFrame = frame || null;
|
||||
|
||||
resources.load(path, AnimationClip, (err, clip) => {
|
||||
if (err || !clip) {
|
||||
mLogger.log(this.debugMode, "CHeroComp", "updateHeroAnimation load failed", {
|
||||
nodeName: this.node?.name,
|
||||
uuid,
|
||||
path,
|
||||
message: err?.message ?? null
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 竞态保护:仅允许最新一次图标请求落地
|
||||
if (token !== this.iconVisualToken) return;
|
||||
if (!node || !node.isValid) return;
|
||||
const target = node.getComponent(Sprite) || node.getComponentInChildren(Sprite);
|
||||
if (!target || !target.isValid) return;
|
||||
|
||||
// 帧动画数据存于 spriteFrame 轨道的 _channel._curve._values(裸 SpriteFrame 数组)
|
||||
let firstFrame: SpriteFrame | undefined;
|
||||
for (const track of clip.tracks) {
|
||||
const curve = (track as unknown as { channel?: { curve?: { _values?: unknown[] } } }).channel?.curve;
|
||||
const frame = curve?._values?.[0];
|
||||
if (frame instanceof SpriteFrame) {
|
||||
firstFrame = frame;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstFrame) {
|
||||
target.spriteFrame = firstFrame;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ======================== 长按信息框 ========================
|
||||
|
||||
Reference in New Issue
Block a user