Files
pixelheros/assets/script/game/hero/Mon.ts
panw 1522e93585 feat: 为英雄和怪物添加下落入场动画
- 在 MissionHeroComp 和 MissionMonComp 中定义下落高度常量
- 修改英雄和怪物的加载方法,接受目标落地高度参数
- 使用 Tween 实现平滑下落动画,下落距离越大持续时间越长
- 下落期间禁用移动和碰撞器,落地后恢复
- 为怪物添加随机下落高度偏移,减轻重叠感
2026-03-19 09:11:11 +08:00

222 lines
7.5 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, 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 { BuffConf, SkillSet } from "../common/config/SkillSet";
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 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<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, 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.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);
//根据刷怪控制脚本对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);
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}`);
}
}
}