feat(monster&spawn): 新增飞行怪物支持,重构怪物移动与刷怪系统
抽离MonMoveComp拆分怪物移动逻辑,让MoveComp仅负责英雄移动 新增Fly和FlyBoss怪物类型,配置三层飞行轨道支持空中怪物 重写波次刷怪逻辑,移除固定5槽限制,按轨道自由排布怪物 将怪物生成上限与恢复阈值从5/3调整为50/30 优化渲染排序逻辑,为飞行怪添加持续浮动动画 移除跨波怪物属性继承,波次切换时自动清理残留怪物
This commit is contained in:
@@ -5,6 +5,7 @@ import { smc } from "../common/SingletonModuleComp";
|
||||
import { FacSet } from "../common/config/GameSet";
|
||||
import { HeroDisVal, HType } from "../common/config/heroSet";
|
||||
import { BoxCollider2D, Node } from "cc";
|
||||
import { MonMoveComp } from "./MonMoveComp";
|
||||
|
||||
@ecs.register('MoveComp')
|
||||
export class MoveComp extends ecs.Comp {
|
||||
@@ -47,8 +48,10 @@ interface MoveFacConfig {
|
||||
|
||||
@ecs.register('MoveSystem')
|
||||
export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||
/** 近战判定射程(来自 heroSet) */
|
||||
private readonly meleeAttackRange = HeroDisVal[HType.Melee];
|
||||
/** 近战判定射程 */
|
||||
private readonly meleeAttackRange = 250;
|
||||
/** 远程判定射程 */
|
||||
private readonly longAttackRange = 600;
|
||||
private readonly heroFrontAnchorX = -100;
|
||||
private readonly monFrontAnchorX = 0;
|
||||
/** 常规同阵营横向最小间距(英雄) */
|
||||
@@ -106,21 +109,13 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
/** 战斗未开始/暂停时不驱动移动 */
|
||||
if (!smc.mission.play || smc.mission.pause) return;
|
||||
|
||||
/** 在战斗阶段,英雄和怪物不移动 */
|
||||
if (smc.mission.in_fight) return;
|
||||
|
||||
const model = e.get(HeroAttrsComp);
|
||||
const move = e.get(MoveComp);
|
||||
const view = e.get(HeroViewComp);
|
||||
if (!model || !move || !view || !view.node) return;
|
||||
if (model.fac !== FacSet.HERO && model.fac !== FacSet.MON) return;
|
||||
if (model.fac !== FacSet.HERO) return; // 只处理英雄移动
|
||||
if (!move.moving) return;
|
||||
/** 关卡阶段性冻结怪物行为 */
|
||||
if (model.fac === FacSet.MON && smc.mission.stop_mon_action) {
|
||||
this.clearCombatTarget(model);
|
||||
view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.is_stop || model.is_dead || model.is_reviving || model.isFrost()) {
|
||||
this.clearCombatTarget(model);
|
||||
if (!model.is_reviving) view.status_change("idle");
|
||||
@@ -131,7 +126,10 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
if (view.node.position.y !== move.baseY) {
|
||||
view.node.setPosition(view.node.position.x, move.baseY, 0);
|
||||
}
|
||||
this.updateRenderOrder(view);
|
||||
|
||||
// 渲染层级重排放在独立的系统或这里统筹,这里我们让它处理所有英雄和怪物的排序
|
||||
this.updateRenderOrder();
|
||||
|
||||
const nearestEnemy = this.findNearestEnemy(e);
|
||||
if (nearestEnemy) {
|
||||
/** 有敌人:进入战斗位移逻辑 */
|
||||
@@ -169,12 +167,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
const dist = Math.abs(selfX - enemyX);
|
||||
const rangeType = model.type as HType.Melee | HType.Mid | HType.Long;
|
||||
if (rangeType === HType.Melee) return dist <= this.meleeAttackRange;
|
||||
if (rangeType === HType.Long) {
|
||||
const [, maxRange] = this.resolveCombatRange(model, 360, 720);
|
||||
return dist <= maxRange;
|
||||
}
|
||||
const [, maxRange] = this.resolveCombatRange(model, 120, 720);
|
||||
return dist <= maxRange;
|
||||
return dist <= this.longAttackRange;
|
||||
}
|
||||
|
||||
private processCombatLogic(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
@@ -366,30 +359,42 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
return nearest;
|
||||
}
|
||||
|
||||
private updateRenderOrder(view: HeroViewComp) {
|
||||
private updateRenderOrder() {
|
||||
const scene = smc.map?.MapView?.scene;
|
||||
const actorRoot = scene?.entityLayer?.node?.getChildByName("HERO");
|
||||
if (!actorRoot) return;
|
||||
|
||||
if (view.node.parent !== actorRoot) {
|
||||
view.node.parent = actorRoot;
|
||||
}
|
||||
|
||||
const now = Date.now() / 1000;
|
||||
if (now - this.lastRenderSortAt < this.renderSortInterval) return;
|
||||
this.lastRenderSortAt = now;
|
||||
|
||||
this.renderEntryCount = 0;
|
||||
ecs.query(this.getHeroMoveMatcher()).forEach(e => {
|
||||
ecs.query(this.getHeroViewMatcher()).forEach(e => {
|
||||
const attrs = e.get(HeroAttrsComp);
|
||||
const actorView = e.get(HeroViewComp);
|
||||
const actorMove = e.get(MoveComp);
|
||||
if (!attrs || !actorView?.node || !actorMove || attrs.is_dead) return;
|
||||
if (!attrs || !actorView?.node || attrs.is_dead) return;
|
||||
if (attrs.fac !== FacSet.HERO && attrs.fac !== FacSet.MON) return;
|
||||
|
||||
if (actorView.node.parent !== actorRoot) {
|
||||
actorView.node.parent = actorRoot;
|
||||
}
|
||||
const frontScore = attrs.fac === FacSet.HERO ? actorView.node.position.x : -actorView.node.position.x;
|
||||
|
||||
// 获取 spawnOrder,由于可能挂载 MoveComp 或 MonMoveComp,我们需要动态获取
|
||||
let spawnOrder = 0;
|
||||
const heroMove = e.get(MoveComp);
|
||||
if (heroMove) {
|
||||
spawnOrder = heroMove.spawnOrder;
|
||||
} else {
|
||||
const monMove = e.get(MonMoveComp);
|
||||
if (monMove) {
|
||||
spawnOrder = monMove.spawnOrder;
|
||||
}
|
||||
}
|
||||
|
||||
const isFly = actorView.node.position.y > 50;
|
||||
// 飞行怪在最前,其次是地面的 X 坐标
|
||||
const frontScore = isFly ? 999999 - actorView.node.position.x : (attrs.fac === FacSet.HERO ? actorView.node.position.x : -actorView.node.position.x);
|
||||
|
||||
const entryIndex = this.renderEntryCount;
|
||||
let entry = this.renderEntries[entryIndex];
|
||||
if (!entry) {
|
||||
@@ -399,7 +404,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
entry.node = actorView.node;
|
||||
entry.bossPriority = attrs.is_boss ? 1 : 0;
|
||||
entry.frontScore = frontScore;
|
||||
entry.spawnOrder = actorMove.spawnOrder;
|
||||
entry.spawnOrder = spawnOrder;
|
||||
entry.eid = e.eid;
|
||||
this.renderEntryCount += 1;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user