From 0c9818ca2762e95d587946da7bb59b1bcc47218e Mon Sep 17 00:00:00 2001 From: pan Date: Thu, 4 Jun 2026 15:25:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(cast&skill):=20=E4=BF=AE=E5=A4=8D=E6=8A=80?= =?UTF-8?q?=E8=83=BD=E9=A2=84=E5=88=B6=E4=BD=93=E5=8A=A0=E8=BD=BD=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E9=87=8D=E6=9E=84=E4=BB=A3=E7=A0=81=E5=B9=B6?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B0=83=E8=AF=95=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为SCastSystem添加多处调试日志,便于排查技能施法相关问题 重构Skill类的load方法,将同步预制体获取改为异步加载逻辑 封装重复的技能节点初始化逻辑为内部函数,提升代码可读性 修复预制体未预加载时无法创建技能实体的问题 --- assets/script/game/hero/SCastSystem.ts | 12 +- assets/script/game/skill/Skill.ts | 231 +++++++++++++------------ 2 files changed, 131 insertions(+), 112 deletions(-) diff --git a/assets/script/game/hero/SCastSystem.ts b/assets/script/game/hero/SCastSystem.ts index af587745..43e76121 100644 --- a/assets/script/game/hero/SCastSystem.ts +++ b/assets/script/game/hero/SCastSystem.ts @@ -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); 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 }; diff --git a/assets/script/game/skill/Skill.ts b/assets/script/game/skill/Skill.ts index 84e87950..2cacd91c 100644 --- a/assets/script/game/skill/Skill.ts +++ b/assets/script/game/skill/Skill.ts @@ -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(); } /** 模块资源释放 */