From 0129771435d028f35c70c9aca2409f5f18d47919 Mon Sep 17 00:00:00 2001 From: panw Date: Fri, 3 Apr 2026 16:52:12 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E6=80=AA=E7=89=A9=E7=94=9F=E6=88=90):?= =?UTF-8?q?=20=E9=87=8D=E6=9E=84=E6=B3=A2=E6=AC=A1=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E4=B8=8E=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将波次配置从属性迁移至配置文件,增强可维护性 - 重构怪物生成逻辑,使用基于槽位的排队机制 - 移除旧的计时生成方式,改为配置驱动 --- assets/script/game/map/MissionMonComp.ts | 110 ++++++++--------------- assets/script/game/map/RogueConfig.ts | 45 ++++++---- 2 files changed, 66 insertions(+), 89 deletions(-) diff --git a/assets/script/game/map/MissionMonComp.ts b/assets/script/game/map/MissionMonComp.ts index 586c2ecd..3f090946 100644 --- a/assets/script/game/map/MissionMonComp.ts +++ b/assets/script/game/map/MissionMonComp.ts @@ -23,14 +23,6 @@ export class MissionMonCompComp extends CCComp { private static readonly MON_DROP_HEIGHT = 280; @property({ tooltip: "是否启用调试日志" }) private debugMode: boolean = false; - @property({ tooltip: "每波基础普通怪数量" }) - private baseMonstersPerWave: number = 5; - @property({ tooltip: "每波额外增加普通怪数量" }) - private waveMonsterGrowth: number = 0; - @property({ tooltip: "多少波刷新一次 Boss" }) - private bossWaveInterval: number = 5; - @property({ tooltip: "同一波内刷怪间隔(秒)" }) - private waveSpawnCd: number = 0.35; // 刷怪队列(用于插队生成:比如运营活动怪、技能召唤怪、剧情强制怪) // 约定:队列里的怪会优先于常规刷新处理 @@ -53,11 +45,9 @@ export class MissionMonCompComp extends CCComp { private globalSpawnOrder: number = 0; /** 插队刷怪处理计时器 */ private queueTimer: number = 0; - private waveSpawnTimer: number = 0; private currentWave: number = 0; private waveTargetCount: number = 0; private waveSpawnedCount: number = 0; - private bossSpawnedInWave: boolean = false; onLoad(){ this.on(GameEvent.FightReady,this.fight_ready,this) this.on("SpawnSpecialMonster", this.onSpawnSpecialMonster, this); @@ -87,11 +77,9 @@ export class MissionMonCompComp extends CCComp { smc.mission.stop_spawn_mon = false this.globalSpawnOrder = 0 this.queueTimer = 0 - this.waveSpawnTimer = 0 this.currentWave = 0 this.waveTargetCount = 0 this.waveSpawnedCount = 0 - this.bossSpawnedInWave = false this.MonQueue = [] this.startNextWave() mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System"); @@ -108,7 +96,6 @@ export class MissionMonCompComp extends CCComp { if(!smc.mission.in_fight) return; if(smc.mission.stop_spawn_mon) return; this.updateSpecialQueue(dt); - this.updateWaveSpawn(dt); } private updateSpecialQueue(dt: number) { @@ -125,82 +112,50 @@ export class MissionMonCompComp extends CCComp { this.enqueueMonsterRequest(item.uuid, isBoss, upType, Math.max(1, Number(item.level ?? 1)), slotsPerMon, true); } - private updateWaveSpawn(dt: number) { - this.waveSpawnTimer += dt; - if (this.waveSpawnTimer < this.waveSpawnCd) return; - this.waveSpawnTimer = 0; - if (this.waveSpawnedCount >= this.waveTargetCount) { - if (this.isBossWave() && !this.bossSpawnedInWave) { - const bossUuid = this.getRandomBossUuid(); - const bossUpType = this.getRandomUpType(); - this.enqueueMonsterRequest(bossUuid, true, bossUpType, 1, 2); - this.bossSpawnedInWave = true; - } - return; - } - const uuid = this.getRandomNormalMonsterUuid(); - const upType = this.getRandomUpType(); - this.enqueueMonsterRequest(uuid, false, upType, 1, 1); - this.waveSpawnedCount += 1; - } - private startNextWave() { this.currentWave += 1; smc.vmdata.mission_data.level = this.currentWave; - this.waveTargetCount = Math.max(1, this.baseMonstersPerWave + (this.currentWave - 1) * this.waveMonsterGrowth); - this.waveSpawnedCount = 0; - this.bossSpawnedInWave = false; - this.waveSpawnTimer = this.waveSpawnCd; this.resetSlotSpawnData(this.currentWave); - this.primeWaveInitialBurst(); + + let hasBoss = false; + const config = WaveSlotConfig[this.currentWave] || DefaultWaveSlot; + for (const slot of config) { + if (slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss) { + hasBoss = true; + } + } + oops.message.dispatchEvent(GameEvent.NewWave, { wave: this.currentWave, total: this.waveTargetCount, - bossWave: this.isBossWave(), + bossWave: hasBoss, }); } private tryAdvanceWave() { - if (this.waveSpawnedCount < this.waveTargetCount) return; - if (this.isBossWave() && !this.bossSpawnedInWave) return; if (this.hasPendingSlotQueue()) return; if (this.hasActiveSlotMonster()) return; if (smc.vmdata.mission_data.mon_num > 0) return; this.startNextWave(); } - private isBossWave() { - const interval = Math.max(1, Math.floor(this.bossWaveInterval)); - return this.currentWave > 0 && this.currentWave % interval === 0; - } - private getCurrentStage(): number { return Math.max(0, this.currentWave - 1); } private getRandomUpType(): UpType { - // 从 StageGrow 的 key 中采样,保证新增配置无需改逻辑 const keys = Object.keys(StageGrow).map(v => Number(v) as UpType); const index = Math.floor(Math.random() * keys.length); return keys[index] ?? UpType.AP1_HP1; } - private getRandomNormalMonsterUuid(): number { - // MonType 是常量对象,这里通过值采样拿到怪物类型 id - const typeKeys = Object.keys(MonType).map(k => (MonType as any)[k]).filter(v => typeof v === "number"); - const randomType = typeKeys[Math.floor(Math.random() * typeKeys.length)] as number; - // 如果某类型配置被清空,回退到 AP 类型,避免空池异常 - const pool = MonList[randomType] || MonList[MonType.Melee]; + private getRandomUuidByType(monType: number): number { + const pool = (MonList as any)[monType] || MonList[MonType.Melee]; + if (!pool || pool.length === 0) return 6001; const index = Math.floor(Math.random() * pool.length); return pool[index]; } - private getRandomBossUuid(): number { - // 目前 Boss 池可扩展为多个,先走随机抽取 - const index = Math.floor(Math.random() * BossList.length); - return BossList[index]; - } - private resolveGrowPair(upType: UpType, isBoss: boolean): [number, number] { // 普通怪基础成长:StageGrow const grow = StageGrow[upType] || StageGrow[UpType.AP1_HP1]; @@ -214,27 +169,21 @@ export class MissionMonCompComp extends CCComp { return SpawnPowerBias; } - private primeWaveInitialBurst() { - const remain = this.waveTargetCount - this.waveSpawnedCount; - if (remain <= 0) return; - const burstCount = Math.min(this.slotSpawnQueues.length, remain); - for (let i = 0; i < burstCount; i++) { - const uuid = this.getRandomNormalMonsterUuid(); - const upType = this.getRandomUpType(); - this.enqueueMonsterRequest(uuid, false, upType, 1, 1); - } - this.waveSpawnedCount += burstCount; - } - private resetSlotSpawnData(wave: number = 1) { const config: IWaveSlot[] = WaveSlotConfig[wave] || DefaultWaveSlot; let totalSlots = 0; + let totalMonsters = 0; for (const slot of config) { const slotsPerMon = slot.slotsPerMon || 1; + const monCount = slot.monCount || 1; totalSlots += slot.count * slotsPerMon; + totalMonsters += slot.count * monCount; } + this.waveTargetCount = totalMonsters; + this.waveSpawnedCount = 0; + this.slotSpawnQueues = Array.from( { length: totalSlots }, () => [] @@ -243,18 +192,31 @@ export class MissionMonCompComp extends CCComp { { length: totalSlots }, () => null ); - this.confirmWaveSlotTypes(config); - } - - private confirmWaveSlotTypes(config: IWaveSlot[]) { this.slotRangeTypes = []; this.slotSizes = []; + + let slotIndex = 0; for (const slot of config) { const slotsPerMon = slot.slotsPerMon || 1; + const monCount = slot.monCount || 1; + const isBoss = slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss; + for (let i = 0; i < slot.count; i++) { + const currentSlotIndex = slotIndex; + + // 设置槽位类型和大小 for (let s = 0; s < slotsPerMon; s++) { this.slotRangeTypes.push(slot.type); this.slotSizes.push(slotsPerMon); + slotIndex++; + } + + // 根据配置数量,直接在波次开始时把该坑位要刷的所有怪排入队列 + for (let m = 0; m < monCount; m++) { + const uuid = this.getRandomUuidByType(slot.type); + const upType = this.getRandomUpType(); + const request = { uuid, isBoss, upType, monLv: wave, slotsPerMon }; + this.slotSpawnQueues[currentSlotIndex].push(request); } } } diff --git a/assets/script/game/map/RogueConfig.ts b/assets/script/game/map/RogueConfig.ts index 2f93e33d..b42f608f 100644 --- a/assets/script/game/map/RogueConfig.ts +++ b/assets/script/game/map/RogueConfig.ts @@ -37,36 +37,51 @@ export const SpawnPowerBias = 1 export interface IWaveSlot { type: number; // 对应 MonType - count: number; // 怪物数量 + count: number; // 占位数量 slotsPerMon?: number; // 每个怪占用几个位置,默认 1 + monCount: number; // 这个占位排队的怪物总数(每个坑位要刷多少个怪) } -// 每波怪物占位数量配置:数组顺序即为占位从左到右的排列顺序 +// ========================================================================================= +// 【每波怪物占位与刷怪配置说明】 +// 1. 数组顺序:数组中的元素顺序即为战场上怪物从左到右占位的物理顺序。 +// 2. 字段说明: +// - type: 怪物类型 (参考 MonType,如近战 0,远程 1,Boss 3 等)。 +// - count: 该类型在场上同时存在几个并排的占位坑。 +// - monCount: 每个占位坑需要刷出的怪物总数(即每个坑排队的怪物数量)。当场上该坑位的怪死亡后,排队的下一只才会生成。 +// - slotsPerMon: (可选) 单个怪物体积占用几个占位坑,默认为 1。如果是大型 Boss 可设为 2 或更多,它会跨占位降落。 +// +// 举例: +// { type: MonType.Melee, count: 2, monCount: 3 } +// 表示:在对应的位置开启 2 个近战占位坑,每个坑要排队刷出 3 只怪,总计该行配置会刷出 6 只近战怪。 +// +// 【注意】:波次怪物的总数将由所有坑位的 count * monCount 自动累加计算得出。 +// ========================================================================================= export const WaveSlotConfig: { [wave: number]: IWaveSlot[] } = { 1: [ - { type: MonType.Melee, count: 2 }, - { type: MonType.Long, count: 2 } + { type: MonType.Melee, count: 2, monCount: 3 }, + { type: MonType.Long, count: 2, monCount: 2 } ], 2: [ - { type: MonType.Melee, count: 2 }, - { type: MonType.Long, count: 2 }, - { type: MonType.Support, count: 1 } + { type: MonType.Melee, count: 2, monCount: 4 }, + { type: MonType.Long, count: 2, monCount: 3 }, + { type: MonType.Support, count: 1, monCount: 2 } ], 3: [ - { type: MonType.Melee, count: 1 }, - { type: MonType.MeleeBoss, count: 1, slotsPerMon: 2 }, - { type: MonType.Long, count: 2 } + { type: MonType.Melee, count: 1, monCount: 5 }, + { type: MonType.MeleeBoss, count: 1, slotsPerMon: 2, monCount: 1 }, + { type: MonType.Long, count: 2, monCount: 4 } ], 4: [ - { type: MonType.Melee, count: 1 }, - { type: MonType.Long, count: 1 }, - { type: MonType.LongBoss, count: 1, slotsPerMon: 2 } + { type: MonType.Melee, count: 1, monCount: 5 }, + { type: MonType.Long, count: 1, monCount: 5 }, + { type: MonType.LongBoss, count: 1, slotsPerMon: 2, monCount: 1 } ], } // 默认占位配置 (如果在 WaveSlotConfig 中找不到波次,则使用此配置) export const DefaultWaveSlot: IWaveSlot[] = [ - { type: MonType.Melee, count: 2 }, - { type: MonType.Long, count: 3 } + { type: MonType.Melee, count: 2, monCount: 3 }, + { type: MonType.Long, count: 3, monCount: 3 } ]