refactor(hero): 移除SkillConComp并添加ECS系统注册装饰器

- 删除废弃的SkillConComp组件及其meta文件
- 为HeroAtkSystem、HeroAttrSystem等系统添加@ecs.register装饰器
- 在生命周期系统中添加空安全检查
- 移除SkillConComp相关引用及调试日志
- 在移动系统中添加节点有效性检查
This commit is contained in:
2025-10-30 16:31:44 +08:00
parent 7984f8b784
commit 40e0086be3
10 changed files with 64 additions and 210 deletions

View File

@@ -52,7 +52,6 @@ export class SkillCastSystem extends ecs.ComblockSystem implements ecs.IEntityEn
* 过滤器:拥有技能数据 + 施法请求的实体 * 过滤器:拥有技能数据 + 施法请求的实体
*/ */
filter(): ecs.IMatcher { filter(): ecs.IMatcher {
console.log("[SkillCastSystem] filter");
return ecs.allOf(HeroSkillsComp, HeroAttrsComp, CastSkillRequestComp); return ecs.allOf(HeroSkillsComp, HeroAttrsComp, CastSkillRequestComp);
} }
@@ -187,7 +186,6 @@ export class SkillCastSystem extends ecs.ComblockSystem implements ecs.IEntityEn
export class SkillCDSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { export class SkillCDSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher { filter(): ecs.IMatcher {
console.log("[SkillCDSystem] filter");
return ecs.allOf(HeroSkillsComp); return ecs.allOf(HeroSkillsComp);
} }
@@ -218,7 +216,6 @@ export class SkillCDSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
export class SkillAutocastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { export class SkillAutocastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher { filter(): ecs.IMatcher {
console.log("[SkillAutocastSystem] filter");
return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp); return ecs.allOf(HeroSkillsComp, HeroAttrsComp, HeroViewComp);
} }
@@ -226,7 +223,6 @@ export class SkillAutocastSystem extends ecs.ComblockSystem implements ecs.ISyst
const skillsData = e.get(HeroSkillsComp); const skillsData = e.get(HeroSkillsComp);
const heroModel = e.get(HeroAttrsComp); const heroModel = e.get(HeroAttrsComp);
const heroView = e.get(HeroViewComp); const heroView = e.get(HeroViewComp);
console.log("[SkillAutocastSystem] update");
if (!skillsData || !heroModel || !heroView) return; if (!skillsData || !heroModel || !heroView) return;
// 检查基本条件 // 检查基本条件

View File

@@ -114,6 +114,7 @@ export class Hero extends ecs.Entity {
} }
@ecs.register('HeroLifecycleSystem')
export class HeroLifecycleSystem extends ecs.ComblockSystem export class HeroLifecycleSystem extends ecs.ComblockSystem
implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem { implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem {
@@ -123,11 +124,21 @@ export class HeroLifecycleSystem extends ecs.ComblockSystem
entityEnter(e: ecs.Entity): void { entityEnter(e: ecs.Entity): void {
// 英雄实体创建时的特殊处理 // 英雄实体创建时的特殊处理
console.log(`英雄进入世界: ${e.get(HeroAttrsComp).hero_name}`); const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
console.log(`英雄进入世界: ${heroAttrs.hero_name}`);
} else {
console.log(`英雄进入世界: 实体ID ${e.eid}`);
}
} }
entityRemove(e: ecs.Entity): void { entityRemove(e: ecs.Entity): void {
// 英雄实体销毁时的清理工作 // 英雄实体销毁时的清理工作
console.log(`英雄离开世界: ${e.get(HeroAttrsComp).hero_name}`); const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
console.log(`英雄离开世界: ${heroAttrs.hero_name}`);
} else {
console.log(`英雄离开世界: 实体ID ${e.eid}`);
}
} }
} }

View File

