refactor(monster&cast): 重构怪物移动与AOE技能目标逻辑

1. 移除固定网格站位,怪物从右侧统一出生并向左推进
2. 修改AOE技能索敌逻辑,直接使用最近敌人真实位置
3. 新增怪物自动推进与战斗停船逻辑,优化战斗体验
This commit is contained in:
panFD
2026-07-20 23:20:24 +08:00
parent 05dc771d4e
commit 48e0ab06c5
3 changed files with 104 additions and 65 deletions

View File

@@ -4,11 +4,12 @@
*
* 职责:
* 1. 管理每一波怪物的生成计划:根据 RogueConfig 生成怪物。
* 2. 自动推进波次在准备阶段结束时PhasePrepareEnd统一刷出怪物
* 2. 自动推进波次在准备阶段结束时PhasePrepareEnd把怪物转入逐个刷出队列
*
* 关键设计:
* - 采用 12 个硬编码的网格位置点 (MON_POSITIONS3行x4列)
* - 每次生成最多 12 个怪物,固定在位置点。
* - 所有怪物统一从右侧 X=400 出生点逐个刷出MON_SPAWN_INTERVAL 节奏控制)。
* - 实际阵型与推进由 MonMoveComp 在战斗中向左移动自然形成,不再使用固定网格点。
* - 槽位索引3行×4列仅用于 monGrid 寻路与 SCastSystem 索敌定位。
* - 上一波残留怪在波次结束/开始时统一清理。
*/
import { _decorator, v3, Vec3 } from "cc";
@@ -35,25 +36,29 @@ export class MissionMonCompComp extends CCComp {
private static readonly MAX_MONSTERS = 12;
/** 怪物出生掉落高度 */
private static readonly MON_DROP_HEIGHT = 0;
/** 怪物统一从右侧 X=400 出生点逐个刷出,随后向左推进 */
private static readonly MON_SPAWN_X = 400;
/** 逐个刷怪间隔(秒):保证怪物排成纵队,避免堆叠 */
private static readonly MON_SPAWN_INTERVAL = 0.3;
/** 硬编码的 12 个怪物占位点 (3行4列) */
/**
* 12 个怪物占位点:所有槽位统一以右侧出生点 (X=400) 作为坐标,
* 实际阵型由 MonMoveComp 在战斗中向左推进时自然拉开。
* 槽位索引仍保留 3 行 × 4 列结构,供 monGrid 寻路与 SCastSystem 索敌使用。
*/
public static readonly MON_POSITIONS: Vec3[] = [
// 第 1 列 (X=60)
v3(0, BoxSet.GAME_LINE, 0), // index 0: Top
v3(0, BoxSet.GAME_LINE, 0), // index 1: Mid
v3(0, BoxSet.GAME_LINE, 0), // index 2: Bot
// 第 2 列 (X=140)
v3(90, BoxSet.GAME_LINE, 0), // index 3: Top
v3(90, BoxSet.GAME_LINE, 0), // index 4: Mid
v3(90, BoxSet.GAME_LINE, 0), // index 5: Bot
// 第 3 列 (X=220)
v3(180, BoxSet.GAME_LINE, 0), // index 6: Top
v3(180, BoxSet.GAME_LINE, 0), // index 7: Mid
v3(180, BoxSet.GAME_LINE, 0), // index 8: Bot
// 第 4 列 (X=300)
v3(270, BoxSet.GAME_LINE, 0), // index 9: Top
v3(270, BoxSet.GAME_LINE, 0), // index 10: Mid
v3(270, BoxSet.GAME_LINE, 0), // index 11: Bot
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 0: Col1-Top
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 1: Col1-Mid
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 2: Col1-Bot
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 3: Col2-Top
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 4: Col2-Mid
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 5: Col2-Bot
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 6: Col3-Top
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 7: Col3-Mid
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 8: Col3-Bot
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 9: Col4-Top
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 10: Col4-Mid
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 11: Col4-Bot
];
// ======================== 编辑器属性 ========================
@@ -71,8 +76,12 @@ export class MissionMonCompComp extends CCComp {
private waveTargetCount: number = 0;
/** 当前波已生成的怪物数量 */
private waveSpawnedCount: number = 0;
/** 等待生成的怪物队列 */
/** 等待生成的怪物队列(波次总池) */
private pendingMonsters: GeneratedMonster[] = [];
/** 逐个刷怪队列:从 pendingMonsters 转入,按节奏在 update 中释放 */
private spawnQueue: GeneratedMonster[] = [];
/** 逐个刷怪累计计时(秒) */
private spawnTimer: number = 0;
// ======================== 生命周期 ========================
@@ -83,7 +92,20 @@ export class MissionMonCompComp extends CCComp {
}
protected update(dt: number): void {
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length;
// pending_mon_num 同时统计未刷出的总池与正在释放的队列,确保 UI 与全灭判定一致
smc.vmdata.mission_data.pending_mon_num = this.pendingMonsters.length + this.spawnQueue.length;
// 逐个刷怪:按 MON_SPAWN_INTERVAL 节奏从队列释放
if (this.spawnQueue.length > 0) {
this.spawnTimer += dt;
if (this.spawnTimer >= MissionMonCompComp.MON_SPAWN_INTERVAL) {
this.spawnTimer = 0;
const monData = this.spawnQueue.shift()!;
const targetPosIndex = this.waveSpawnedCount % MissionMonCompComp.MAX_MONSTERS;
this.addMonsterAtGrid(targetPosIndex, monData, this.currentWave);
this.waveSpawnedCount++;
}
}
}
start() {}
@@ -115,6 +137,8 @@ export class MissionMonCompComp extends CCComp {
this.waveTargetCount = 0;
this.waveSpawnedCount = 0;
this.pendingMonsters = [];
this.spawnQueue = [];
this.spawnTimer = 0;
// 预生成第一波数据以获取数量和 Boss 信息
const monsters = spawningEngine.generateWave(this.currentWave);
@@ -142,24 +166,24 @@ export class MissionMonCompComp extends CCComp {
private onPhasePrepareEnd() {
this.resetSlotSpawnData();
// 准备结束阶段,立即刷出本波所有怪物
// 准备结束阶段:把本波待刷怪物转入逐个释放队列,
// 实际生成时机由 update() 按 MON_SPAWN_INTERVAL 节奏触发,
// 让怪物从 X=400 出生点排成纵队依次向左推进。
if (this.pendingMonsters.length > 0) {
let count = Math.min(this.pendingMonsters.length, MissionMonCompComp.MAX_MONSTERS);
const count = Math.min(this.pendingMonsters.length, MissionMonCompComp.MAX_MONSTERS);
for (let i = 0; i < count; i++) {
const monData = this.pendingMonsters.shift()!;
const targetPosIndex = this.waveSpawnedCount % MissionMonCompComp.MAX_MONSTERS;
this.addMonsterAtGrid(targetPosIndex, monData, this.currentWave);
this.waveSpawnedCount++;
this.spawnQueue.push(this.pendingMonsters.shift()!);
}
this.pendingMonsters = [];
// 让首个怪物在下一帧立即刷出,避免额外延迟
this.spawnTimer = MissionMonCompComp.MON_SPAWN_INTERVAL;
}
}
// ======================== 槽位管理 ========================
/**
* 清理上一波残留怪物,并重置生成计数
* 清理上一波残留怪物,并重置生成计数与逐个刷怪队列
*/
private resetSlotSpawnData() {
ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => {
@@ -168,8 +192,11 @@ export class MissionMonCompComp extends CCComp {
e.destroy();
}
});
this.waveSpawnedCount = 0;
// 同步丢弃上一波未释放的队列,避免与新一波混合
this.spawnQueue = [];
this.spawnTimer = 0;
}
// ======================== 怪物生成 ========================