fix(skill): 修复技能碰撞检测逻辑和组件初始化问题

- 在Skill.ts中确保节点激活并添加SkillView组件缺失的错误处理
- 修复SkillView中碰撞体启用/禁用逻辑,避免同一帧内重复触发伤害
- 增加pendingDisableCollider标志防止异步操作中的竞争条件
- 完善组件重置逻辑,正确清理事件监听和定时器
- 修复动画事件监听可能重复绑定的问题
This commit is contained in:
walkpan
2026-03-15 15:56:22 +08:00
parent 211f48b6aa
commit 02565c372e
2 changed files with 29 additions and 11 deletions

View File

@@ -99,11 +99,17 @@ export class Skill extends ecs.Entity {
} }
node.parent = skillParent; node.parent = skillParent;
node.active = true;
// 设置节点属性 // 设置节点属性
let face=caster.node.scale.x < 0 ? -1 : 1 let face=caster.node.scale.x < 0 ? -1 : 1
node.setScale(v3(node.scale.x*face,node.scale.y,1)) node.setScale(v3(node.scale.x*face,node.scale.y,1))
// 初始视图 // 初始视图
const SView = node.getComponent(SkillView); 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){ if(config.EType!=EType.collision){
const collider=node.getComponent(BoxCollider2D); const collider=node.getComponent(BoxCollider2D);
if(collider){ if(collider){
@@ -163,6 +169,7 @@ export class Skill extends ecs.Entity {
sDataCom.s_uuid=s_uuid sDataCom.s_uuid=s_uuid
sDataCom.fac=cAttrsComp.fac sDataCom.fac=cAttrsComp.fac
sDataCom.ext_dmg=ext_dmg sDataCom.ext_dmg=ext_dmg
SView.init();
} }
/** 模块资源释放 */ /** 模块资源释放 */

View File

@@ -30,27 +30,27 @@ export class SkillView extends CCComp {
sData:SDataCom=null; sData:SDataCom=null;
s_uuid:number=1001 s_uuid:number=1001
private collider: Collider2D = null; // 缓存碰撞体引用 private collider: Collider2D = null; // 缓存碰撞体引用
private pendingDisableCollider: boolean = false;
private attackFrameCount: number = 0; // 攻击帧计数器 private attackFrameCount: number = 0; // 攻击帧计数器
private maxAttackFrames: number = 1; // 最大攻击帧数,可配置 private maxAttackFrames: number = 1; // 最大攻击帧数,可配置
// 已命中目标追踪,防止重复伤害 // 已命中目标追踪,防止重复伤害
start() {
this.init();
}
init() { init() {
this.SConf = SkillSet[this.s_uuid] this.SConf = SkillSet[this.s_uuid]
this.sData = this.ent.get(SDataCom) this.sData = this.ent.get(SDataCom)
this.anim = this.node.getComponent(Animation) this.anim = this.node.getComponent(Animation)
this.node.active = true; this.node.active = true;
this.pendingDisableCollider = false;
this.collider = this.getComponent(Collider2D); this.collider = this.getComponent(Collider2D);
if(this.collider) { if(this.collider) {
this.collider.group = this.group; this.collider.group = this.group;
this.collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
this.collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); this.collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
this.collider.enabled = true; // 确保复用时开启 this.collider.enabled = this.SConf?.EType === EType.collision;
} }
if(this.node.getComponent(Animation)){ if(this.node.getComponent(Animation)){
let anim = this.node.getComponent(Animation); let anim = this.node.getComponent(Animation);
mLogger.log(this.debugMode, 'SkillView', "[SkillCom]:has anim",anim) mLogger.log(this.debugMode, 'SkillView', "[SkillCom]:has anim",anim)
anim.off(Animation.EventType.FINISHED, this.onAnimationFinished, this);
anim.on(Animation.EventType.FINISHED, this.onAnimationFinished, this); anim.on(Animation.EventType.FINISHED, this.onAnimationFinished, this);
// 对象池复用时,需要手动播放默认动画(因为 Play On Load 只在首次生效) // 对象池复用时,需要手动播放默认动画(因为 Play On Load 只在首次生效)
@@ -74,6 +74,7 @@ export class SkillView extends CCComp {
return; return;
} }
if (oCol.group === seCol.group) return; if (oCol.group === seCol.group) return;
if (this.pendingDisableCollider) return;
// 不是 HeroViewComp直接忽略 // 不是 HeroViewComp直接忽略
if (!targetView) return; if (!targetView) return;
// 🔥 方案A防御性检查 - 在获取model前强制检查ent是否存在 // 🔥 方案A防御性检查 - 在获取model前强制检查ent是否存在
@@ -101,6 +102,7 @@ export class SkillView extends CCComp {
// 开启碰撞检测 // 开启碰撞检测
if(this.collider) { if(this.collider) {
this.pendingDisableCollider = false;
this.collider.enabled = true; this.collider.enabled = true;
mLogger.log(this.debugMode, 'SkillView', `[SkillView] [${this.SConf?.name}] 第${this.attackFrameCount}次攻击帧开启碰撞检测`); mLogger.log(this.debugMode, 'SkillView', `[SkillView] [${this.SConf?.name}] 第${this.attackFrameCount}次攻击帧开启碰撞检测`);
} }
@@ -121,7 +123,7 @@ export class SkillView extends CCComp {
// 对于非持续碰撞类型的技能,在造成伤害后立即关闭碰撞检测 // 对于非持续碰撞类型的技能,在造成伤害后立即关闭碰撞检测
// 这样可以避免同一帧内的重复伤害 // 这样可以避免同一帧内的重复伤害
if(this.SConf.EType !== EType.collision && this.collider) { if(this.SConf.EType !== EType.collision && this.collider) {
this.collider.enabled = false; this.close_collider();
mLogger.log(this.debugMode, 'SkillView', `[SkillView] [${this.SConf.name}] 伤害后关闭碰撞检测`); mLogger.log(this.debugMode, 'SkillView', `[SkillView] [${this.SConf.name}] 伤害后关闭碰撞检测`);
} }
@@ -157,17 +159,26 @@ export class SkillView extends CCComp {
} }
} }
close_collider(){ close_collider(){
if (this.collider) { if (!this.collider || this.pendingDisableCollider) return;
this.collider.enabled = false; this.pendingDisableCollider = true;
} this.scheduleOnce(() => {
if (this.collider && this.collider.isValid) {
this.collider.enabled = false;
}
this.pendingDisableCollider = false;
}, 0);
} }
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */ /** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() { reset() {
// 清理碰撞体事件监听 // 清理碰撞体事件监听
if (this.collider) { if (this.collider) {
this.collider.off(Contact2DType.BEGIN_CONTACT); this.collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
this.collider.enabled = false; this.collider.enabled = false;
} }
this.pendingDisableCollider = false;
if (this.anim) {
this.anim.off(Animation.EventType.FINISHED, this.onAnimationFinished, this);
}
// 取消所有定时器 // 取消所有定时器
this.unscheduleAllCallbacks(); this.unscheduleAllCallbacks();
if (this.node && this.node.isValid) { if (this.node && this.node.isValid) {