@@ -16,6 +16,7 @@ export class HeroAtkComp extends ecs.Comp {
} }
/** 业务层业务逻辑处理对象 */ /** 业务层业务逻辑处理对象 */
@ecs.register('HeroAtkSystem')
export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate, ecs.IEntityEnterSystem { export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate, ecs.IEntityEnterSystem {
private debugMode: boolean = false; // 是否启用调试模式 private debugMode: boolean = false; // 是否启用调试模式

View File

@@ -392,9 +392,11 @@ export class HeroAttrsComp extends ecs.Comp {
* 2. 每帧更新 HP/MP 自然回复 * 2. 每帧更新 HP/MP 自然回复
* 3. 限制属性值在合理范围内 * 3. 限制属性值在合理范围内
* *
/**
* 使用方式: * 使用方式:
* 在 RootSystem 中注册此系统,它会自动每帧更新所有拥有 HeroAttrsComp 的实体 * 在 RootSystem 中注册此系统,它会自动每帧更新所有拥有 HeroAttrsComp 的实体
*/ */
@ecs.register('HeroAttrSystem')
export class HeroAttrSystem extends ecs.ComblockSystem export class HeroAttrSystem extends ecs.ComblockSystem
implements ecs.ISystemUpdate, ecs.IEntityEnterSystem, ecs.ISystemFirstUpdate { implements ecs.ISystemUpdate, ecs.IEntityEnterSystem, ecs.ISystemFirstUpdate {

View File

@@ -159,7 +159,10 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** 找到最近的敌人 */ /** 找到最近的敌人 */
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null { private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
const currentPos = entity.get(HeroViewComp).node.position; const currentView = entity.get(HeroViewComp);
if (!currentView || !currentView.node) return null;
const currentPos = currentView.node.position;
const team = entity.get(HeroAttrsComp).fac; const team = entity.get(HeroAttrsComp).fac;
let nearestEnemyView: HeroViewComp | null = null; let nearestEnemyView: HeroViewComp | null = null;
let minDistance = Infinity; let minDistance = Infinity;
@@ -167,7 +170,7 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).forEach(e => { ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).forEach(e => {
const model = e.get(HeroAttrsComp); const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp); const view = e.get(HeroViewComp);
if (model.fac !== team && !model.is_dead) { if (model.fac !== team && !model.is_dead && view && view.node) {
const distance = Math.abs(currentPos.x - view.node.position.x); const distance = Math.abs(currentPos.x - view.node.position.x);
if (distance < minDistance) { if (distance < minDistance) {
minDistance = distance; minDistance = distance;
@@ -181,12 +184,16 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** 检测攻击范围内敌人 */ /** 检测攻击范围内敌人 */
private checkEnemiesInRange(entity: ecs.Entity, range: number): boolean { private checkEnemiesInRange(entity: ecs.Entity, range: number): boolean {
const currentPos = entity.get(HeroViewComp).node.position; const currentView = entity.get(HeroViewComp);
if (!currentView || !currentView.node) return false;
const currentPos = currentView.node.position;
const team = entity.get(HeroAttrsComp).fac; const team = entity.get(HeroAttrsComp).fac;
let found = false; let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => { ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp); const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp); const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x); const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) { if (model.fac !== team && !model.is_dead) {
if (distance <= range) { if (distance <= range) {
@@ -200,12 +207,16 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** 检测面前是否有敌人 */ /** 检测面前是否有敌人 */
private checkEnemiesInFace(entity: ecs.Entity): boolean { private checkEnemiesInFace(entity: ecs.Entity): boolean {
const currentPos = entity.get(HeroViewComp).node.position; const currentView = entity.get(HeroViewComp);
if (!currentView || !currentView.node) return false;
const currentPos = currentView.node.position;
const team = entity.get(HeroAttrsComp).fac; const team = entity.get(HeroAttrsComp).fac;
let found = false; let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => { ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp); const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp); const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x); const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) { if (model.fac !== team && !model.is_dead) {
if (distance <= 75) { if (distance <= 75) {
@@ -232,8 +243,11 @@ export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
// 按x坐标排序x坐标越大越前面的显示在上层 // 按x坐标排序x坐标越大越前面的显示在上层
const sortedUnits = allUnits.sort((a, b) => { const sortedUnits = allUnits.sort((a, b) => {
const posA = a.get(HeroViewComp).node.position.x; const viewA = a.get(HeroViewComp);
const posB = b.get(HeroViewComp).node.position.x; const viewB = b.get(HeroViewComp);
if (!viewA || !viewA.node || !viewB || !viewB.node) return 0;
const posA = viewA.node.position.x;
const posB = viewB.node.position.x;
return posA - posB; // x坐标从小到大排序 return posA - posB; // x坐标从小到大排序
}); });

View File

@@ -101,6 +101,7 @@ export class Monster extends ecs.Entity {
} }
} }
@ecs.register('MonLifecycleSystem')
export class MonLifecycleSystem extends ecs.ComblockSystem export class MonLifecycleSystem extends ecs.ComblockSystem
implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem { implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem {
@@ -110,11 +111,21 @@ export class MonLifecycleSystem extends ecs.ComblockSystem
entityEnter(e: ecs.Entity): void { entityEnter(e: ecs.Entity): void {
// 怪物实体创建时的特殊处理 // 怪物实体创建时的特殊处理
console.log(`怪物进入世界: ${e.get(HeroAttrsComp).hero_name}`); const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
console.log(`怪物进入世界: ${heroAttrs.hero_name}`);
} else {
console.log(`怪物进入世界: 实体ID ${e.eid}`);
}
} }
entityRemove(e: ecs.Entity): void { entityRemove(e: ecs.Entity): void {
// 怪物实体销毁时的清理工作 // 怪物实体销毁时的清理工作
console.log(`怪物离开世界: ${e.get(HeroAttrsComp).hero_name}`); const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
console.log(`怪物离开世界: ${heroAttrs.hero_name}`);
} else {
console.log(`怪物离开世界: 实体ID ${e.eid}`);
}
} }
} }

View File

@@ -88,12 +88,16 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
/** 检测攻击范围内敌人 */ /** 检测攻击范围内敌人 */
private checkEnemiesInRange(entity: ecs.Entity, range: number): boolean { private checkEnemiesInRange(entity: ecs.Entity, range: number): boolean {
const currentPos = entity.get(HeroViewComp).node.position; const currentView = entity.get(HeroViewComp);
if (!currentView || !currentView.node) return false;
const currentPos = currentView.node.position;
const team = entity.get(HeroAttrsComp).fac; const team = entity.get(HeroAttrsComp).fac;
let found = false; let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => { ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp); const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp); const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x); const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) { if (model.fac !== team && !model.is_dead) {
if (distance <= range) { if (distance <= range) {
@@ -107,12 +111,16 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
/** 检测面前是否有敌人 */ /** 检测面前是否有敌人 */
private checkEnemiesInFace(entity: ecs.Entity): boolean { private checkEnemiesInFace(entity: ecs.Entity): boolean {
const currentPos = entity.get(HeroViewComp).node.position; const currentView = entity.get(HeroViewComp);
if (!currentView || !currentView.node) return false;
const currentPos = currentView.node.position;
const team = entity.get(HeroAttrsComp).fac; const team = entity.get(HeroAttrsComp).fac;
let found = false; let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => { ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp); const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp); const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x); const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) { if (model.fac !== team && !model.is_dead) {
if (distance <= 75) { if (distance <= 75) {
@@ -139,8 +147,11 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
// 按x坐标排序x坐标越大越前面的显示在上层 // 按x坐标排序x坐标越大越前面的显示在上层
const sortedUnits = allUnits.sort((a, b) => { const sortedUnits = allUnits.sort((a, b) => {
const posA = a.get(HeroViewComp).node.position.x; const viewA = a.get(HeroViewComp);
const posB = b.get(HeroViewComp).node.position.x; const viewB = b.get(HeroViewComp);
if (!viewA || !viewA.node || !viewB || !viewB.node) return 0;
const posA = viewA.node.position.x;
const posB = viewB.node.position.x;
return posA - posB; // x坐标从小到大排序 return posA - posB; // x坐标从小到大排序
}); });

View File

@@ -1,181 +0,0 @@
import { _decorator, Component, Node, ProgressBar, v3, Vec3 } from 'cc';
import { HeroViewComp } from './HeroViewComp';
import { SkillSet, SType, TGroup, } from '../common/config/SkillSet';
import { ecs } from 'db://oops-framework/libs/ecs/ECS';
import { GameEvent } from '../common/config/GameEvent';
import { FacSet } from '../common/config/BoxSet';
import { smc } from '../common/SingletonModuleComp';
import { CCComp } from 'db://oops-framework/module/common/CCComp';
import { HeroAttrsComp } from './HeroAttrsComp';
import { HeroSkillsComp } from './HeroSkills';
import { CastSkillRequestComp } from './HSkillSystem';
import { SkillEnt } from '../skill/SkillEnt';
import { Attrs } from '../common/config/HeroAttrs';
import { TalComp } from './TalComp';
const { ccclass, property } = _decorator;
@ccclass('SkillCon')
@ecs.register('SkillCon')
export class SkillConComp extends CCComp {
HeroView:any=null;
HeroEntity:any=null;
skill_cd=0
private _timers: { [key: string]: any } = {};
init(): void {
this.on(GameEvent.FightEnd, this.clear_timer, this);
}
onLoad(){
this.HeroView=this.node.getComponent(HeroViewComp)
}
start() {
this.HeroEntity=this.HeroView.ent
}
/**
* ⚠️ 注意:此方法已废弃
* 技能CD更新和施法逻辑已迁移到 HSkillSystemSkillCDSystem + SkillAutocastSystem
* 保留此方法仅用于手动触发技能(如玩家点击技能按钮)
*/
update(dt: number) {
// 已由 SkillCDSystem 和 SkillAutocastSystem 处理
// 此方法可以删除或改为手动施法的入口
}
/**
* 手动施放技能(玩家点击技能按钮)
* @param skillIndex 技能索引
*/
manualCastSkill(skillIndex: number) {
if (!this.HeroEntity) return;
// 选择目标
const targets = this.selectTargets(1);
// ✅ 通过添加标记组件请求施法
const request = this.HeroEntity.add(CastSkillRequestComp) as CastSkillRequestComp;
request.skillIndex = skillIndex;
request.targetPositions = targets;
}
/** 施放技能 */
castSkill(config: typeof SkillSet[keyof typeof SkillSet]) {
let wfuny=this.check_wfuny()
let dmg=0
this.doSkill(config,wfuny,dmg);
}
private doSkill(config: typeof SkillSet[keyof typeof SkillSet],is_wfuny:boolean=false,dmg:number=0) {
// 添加节点有效性检查
if (!this.node || !this.node.isValid || !this.HeroView || !this.HeroView.node || !this.HeroView.node.isValid) {
return;
}
let targets:any=null
if(config.TGroup==TGroup.Self){
targets = [this.node.position]
}
if(config.TGroup==TGroup.Enemy){
targets = this.selectTargets(config.t_num)
}
this.HeroView.playSkillEffect(config.uuid)
const sEnt = ecs.getEntity<SkillEnt>(SkillEnt);
const timerId = setTimeout(() => {
// 再次检查节点有效性
if (!this.node || !this.node.isValid || !this.HeroView || !this.HeroView.node || !this.HeroView.node.isValid) {
return;
}
console.log("技能开始",sEnt)
sEnt.load(
this.node.position,
this.node.parent,
config.uuid,
targets,
this.HeroView,
dmg
);
}, 300);
if(is_wfuny){
this.scheduleOnce(()=>{
this.doSkill(config,false,dmg)
},0.1)
}
// 保存定时器ID
this._timers[`skill_${config.uuid}`] = timerId;
}
check_wfuny(){
let random = Math.random()*100
if(random < this.HeroView.Attrs[Attrs.WFUNY]){
return true
}
return false
}
check_target(){
if(this.HeroView.fac==FacSet.HERO){
return ecs.query(ecs.allOf(HeroAttrsComp))
}else{
return ecs.query(ecs.allOf(HeroAttrsComp))
}
}
get_front(entities:any){
let keyPos = this.HeroView.fac==FacSet.HERO ?
Math.min(...entities.map(e => e.get(HeroViewComp).node.position.x)) :
Math.max(...entities.map(e => e.get(HeroViewComp).node.position.x));
let keyEntity = entities.find(e => e.get(HeroViewComp).node.position.x === keyPos);
return keyEntity.get(HeroViewComp).node.position;
}
/**
* 选择目标(整合版)
* @param t_num 目标数量,第一个是最近的前排,后续随机(可重复)
* @returns 目标坐标数组
*/
private selectTargets(t_num: number): Vec3[] {
const targets: Vec3[] = [];
const entities = this.check_target();
// 如果没有目标实体
if (entities.length === 0) {
const defaultPos = this.HeroView.fac === FacSet.HERO ? v3(400, 0, 0) : v3(-400, 0, 0);
// 返回t_num个相同的默认位置
for (let i = 0; i < t_num; i++) {
targets.push(defaultPos.clone());
}
return targets;
}
// 第一个目标:最前排(离施法者最近的)
const frontPos = this.get_front(entities);
targets.push(v3(frontPos.x, frontPos.y, 0));
// 后续目标:随机选择(可以重复)
for (let i = 1; i < t_num; i++) {
const randomEntity = entities[Math.floor(Math.random() * entities.length)];
const randomPos = randomEntity.get(HeroViewComp).node.position;
targets.push(v3(randomPos.x, randomPos.y, 0));
}
return targets;
}
public clear_timer() {
// console.log("[SkillConComp]:clear_timer",this.HeroView);
Object.values(this._timers).forEach(clearTimeout);
}
reset() {
this.clear_timer();
}
onDestroy() {
// 清理所有定时器
// console.log("[SkillConComp]:onDestroy:",this.node.name)
Object.values(this._timers).forEach(clearTimeout);
this._timers = {};
// 移除事件监听
this.off(GameEvent.CastHeroSkill);
}
}

View File

@@ -1,9 +0,0 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "6f882a1f-6f5a-4ef5-9ea0-21a0192c2785",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -5,7 +5,6 @@ import { ItalConf, TalType, TalEType, talConf } from "../common/config/TalSet";
import { BuffConf, SkillSet } from "../common/config/SkillSet"; import { BuffConf, SkillSet } from "../common/config/SkillSet";
import { HeroInfo } from "../common/config/heroSet"; import { HeroInfo } from "../common/config/heroSet";
import { HeroViewComp } from "./HeroViewComp"; import { HeroViewComp } from "./HeroViewComp";
import { SkillConComp } from "./SkillConComp";
const { ccclass } = _decorator; const { ccclass } = _decorator;
@@ -60,7 +59,6 @@ export class TalComp extends ecs.Comp {
start() { start() {
// 运行时获取组件,避免编译时循环引用 // 运行时获取组件,避免编译时循环引用
this.heroView = this.ent.get(HeroViewComp); this.heroView = this.ent.get(HeroViewComp);
this.skillCon = this.ent.get(SkillConComp);
if (this.heroView) { if (this.heroView) {
this.heroUuid = this.heroView.hero_uuid; this.heroUuid = this.heroView.hero_uuid;
this.initializeTalents(); this.initializeTalents();