feat(monster): 调整怪物AI并重构刷怪系统

重构怪物移动逻辑,移除移动追击相关代码,改为定点攻击模式,无敌人时原地待机,仅根据攻击范围切换攻击状态与朝向。
重构波次刷怪逻辑,删除分段刷怪阶段处理,改为波次准备结束后批量生成所有怪物。
将原6路刷怪改为3行网格布局,调整怪物出生点的X轴起点与间距。
限制单波最大怪物数量为12,简化刷怪分配逻辑为按生成顺序自动排列行列。
清理冗余的运行时状态变量与废弃函数,优化代码整体结构。

BREAKING CHANGES: 怪物攻击逻辑从移动追击改为定点攻击,移除了MonMoveComp中的moveEntity和resolveCombatRange函数;刷怪系统从6路改为3行网格布局,移除了分段刷怪功能与相关状态变量。
This commit is contained in:
pan
2026-06-11 10:47:17 +08:00
parent 8d71cdd050
commit 10f5a9f35d
2 changed files with 46 additions and 153 deletions

View File

@@ -86,19 +86,19 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
}
// 渲染层级统交由 MoveSystem 统一处理,避免两个 System 争抢 setSiblingIndex
// 仅在战斗中才处理索敌和移动
// 仅在战斗中才处理索敌
if (!smc.mission.in_fight) return;
const nearestEnemy = this.findNearestEnemy(e);
if (nearestEnemy) {
/** 有敌人:进入战斗位移逻辑 */
/** 有敌人:进入固定位置攻击逻辑 */
this.processCombatLogic(e, move, view, model, nearestEnemy);
this.syncCombatTarget(model, view, nearestEnemy);
} else {
/** 无敌人:继续向左推进 */
/** 无敌人:原地待机 */
this.clearCombatTarget(model);
model.is_atking = false;
this.moveEntity(view, -1, model.speed / 3);
view.status_change("idle");
}
}
@@ -130,69 +130,20 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
private processCombatLogic(e: ecs.Entity, move: MonMoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
const selfX = view.node.position.x;
const enemyX = enemy.node.position.x;
const dist = Math.abs(selfX - enemyX);
const inRange = this.isEnemyInAttackRange(model, selfX, enemyX);
// 接触判定距离,只有接触英雄才停止移动
const touchDistance = 120;
const isTouching = dist <= touchDistance;
// 攻击判定
// 攻击判定:怪物在固定位置,如果在攻击范围内则攻击,否则原地待机
if (inRange) {
model.is_atking = true;
// 确保朝向敌人
const dir = enemyX > selfX ? 1 : -1;
view.scale = dir;
} else {
model.is_atking = false;
}
// 移动判定:只有接触了英雄才停止移动,否则继续向英雄方向移动
if (isTouching) {
view.status_change("idle");
} else {
const dir = enemyX > selfX ? 1 : -1;
this.moveEntity(view, dir, model.speed / 3);
}
}
private moveEntity(view: HeroViewComp, direction: number, speed: number) {
const model = view.ent.get(HeroAttrsComp);
const move = view.ent.get(MonMoveComp);
if (!model || !move) return;
// 简化的边界限制(怪物主要往左走,英雄防线在左侧,-999999 代表左侧尽头)
const moveMinX = -999999;
const moveMaxX = 999999;
const currentX = view.node.position.x;
const delta = speed * this.dt * direction;
let newX = currentX + delta;
newX = Math.max(moveMinX, Math.min(moveMaxX, newX));
if (Math.abs(newX - currentX) < 0.01) {
view.status_change("idle");
return;
}
view.node.setPosition(newX, move.baseY, 0);
move.direction = direction;
// 确保怪物的朝向表现,向左走 scale=-1向右走 scale=1
if (direction < 0) {
view.scale = -1;
} else if (direction > 0) {
view.scale = 1;
}
view.status_change("move");
}
private resolveCombatRange(model: HeroAttrsComp, defaultMin: number, defaultMax: number): [number, number] {
const minRange = model.getCachedMinSkillDistance();
const maxRange = model.getCachedMaxSkillDistance();
if (maxRange <= 0) return [defaultMin, defaultMax];
const safeMin = Math.max(0, Math.min(minRange, maxRange - 20));
return [safeMin, maxRange];
}
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
const currentView = entity.get(HeroViewComp);
if (!currentView?.node) return null;