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.active = true;
// 设置节点属性
let face=caster.node.scale.x < 0 ? -1 : 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){
const collider=node.getComponent(BoxCollider2D);
if(collider){
@@ -163,6 +169,7 @@ export class Skill extends ecs.Entity {
sDataCom.s_uuid=s_uuid
sDataCom.fac=cAttrsComp.fac
sDataCom.ext_dmg=ext_dmg
SView.init();
}
/** 模块资源释放 */

View File

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