import { instantiate, Node, Prefab, Vec3 ,v3,resources,SpriteFrame,Sprite,SpriteAtlas, BoxCollider2D, NodePool, tween, Tween} 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 { 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 static pools: Map = new Map(); static readonly MAX_POOL_SIZE: number = 12; static readonly MAX_POOL_TOTAL: number = 60; private static totalPoolSize(): number { let total = 0; this.pools.forEach((pool) => { total += pool.size(); }); return total; } static getFromPool(path: string): Node | null { if (this.pools.has(path)) { const pool = this.pools.get(path)!; while (pool.size() > 0) { const node = pool.get(); if (node && node.isValid) { return node; } } } return null; } static putToPool(path: string, node: Node) { if (!node || !node.isValid) return; if (!this.pools.has(path)) { this.pools.set(path, new NodePool()); } const pool = this.pools.get(path)!; if (pool.size() >= this.MAX_POOL_SIZE || this.totalPoolSize() >= this.MAX_POOL_TOTAL) { node.destroy(); return; } pool.put(node); } static clearPools() { this.pools.forEach((pool) => { while (pool.size() > 0) { const node = pool.get(); if (node && node.isValid) { node.destroy(); } } pool.clear(); }); this.pools.clear(); } static getPoolStats() { let total = 0; this.pools.forEach((pool) => { total += pool.size(); }); return { paths: this.pools.size, total, maxPerPath: this.MAX_POOL_SIZE, maxTotal: this.MAX_POOL_TOTAL }; } protected init() { this.addComponents( 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, is_boss:boolean=false, dropToY:number = pos.y) { 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); } node.parent = scene.entityLayer!.node!.getChildByName("HERO")!; var view = node.getComponent(HeroViewComp)!; const collider = node.getComponent(BoxCollider2D); if (collider) { collider.enabled = false; } 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.hp = model.hp_max = hero.hp; model.ap = hero.ap; model.speed = hero.speed; // 使用成长后的速度 model.type = hero.type; model.fac = FacSet.MON; model.is_boss =is_boss if(!model.is_boss){ model.is_kalami = true; } model.skills = {}; for (const key in hero.skills) { const skill = hero.skills[key]; if (!skill) continue; model.skills[skill.uuid] = { ...skill, ccd: 0 }; } model.updateSkillDistanceCache(); //根据刷怪控制脚本对ap和hp进行加强 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 = dropToY; move.moving = false; const dropDistance = Math.abs(pos.y - dropToY); const dropDuration = Math.max(0.18, Math.min(0.38, dropDistance / 1200)); 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); view.playEnd("down"); move.moving = true; if (collider) { collider.enabled = true; collider.group = BoxSet.MONSTER; collider.apply(); } }) .start(); 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}`); } } }