Files
pixelheros/assets/script/game/hero/Hero.ts
walkpan ee9d72be5c fix(hero): 修复英雄属性组件事件监听未正确初始化和清理的问题
将 HeroAttrsComp 的事件监听从 onLoad/onDestroy 生命周期方法移至显式调用的 initEvent/removeEvent 方法,确保在 Hero 初始化时正确初始化事件监听,并在重置时清理事件监听,避免事件重复绑定和内存泄漏。
2026-02-05 08:01:34 +08:00

171 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { instantiate, Node, Prefab, Vec3 ,v3,resources,SpriteFrame,Sprite,SpriteAtlas, BoxCollider2D} from "cc";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { smc } from "../common/SingletonModuleComp";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { BoxSet, FacSet, IndexSet } from "../common/config/GameSet";
import { HeroInfo, HeroPos, HType } from "../common/config/heroSet";
import { GameEvent } from "../common/config/GameEvent";
import { getNeAttrs, getAttrs ,Attrs} from "../common/config/HeroAttrs";
import { HeroSkillsComp } from "./HeroSkills";
import { HeroMoveComp } from "./HeroMove";
import { TalComp } from "./TalComp";
import { mLogger } from "../common/Logger";
import { HeroMasterComp } from "./HeroMasterComp";
/** 角色实体 */
@ecs.register(`Hero`)
export class Hero extends ecs.Entity {
HeroModel!: HeroAttrsComp;
HeroSkills!: HeroSkillsComp;
View!: HeroViewComp;
HeroMove!: HeroMoveComp;
TalComp!: TalComp;
debugMode: boolean = false; // 是否启用调试模式
protected init() {
this.addComponents<ecs.Comp>(
HeroMoveComp,
HeroAttrsComp,
HeroSkillsComp,
);
}
destroy(): void {
// 销毁节点,防止视觉残留
const view = this.get(HeroViewComp);
if (view && view.node && view.node.isValid) {
view.node.destroy();
}
this.remove(HeroViewComp);
this.remove(HeroAttrsComp);
this.remove(HeroSkillsComp);
this.remove(TalComp);
this.remove(HeroMasterComp)
super.destroy();
}
/** 加载角色 */
load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001,fight_pos:number=1,is_master:boolean=false,is_friend:boolean=false) {
scale = 1
// 查找空闲英雄槽位
let size=1
var path = "game/heros/"+HeroInfo[uuid].path;
var prefab: Prefab = oops.res.get(path, Prefab)!;
var node = instantiate(prefab);
var scene = smc.map.MapView.scene;
node.parent = scene.entityLayer!.node!.getChildByName("HERO")!;
const collider = node.getComponent(BoxCollider2D);
if (collider) collider.enabled = false; // 先禁用
node.setScale(size*node.scale.x,size*node.scale.y);
node.setPosition(pos)
// 🔥 设置初始 SiblingIndex - 英雄基础层级 + 位置偏移
mLogger.log(this.debugMode,"hero",node.getSiblingIndex());
var hv = node.getComponent(HeroViewComp)!;
const model = this.get(HeroAttrsComp);
const skillsComp = this.get(HeroSkillsComp);
let hero = HeroInfo[uuid]; // 共用英雄数据
// 设置 View 层属性(表现相关)
hv.scale = 1;
hv.box_group = BoxSet.HERO;
// 设置 Model 层属性(数据相关)
model.hero_uuid = uuid;
model.hero_name = hero.name;
model.lv = hero.lv ? hero.lv : 1;
model.type = hero.type;
model.fac = FacSet.HERO;
model.is_master = is_master;
model.is_friend = is_friend
model.rangeType = hero.rangeType;
// 只有主角才挂载天赋组件
if (is_master) {
mLogger.log(this.debugMode,`[Hero] 主角创建, uuid=${uuid},is_master=${is_master}`);
this.add(TalComp);
this.add(HeroMasterComp)
const talComp = this.get(TalComp);
if (talComp) {
talComp.init(uuid);
}
model.initEvent();
}
// ✅ 初始化技能数据(迁移到 HeroSkillsComp
skillsComp.initSkills(hero.skills, uuid);
// 设置基础属性
model.base_ap = hero.ap;
model.base_def = hero.def;
model.base_hp = hero.hp;
model.base_mp = hero.mp;
model.base_speed = hero.speed;
// 初始化属性数组
model.Attrs = getAttrs(); // 属性
model.NeAttrs = getNeAttrs(); //负面属性
model.hp = model.Attrs[Attrs.HP_MAX] = model.base_hp;
model.mp = model.Attrs[Attrs.MP_MAX] = model.base_mp;
model.Attrs[Attrs.DEF] = model.base_def;
model.Attrs[Attrs.AP] = model.base_ap;
model.Attrs[Attrs.SPEED] = hero.speed;
// 初始化 buff/debuff 系统
model.initAttrs();
model.Attrs[Attrs.REVIVE_COUNT]=1 // 复活次数
model.Attrs[Attrs.BACK_CHANCE]=50 // 击退对手几率
model.Attrs[Attrs.CON_RES]=10 // 控制抗性
this.add(hv);
oops.message.dispatchEvent(GameEvent.MasterCalled,{uuid:uuid})
const move = this.get(HeroMoveComp);
move.direction = 1; // 向右移动
move.targetX = 0; // 右边界'
if(HeroInfo[uuid].type==HType.remote){
move.targetX = -100; // 右边界'
}
if(HeroInfo[uuid].type==HType.mage){
move.targetX = -200; // 右边界'
}
smc.vmdata.mission_data.hero_num++
}
reset() {
// 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放
super.destroy();
}
}
@ecs.register('HeroLifecycleSystem')
export class HeroLifecycleSystem extends ecs.ComblockSystem
implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem {
filter() {
return ecs.allOf(HeroMoveComp);
}
entityEnter(e: ecs.Entity): void {
// 英雄实体创建时的特殊处理
const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
mLogger.log(heroAttrs.debugMode, 'HeroLifecycle', `英雄进入世界: ${heroAttrs.hero_name}`);
} else {
mLogger.log(true, 'HeroLifecycle', `英雄进入世界: 实体ID ${e.eid}`);
}
}
entityRemove(e: ecs.Entity): void {
// 英雄实体销毁时的清理工作
const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
mLogger.log(heroAttrs.debugMode, 'HeroLifecycle', `英雄离开世界: ${heroAttrs.hero_name}`);
} else {
mLogger.log(true, 'HeroLifecycle', `英雄离开世界: 实体ID ${e.eid}`);
}
}
}