refactor(hero): 优化英雄与怪物实体加载与注释

- 统一英雄与怪物实体加载流程,增强代码可读性与维护性
- 为 Hero.ts 与 Mon.ts 添加详细方法级注释,说明组件职责与关键逻辑
- 在配置文件中将攻击速度(as)注释更新为技能冷却(skills[0].cd),使配置项含义更清晰
- 修复怪物技能等级未随怪物等级提升的问题,使其与英雄逻辑保持一致
- 优化对象池管理,增加容量统计与调试信息
This commit is contained in:
panw
2026-03-24 14:40:04 +08:00
parent 23cc992579
commit 1fa2be19f7
3 changed files with 120 additions and 49 deletions

View File

@@ -10,14 +10,20 @@ import { GameEvent } from "../common/config/GameEvent";
import { Attrs} from "../common/config/HeroAttrs";
import { MoveComp } from "./MoveComp";
import { mLogger } from "../common/Logger";
/** 角色实体 */
/** 英雄实体:负责英雄节点创建、属性初始化、入场动画与销毁流程 */
@ecs.register(`Hero`)
export class Hero extends ecs.Entity {
/** 英雄数据组件引用 */
HeroModel!: HeroAttrsComp;
/** 英雄表现组件引用 */
View!: HeroViewComp;
/** 英雄移动组件引用 */
HeroMove!: MoveComp;
debugMode: boolean = false; // 是否启用调试模式
/** 调试开关,开启后输出实体层级等调试信息 */
debugMode: boolean = false;
/** 注册实体必需组件:移动 + 属性 */
protected init() {
this.addComponents<ecs.Comp>(
MoveComp,
@@ -25,88 +31,111 @@ export class Hero extends ecs.Entity {
);
}
/** 销毁实体并释放视图节点,防止残留碰撞体与显示对象 */
destroy(): void {
// 销毁节点,防止视觉残留
// 优先销毁节点,避免实体销毁后场景仍残留可见对象
const view = this.get(HeroViewComp);
if (view && view.node && view.node.isValid) {
view.node.destroy();
}
// 手动移除组件,确保 ecs 侧引用及时释放
this.remove(HeroViewComp);
this.remove(HeroAttrsComp);
super.destroy();
}
/** 加载角色 */
/**
* 加载并初始化英雄
* 1) 创建节点并挂到 HERO 层
* 2) 初始化表现与属性数据
* 3) 播放下落入场并在落地后启用碰撞与移动
*/
load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001, dropToY:number = pos.y,hero_lv:number=1) {
// 英雄始终朝右,表现缩放固定为正向
scale = 1
// 英雄等级在当前规则下上限为 3避免超配表范围
if(hero_lv>3) hero_lv=3
// 查找空闲英雄槽位
// 英雄尺寸随等级做轻量放大,强化成长反馈
let size=1+0.1*hero_lv
// 根据配置路径加载英雄预制体
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;
// 统一挂到实体显示层 HERO 节点下
node.parent = scene.entityLayer!.node!.getChildByName("HERO")!;
const collider = node.getComponent(BoxCollider2D);
if (collider) collider.enabled = false; // 先禁用
// 入场过程暂不参与碰撞,防止半空触发战斗逻辑
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);
let hero = HeroInfo[uuid]; // 共用英雄数据
// 从配置中读取英雄静态数据
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;
model.type = hero.type;
model.fac = FacSet.HERO;
// 只有主角才挂载天赋组件
// ✅ 初始化技能数据(迁移到 HeroSkillsComp
// 设置基础属性
// 基础属性按等级倍率初始化
model.ap = hero.ap*model.lv;
model.hp= model.hp_max = hero.hp*model.lv;
model.speed = hero.speed;
// 构建技能表并注入运行时冷却字段 ccd
model.skills = {};
for (const key in hero.skills) {
const skill = hero.skills[key];
if (!skill) continue;
//用于增量 计算最终技能等级英雄等级与技能初始等级均从1开始需各减1抵消故-2最低等级时0
// 最终技能等级 = 初始技能等级 + 英雄等级增量,且下限为 0
model.skills[skill.uuid] = { ...skill, lv: Math.max(0,skill.lv + hero_lv - 2), ccd: 0 };
}
// 缓存技能射程等派生数据,减少战斗帧内重复计算
model.updateSkillDistanceCache();
// 初始化 buff/debuff 系统
// 初始化属性系统(buff/debuff 等动态属性容器)
model.initAttrs();
// 将视图组件注册到实体,打通逻辑与表现
this.add(hv);
// 广播主角召唤事件,触发外部系统监听逻辑
oops.message.dispatchEvent(GameEvent.MasterCalled,{uuid:uuid})
// 初始化移动组件:方向、目标 X、站位基准 Y
const move = this.get(MoveComp);
move.direction = 1; // 向右移动
move.direction = 1;
move.targetX = resolveFormationTargetX(model.fac, model.type);
move.baseY = dropToY;
move.moving = false;
// 依据下落距离自适应入场时长,保证手感稳定
const dropDistance = Math.abs(pos.y - dropToY);
const dropDuration = Math.max(0.18, Math.min(0.38, dropDistance / 1200));
// 停止旧动画后执行下落 tween避免复用节点时动画叠加
Tween.stopAllByTarget(node);
tween(node)
.to(dropDuration, { position: v3(pos.x, dropToY, 0) })
.call(() => {
if (!node || !node.isValid) return;
// 落地后锁定最终位置,切换到落地完成状态
node.setPosition(pos.x, dropToY, 0);
hv.playEnd("down");
move.moving = true;
// 落地后再启用碰撞,避免空中阶段触发伤害结算
if (collider) {
collider.enabled = true;
collider.group = BoxSet.HERO;
@@ -114,26 +143,29 @@ export class Hero extends ecs.Entity {
}
})
.start();
// 维护关卡内英雄数量统计
smc.vmdata.mission_data.hero_num++
}
/** 重置入口:复用 destroy 的释放流程 */
reset() {
// 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放
super.destroy();
}
}
/** 英雄生命周期系统:监听实体进入与移除并输出调试日志 */
@ecs.register('HeroLifecycleSystem')
export class HeroLifecycleSystem extends ecs.ComblockSystem
implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem {
/** 仅处理拥有 MoveComp 的实体 */
filter() {
return ecs.allOf(MoveComp);
}
/** 实体进入世界时记录日志 */
entityEnter(e: ecs.Entity): void {
// 英雄实体创建时的特殊处理
const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
mLogger.log(heroAttrs.debugMode, 'HeroLifecycle', `英雄进入世界: ${heroAttrs.hero_name}`);
@@ -142,8 +174,8 @@ export class HeroLifecycleSystem extends ecs.ComblockSystem
}
}
/** 实体离开世界时记录日志 */
entityRemove(e: ecs.Entity): void {
// 英雄实体销毁时的清理工作
const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
mLogger.log(heroAttrs.debugMode, 'HeroLifecycle', `英雄离开世界: ${heroAttrs.hero_name}`);