Files
pixelheros/assets/script/game/hero/Mon.ts
walkpan b12b421823 refactor: 合并英雄与怪物移动组件为通用 MoveComp
重构移动系统,将 HeroMoveComp 和 MonMoveComp 合并为通用的 MoveComp 组件,统一移动逻辑。
- 移除 HeroMasterComp 相关代码,简化实体查询
- 统一战斗范围计算和阵型回归逻辑
- 调整移动边界和撤退范围配置
- 优化敌人查找算法,提高性能
2026-03-13 15:54:12 +08:00

184 lines
7.0 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, NodePool} 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 { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet";
import { HeroInfo } from "../common/config/heroSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { BuffConf, SkillSet } from "../common/config/SkillSet";
import { getMonAttr, MonType } from "../map/RogueConfig";
import { HeroViewComp } from "./HeroViewComp";
import { MoveComp } from "./MoveComp";
import { mLogger } from "../common/Logger";
/** 角色实体 */
@ecs.register(`Monster`)
export class Monster extends ecs.Entity {
HeroModel!: HeroAttrsComp;
HeroView!: HeroViewComp;
MonMove!: MoveComp;
private debugMode: boolean = false; // 是否启用调试模式
// 多键对象池Map<prefabPath, NodePool>
static pools: Map<string, NodePool> = new Map();
static getFromPool(path: string): Node | null {
if (this.pools.has(path)) {
const pool = this.pools.get(path)!;
if (pool.size() > 0) {
return pool.get();
}
}
return null;
}
static putToPool(path: string, node: Node) {
if (!this.pools.has(path)) {
this.pools.set(path, new NodePool());
}
this.pools.get(path)!.put(node);
}
protected init() {
this.addComponents<ecs.Comp>(
MoveComp,
HeroAttrsComp,
);
}
destroy(): void {
// 回收节点到对象池
const model = this.get(HeroAttrsComp);
const view = this.get(HeroViewComp);
if (model && view && view.node && view.node.isValid) {
const path = "game/heros/" + HeroInfo[model.hero_uuid].path;
Monster.putToPool(path, view.node);
}
this.remove(HeroViewComp);
this.remove(HeroAttrsComp);
super.destroy();
}
/** 加载角色 */
load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001,lv:number=1,monType:MonType=MonType.NORMAL, buffs: BuffConf[] = [],is_call=false, lane: number = 0, spawnOrder: number = 0, gameTime: number = 0) {
scale=-1
let size=1
var scene = smc.map.MapView.scene;
var path = "game/heros/"+HeroInfo[uuid].path;
// 尝试从池中获取
let node = Monster.getFromPool(path);
if (!node) {
var prefab: Prefab = oops.res.get(path, Prefab)!;
node = instantiate(prefab);
}
let LINE1=scene.entityLayer!.node!.getChildByName("LINE1")!;
let LINE2=scene.entityLayer!.node!.getChildByName("LINE2")!;
let LINE3=scene.entityLayer!.node!.getChildByName("LINE3")!;
let LINE4=scene.entityLayer!.node!.getChildByName("LINE4")!;
// 🔥 设置初始 SiblingIndex - 防止溢出
const baseLane = lane === 0 ? LINE1 : lane === 1 ? LINE2 : lane === 2 ? LINE3 : LINE4;
node.parent = baseLane
var view = node.getComponent(HeroViewComp)!;
const collider = node.getComponent(BoxCollider2D);
if (collider) {
// 先禁用,下一帧启用,确保物理系统能正确注册新激活的节点
collider.enabled = false;
view.scheduleOnce(() => {
if (node && node.isValid) {
collider.enabled = true;
collider.group = BoxSet.MONSTER; // 确保碰撞组正确
collider.apply(); // 强制应用更改(如果需要)
}
}, 0); // 0延迟等于下一帧执行
}
node.setScale(size*node.scale.x,size*node.scale.y);
node.setPosition(pos)
const model = this.get(HeroAttrsComp);
let hero = HeroInfo[uuid]; // 共用英雄数据
// 设置 View 层属性(表现相关)
view.scale = scale;
view.box_group = BoxSet.MONSTER;
// 设置 Model 层属性(数据相关)
model.hero_uuid = uuid;
model.hero_name = hero.name;
model.lv = lv;
model.type = hero.type;
model.fac = FacSet.MON;
model.is_boss = monType == MonType.BOSS;
if(model.is_boss) node.parent = scene.entityLayer!.node!.getChildByName("HERO")!;
if(!model.is_boss){
model.is_kalami = true;
}
// 根据等级和类型获取怪物属性(使用新的动态成长系统)
const {hp,ap, speed} = getMonAttr(lv, uuid, monType, gameTime);
// 初始化属性数组
model.hp = model.hp_max = hp;
model.ap = ap;
model.speed = speed; // 使用成长后的速度
model.a_cd_max=hero.as
model.s_cd_max=hero.ss
model.back_chance=FightSet.BACK_CHANCE
// ✅ 初始化技能数据(迁移到 HeroSkillsComp
if(hero.skills[0]) model.atk_id=hero.skills[0]
if(hero.skills[1]) model.skill_id=hero.skills[1]
model.updateSkillDistanceCache(model.skill_id || model.atk_id);
this.add(view);
// 重置视图状态(对象池复用时必须)
view.init();
oops.message.dispatchEvent("monster_load",this)
// 初始化移动参数,包括线路和生成顺序
const move = this.get(MoveComp);
move.reset();
move.direction = -1; // 向左移动
move.targetX = Math.max(-320, Math.min(320, pos.x));
move.baseY = pos.y;
move.lane = lane; // 设置线路标识
move.spawnOrder = spawnOrder; // 设置生成顺序
smc.vmdata.mission_data.mon_num++
}
reset() {
// 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放
super.destroy();
}
}
@ecs.register('MonLifecycleSystem')
export class MonLifecycleSystem extends ecs.ComblockSystem
implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem {
debugMode: boolean = false; // 是否启用调试模式
filter() {
return ecs.allOf(MoveComp);
}
entityEnter(e: ecs.Entity): void {
// 怪物实体创建时的特殊处理
const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
mLogger.log(this.debugMode, 'MonLifecycleSystem', `怪物进入世界: ${heroAttrs.hero_name}`);
} else {
mLogger.log(this.debugMode, 'MonLifecycleSystem', `怪物进入世界: 实体ID ${e.eid}`);
}
}
entityRemove(e: ecs.Entity): void {
// 怪物实体销毁时的清理工作
const heroAttrs = e.get(HeroAttrsComp);
if (heroAttrs) {
mLogger.log(this.debugMode, 'MonLifecycleSystem', `怪物离开世界: ${heroAttrs.hero_name}`);
} else {
mLogger.log(this.debugMode, 'MonLifecycleSystem', `怪物离开世界: 实体ID ${e.eid}`);
}
}
}