fix(cast&skill): 修复技能预制体加载问题,重构代码并添加调试日志

为SCastSystem添加多处调试日志,便于排查技能施法相关问题
重构Skill类的load方法,将同步预制体获取改为异步加载逻辑
封装重复的技能节点初始化逻辑为内部函数,提升代码可读性
修复预制体未预加载时无法创建技能实体的问题
This commit is contained in:
pan
2026-06-04 15:25:43 +08:00
parent efe6cc0dd7
commit 0c9818ca27
2 changed files with 131 additions and 112 deletions

View File

@@ -128,9 +128,13 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
targetPos = this.resolveEnemyCastTargetPos(config, mockAttrs, mockView, target, maxRange);
}
// 如果全屏都没找到敌人,直接放弃释放伤害技能
if (!targetPos) return;
if (!targetPos) {
console.log("[SCastSystem] forceCastCardSkill: no enemy found for skill", s_uuid);
return;
}
}
console.log("[SCastSystem] forceCastCardSkill: casting skill", s_uuid, "castTimes", castTimes, "targetPos", targetPos);
for (let i = 0; i < castTimes; i++) {
if (isFriendly) {
const friendlyTargets = this.resolveFriendlyTargets(targetEids, FacSet.HERO);
@@ -147,7 +151,10 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
private createSkillEntityForCard(s_uuid: number, skillLv: number, mockAttrs: HeroAttrsComp, startPos: Vec3, targetPos: Vec3 | null, castIndex: number = 0, overrides?: SkillOverrides) {
const scene = smc.map.MapView.scene;
const parent = scene.entityLayer?.node?.getChildByName("SKILL");
if (!parent || !targetPos) return;
if (!parent || !targetPos) {
console.log("[SCastSystem] createSkillEntityForCard failed: parent or targetPos missing", !!parent, !!targetPos);
return;
}
const skill = ecs.getEntity<Skill>(Skill);
const actualStartPos = this.resolveRepeatCastStartPos(startPos, castIndex);
@@ -160,6 +167,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
} as any;
skill.load(actualStartPos, parent, s_uuid, targetPos.clone(), mockView, mockAttrs, skillLv, 0, overrides);
console.log("[SCastSystem] createSkillEntityForCard success for skill", s_uuid);
}
/** 空施法计划:用于“当前无可施法技能”时的统一返回 */
private readonly emptyCastPlan = { skillId: 0, skillLv: 1, isFriendly: false, targetPos: null as Vec3 | null, targetEids: [] as number[], overrides: undefined as SkillOverrides | undefined };

View File

@@ -109,124 +109,135 @@ export class Skill extends ecs.Entity {
}
config = mergeSkillParams(config, overrides);
// 加载预制体
const path = `game/skill/atk/${config.sp_name}`;
const prefab:Prefab = oops.res.get(path, Prefab);
if (!prefab) {
mLogger.error(this.debugMode, 'Skill', "[Skill] 预制体加载失败:", path);
return;
}
const node: Node = Skill.getFromPool(path) || instantiate(prefab);
if (!node || !node.isValid) {
mLogger.error(this.debugMode, 'Skill', "[Skill] 节点无效:", path);
return;
}
this.prefabPath = path;
this.skillNode = node;
let skillParent: Node | null = null;
if (smc.map && smc.map.MapView && smc.map.MapView.scene && smc.map.MapView.scene.entityLayer && smc.map.MapView.scene.entityLayer.node) {
skillParent = smc.map.MapView.scene.entityLayer.node.getChildByName("SKILL");
}
if (!skillParent || !skillParent.isValid) {
skillParent = parent;
}
if (!skillParent || !skillParent.isValid) {
mLogger.error(this.debugMode, 'Skill', "[Skill] 父节点无效");
if(node.isValid) node.destroy();
return;
}
node.parent = skillParent;
node.active = true;
// 设置节点属性
let face=caster.node.scale.x < 0 ? -1 : 1
node.setScale(v3(Math.abs(node.scale.x)*face,node.scale.y,1))
// 初始视图
const SView = node.getComponent(SkillView);
if (!SView) {
mLogger.error(this.debugMode, 'Skill', "[Skill] SkillView 组件缺失:", path);
if (node.isValid) node.destroy();
return;
}
if(config.EType!=EType.collision){
const collider=node.getComponent(BoxCollider2D);
if(collider){
collider.enabled=false
const initSkillNode = (prefab: Prefab) => {
const node: Node = Skill.getFromPool(path) || instantiate(prefab);
if (!node || !node.isValid) {
mLogger.error(this.debugMode, 'Skill', "[Skill] 节点无效:", path);
return;
}
}
// 只设置必要的运行时属性,配置信息通过 SkillSet[uuid] 访问
// 核心标识
SView.s_uuid= s_uuid
SView.group= caster.box_group
this.add(SView);
startPos.x=startPos.x+SView.atk_x*face
startPos.y=startPos.y+SView.atk_y
this.prefabPath = path;
this.skillNode = node;
let skillParent: Node | null = null;
if (smc.map && smc.map.MapView && smc.map.MapView.scene && smc.map.MapView.scene.entityLayer && smc.map.MapView.scene.entityLayer.node) {
skillParent = smc.map.MapView.scene.entityLayer.node.getChildByName("SKILL");
}
if (!skillParent || !skillParent.isValid) {
skillParent = parent;
}
if (!skillParent || !skillParent.isValid) {
mLogger.error(this.debugMode, 'Skill', "[Skill] 父节点无效");
if(node.isValid) node.destroy();
return;
}
node.setPosition(startPos);
node.parent = skillParent;
node.active = true;
// 设置节点属性
let face=caster.node.scale.x < 0 ? -1 : 1
node.setScale(v3(Math.abs(node.scale.x)*face,node.scale.y,1))
// 初始视图
const SView = node.getComponent(SkillView);
if (!SView) {
mLogger.error(this.debugMode, 'Skill', "[Skill] SkillView 组件缺失:", path);
if (node.isValid) node.destroy();
return;
}
if(config.EType!=EType.collision){
const collider=node.getComponent(BoxCollider2D);
if(collider){
collider.enabled=false
}
}
// 只设置必要的运行时属性,配置信息通过 SkillSet[uuid] 访问
// 核心标识
SView.s_uuid= s_uuid
SView.group= caster.box_group
// 初始化移动组件 - 从SkillView获取移动参数
let sMoveCom = this.get(SMoveDataComp);
if (!sMoveCom) {
sMoveCom = this.add(SMoveDataComp);
}
sMoveCom.reset(); // 复用组件时重置状态
sMoveCom.startPos.set(startPos);
sMoveCom.targetPos.set(targetPos);
sMoveCom.s_uuid = s_uuid;
sMoveCom.scale = caster.node.scale.x < 0 ? -1 : 1;
sMoveCom.runType = config.RType;
sMoveCom.endType = config.EType;
sMoveCom.bezierStartHeight = config.bezier_start_y ?? sMoveCom.bezierStartHeight;
sMoveCom.bezierMidHeight = config.bezier_mid_y ?? sMoveCom.bezierMidHeight;
sMoveCom.bezierArc = config.bezier_arc ?? sMoveCom.bezierArc;
// 从SkillView获取移动参数位置初始化由SMoveSystem统一处理
sMoveCom.atk_x = SView.atk_x;
sMoveCom.atk_y = SView.atk_y;
this.add(SView);
startPos.x=startPos.x+SView.atk_x*face
startPos.y=startPos.y+SView.atk_y
if (config.EType === EType.timeEnd) {
let sTimeCom = this.get(StimeDataComp);
if (!sTimeCom) sTimeCom = this.add(StimeDataComp);
sTimeCom.reset();
sTimeCom.s_uuid = s_uuid;
sTimeCom.totalTime = Math.max(1, config.time ?? 0);
sTimeCom.hitInterval = Math.max(0.5, config.hitcd || 0);
node.setPosition(startPos);
// 初始化移动组件 - 从SkillView获取移动参数
let sMoveCom = this.get(SMoveDataComp);
if (!sMoveCom) {
sMoveCom = this.add(SMoveDataComp);
}
sMoveCom.reset(); // 复用组件时重置状态
sMoveCom.startPos.set(startPos);
sMoveCom.targetPos.set(targetPos);
sMoveCom.s_uuid = s_uuid;
sMoveCom.scale = caster.node.scale.x < 0 ? -1 : 1;
sMoveCom.runType = config.RType;
sMoveCom.endType = config.EType;
sMoveCom.bezierStartHeight = config.bezier_start_y ?? sMoveCom.bezierStartHeight;
sMoveCom.bezierMidHeight = config.bezier_mid_y ?? sMoveCom.bezierMidHeight;
sMoveCom.bezierArc = config.bezier_arc ?? sMoveCom.bezierArc;
// 从SkillView获取移动参数位置初始化由SMoveSystem统一处理
sMoveCom.atk_x = SView.atk_x;
sMoveCom.atk_y = SView.atk_y;
if (config.EType === EType.timeEnd) {
let sTimeCom = this.get(StimeDataComp);
if (!sTimeCom) sTimeCom = this.add(StimeDataComp);
sTimeCom.reset();
sTimeCom.s_uuid = s_uuid;
sTimeCom.totalTime = Math.max(1, config.time ?? 0);
sTimeCom.hitInterval = Math.max(0.5, config.hitcd || 0);
} else {
const sTimeCom = this.get(StimeDataComp);
if (sTimeCom) this.remove(StimeDataComp);
}
// 初始化数据组件
let sDataCom = this.get(SDataCom);
if (!sDataCom) {
sDataCom = this.add(SDataCom);
}
sDataCom.reset();
sDataCom.group=caster.box_group
sDataCom.casterEid=caster.ent.eid
sDataCom.Attrs = {};
const SUp=SkillUpList[s_uuid] ? SkillUpList[s_uuid]:SkillUpList[1001];
const sCrt = (config.crt ?? 0)+(SUp.crt*skill_lv);
const sFrz = (config.frz ?? 0)+(SUp.frz*skill_lv);
const sAp =config.ap+(SUp.ap*skill_lv);
const sHit=config.hit_count+(SUp.hit_count*skill_lv);
sDataCom.Attrs[Attrs.ap] = Math.floor(cAttrsComp.ap*sAp/100); //技能的ap是百分值 需要/100 而且需要再最终计算总ap时再/100不然会出现ap为90%变0
sDataCom.Attrs[Attrs.critical] = cAttrsComp.getRuntimeCritical() + sCrt;
sDataCom.Attrs[Attrs.critical_damage] = cAttrsComp.getRuntimeCritDamageBonus();
sDataCom.Attrs[Attrs.freeze_chance] = cAttrsComp.getRuntimeFreezeChance() + sFrz;
sDataCom.Attrs[Attrs.knockback_chance] = cAttrsComp.knockback_chance || 0;
sDataCom.Attrs[Attrs.knockback_distance] = cAttrsComp.knockback_distance || 0;
sDataCom.Attrs[Attrs.puncture_chance] = cAttrsComp.getRuntimePunctureChance(); // 初始化携带施法者的穿透概率
sDataCom.s_uuid=s_uuid
sDataCom.skill_lv = Math.max(0, skill_lv);
sDataCom.fac=cAttrsComp.fac
sDataCom.ext_dmg=ext_dmg
sDataCom.hit_count = 0
sDataCom.max_hit_count = Math.max(1,sHit)
SView.init();
};
let prefab: Prefab = oops.res.get(path, Prefab);
if (prefab) {
initSkillNode(prefab);
} else {
const sTimeCom = this.get(StimeDataComp);
if (sTimeCom) this.remove(StimeDataComp);
oops.res.loadAsync(path, Prefab).then((res: Prefab) => {
if (res) {
initSkillNode(res);
} else {
mLogger.error(this.debugMode, 'Skill', "[Skill] 预制体加载失败:", path);
}
}).catch((err) => {
mLogger.error(this.debugMode, 'Skill', "[Skill] 预制体加载异常:", path, err);
});
}
// 初始化数据组件
let sDataCom = this.get(SDataCom);
if (!sDataCom) {
sDataCom = this.add(SDataCom);
}
sDataCom.reset();
sDataCom.group=caster.box_group
sDataCom.casterEid=caster.ent.eid
sDataCom.Attrs = {};
const SUp=SkillUpList[s_uuid] ? SkillUpList[s_uuid]:SkillUpList[1001];
const sCrt = (config.crt ?? 0)+(SUp.crt*skill_lv);
const sFrz = (config.frz ?? 0)+(SUp.frz*skill_lv);
const sAp =config.ap+(SUp.ap*skill_lv);
const sHit=config.hit_count+(SUp.hit_count*skill_lv);
sDataCom.Attrs[Attrs.ap] = Math.floor(cAttrsComp.ap*sAp/100); //技能的ap是百分值 需要/100 而且需要再最终计算总ap时再/100不然会出现ap为90%变0
sDataCom.Attrs[Attrs.critical] = cAttrsComp.getRuntimeCritical() + sCrt;
sDataCom.Attrs[Attrs.critical_damage] = cAttrsComp.getRuntimeCritDamageBonus();
sDataCom.Attrs[Attrs.freeze_chance] = cAttrsComp.getRuntimeFreezeChance() + sFrz;
sDataCom.Attrs[Attrs.knockback_chance] = cAttrsComp.knockback_chance || 0;
sDataCom.Attrs[Attrs.knockback_distance] = cAttrsComp.knockback_distance || 0;
sDataCom.Attrs[Attrs.puncture_chance] = cAttrsComp.getRuntimePunctureChance(); // 初始化携带施法者的穿透概率
sDataCom.s_uuid=s_uuid
sDataCom.skill_lv = Math.max(0, skill_lv);
sDataCom.fac=cAttrsComp.fac
sDataCom.ext_dmg=ext_dmg
sDataCom.hit_count = 0
sDataCom.max_hit_count = Math.max(1,sHit)
SView.init();
}
/** 模块资源释放 */