From 257dfe4c153ccc5a12dc382bcfe960d9b3824e10 Mon Sep 17 00:00:00 2001 From: pan Date: Thu, 11 Jun 2026 11:13:15 +0800 Subject: [PATCH] =?UTF-8?q?refactor(monster):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=80=AA=E7=89=A9=E7=94=9F=E6=88=90=E4=B8=8E=E6=95=8C=E4=BA=BA?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 统一调整敌人选取算法,加入Y轴权重实现同路优先攻击 2. 重构怪物出生点位配置,改用硬编码数组统一管理 3. 移除怪物生成时的Y轴随机偏移,固定站位避免逻辑冲突 4. 简化怪物生成接口参数,使用索引直接获取预设点位 --- assets/script/game/hero/MonMoveComp.ts | 7 ++- assets/script/game/hero/MoveComp.ts | 6 ++- assets/script/game/map/MissionMonComp.ts | 56 ++++++++++++++---------- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/assets/script/game/hero/MonMoveComp.ts b/assets/script/game/hero/MonMoveComp.ts index c481f02e..0f8e6adc 100644 --- a/assets/script/game/hero/MonMoveComp.ts +++ b/assets/script/game/hero/MonMoveComp.ts @@ -81,6 +81,7 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda } /** 所有移动都锁定在 baseY,避免出现“漂移” */ + // 注意:不再在 MonMoveComp 强行重置 X 轴坐标,避免与其他表现逻辑冲突 if (view.node.position.y !== move.baseY) { view.node.setPosition(view.node.position.x, move.baseY, 0); } @@ -154,13 +155,15 @@ export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpda let nearest: HeroViewComp | null = null; let minDis = Infinity; - /** 一次遍历筛出最近敌人(仅比较 x 轴距离,忽略 Y,飞行和地面都能互相攻击) */ + /** 遍历筛出最近敌人:以 X 轴距离为主,Y 轴距离作为同排的决胜权重,使角色优先攻击同路的敌人 */ ecs.query(this.getHeroViewMatcher()).forEach(e => { const m = e.get(HeroAttrsComp); if (m.fac !== myFac && !m.is_dead) { const v = e.get(HeroViewComp); if (v?.node) { - const d = Math.abs(currentPos.x - v.node.position.x); + const dx = Math.abs(currentPos.x - v.node.position.x); + const dy = Math.abs(currentPos.y - v.node.position.y); + const d = dx + dy * 0.1; // Y轴权重较小,仅在 X 相近时起决定作用 if (d < minDis) { minDis = d; nearest = v; diff --git a/assets/script/game/hero/MoveComp.ts b/assets/script/game/hero/MoveComp.ts index 4c1993a3..60e827a4 100644 --- a/assets/script/game/hero/MoveComp.ts +++ b/assets/script/game/hero/MoveComp.ts @@ -353,13 +353,15 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate let nearest: HeroViewComp | null = null; let minDis = Infinity; - /** 一次遍历筛出最近敌人(仅比较 x 轴距离) */ + /** 遍历筛出最近敌人:以 X 轴距离为主,Y 轴距离作为同排的决胜权重,使角色优先攻击同路的敌人 */ ecs.query(this.getHeroViewMatcher()).forEach(e => { const m = e.get(HeroAttrsComp); if (m.fac !== myFac && !m.is_dead) { const v = e.get(HeroViewComp); if (v?.node) { - const d = Math.abs(currentPos.x - v.node.position.x); + const dx = Math.abs(currentPos.x - v.node.position.x); + const dy = Math.abs(currentPos.y - v.node.position.y); + const d = dx + dy * 0.1; // Y轴权重较小,仅在 X 相近时起决定作用 if (d < minDis) { minDis = d; nearest = v; diff --git a/assets/script/game/map/MissionMonComp.ts b/assets/script/game/map/MissionMonComp.ts index b9230ae9..3bfb4d4b 100644 --- a/assets/script/game/map/MissionMonComp.ts +++ b/assets/script/game/map/MissionMonComp.ts @@ -53,14 +53,28 @@ export class MissionMonCompComp extends CCComp { /** 怪物最多 12 个 */ private static readonly MAX_MONSTERS = 12; - /** 怪物出生点起点 X */ - private static readonly MON_SPAWN_START_X = 60; - /** 怪物出生的 X 间距 (列距) */ - private static readonly MON_SPAWN_GAP_X = 80; /** 怪物出生掉落高度 */ private static readonly MON_DROP_HEIGHT = 0; - /** 3行高度偏移 (行距) */ - private static readonly ROW_Y_OFFSETS = [90, 0, -90]; + + /** 硬编码的 12 个怪物占位点 (3行4列) */ + public static readonly MON_POSITIONS: Vec3[] = [ + // 第 1 列 (X=60) + v3(60, BoxSet.GAME_LINE + 90, 0), // index 0: Top + v3(60, BoxSet.GAME_LINE, 0), // index 1: Mid + v3(60, BoxSet.GAME_LINE - 90, 0), // index 2: Bot + // 第 2 列 (X=140) + v3(140, BoxSet.GAME_LINE + 90, 0), // index 3: Top + v3(140, BoxSet.GAME_LINE, 0), // index 4: Mid + v3(140, BoxSet.GAME_LINE - 90, 0), // index 5: Bot + // 第 3 列 (X=220) + v3(220, BoxSet.GAME_LINE + 90, 0), // index 6: Top + v3(220, BoxSet.GAME_LINE, 0), // index 7: Mid + v3(220, BoxSet.GAME_LINE - 90, 0), // index 8: Bot + // 第 4 列 (X=300) + v3(300, BoxSet.GAME_LINE + 90, 0), // index 9: Top + v3(300, BoxSet.GAME_LINE, 0), // index 10: Mid + v3(300, BoxSet.GAME_LINE - 90, 0), // index 11: Bot + ]; // ======================== 编辑器属性 ======================== @@ -202,10 +216,9 @@ export class MissionMonCompComp extends CCComp { MonList[MonType.LongBoss].includes(item.uuid); const spawnIndex = this.waveSpawnedCount++; - const row = spawnIndex % 3; - const col = Math.floor(spawnIndex / 3); + const targetPosIndex = spawnIndex % MissionMonCompComp.MAX_MONSTERS; - // 构造一个模拟的 GeneratedMonster 数据传递给 addMonsterAt + // 构造一个模拟的 GeneratedMonster 数据传递给 addMonsterAtGrid const base = HeroInfo[item.uuid]; const monData: GeneratedMonster = { uuid: item.uuid, @@ -216,7 +229,7 @@ export class MissionMonCompComp extends CCComp { isBoss: isBoss, spawnIndex: 0 }; - this.addMonsterAtGrid(row, col, monData, item.level); + this.addMonsterAtGrid(targetPosIndex, monData, item.level); } // ======================== 波次管理 ======================== @@ -243,10 +256,9 @@ export class MissionMonCompComp extends CCComp { let count = Math.min(this.pendingMonsters.length, MissionMonCompComp.MAX_MONSTERS); for (let i = 0; i < count; i++) { const monData = this.pendingMonsters.shift()!; - const row = this.waveSpawnedCount % 3; - const col = Math.floor(this.waveSpawnedCount / 3); + const targetPosIndex = this.waveSpawnedCount % MissionMonCompComp.MAX_MONSTERS; console.log(`[MissionMonComp] [PhasePrepareEnd] 准备生成怪物 UUID=${monData.uuid}, 当前已生成数量=${this.waveSpawnedCount}`); - this.addMonsterAtGrid(row, col, monData); + this.addMonsterAtGrid(targetPosIndex, monData); this.waveSpawnedCount++; } // 生成完毕后清空 pendingMonsters @@ -279,30 +291,28 @@ export class MissionMonCompComp extends CCComp { // ======================== 怪物生成 ======================== /** - * 在指定层级、指定索引处生成一个怪物: + * 在指定位置索引处生成一个怪物: * - * @param row 行 (0, 1, 2) - * @param col 列 (0, 1, 2, 3) + * @param posIndex 位置索引 (0-11) * @param monData 新引擎生成的怪物数据 (含 uuid, hp, ap, affixes 等) * @param monLv 怪物等级 (仅对旧有的 level 参数做兼容,实际属性由 monData 决定) */ private addMonsterAtGrid( - row: number, - col: number, + posIndex: number, monData: GeneratedMonster, monLv: number = 1 ) { let mon = ecs.getEntity(Monster); let scale = -1; - // 计算坐标 - const spawnX = MissionMonCompComp.MON_SPAWN_START_X + col * MissionMonCompComp.MON_SPAWN_GAP_X; - const randomY = Math.random() * 20 - 10; // -10 到 10 的随机Y轴偏移 - const landingY = BoxSet.GAME_LINE + MissionMonCompComp.ROW_Y_OFFSETS[row] + randomY + (monData.isBoss ? 6 : 0); + // 获取硬编码的占位点坐标,不再使用随机偏移 + const basePos = MissionMonCompComp.MON_POSITIONS[posIndex]; + const spawnX = basePos.x; + const landingY = basePos.y + (monData.isBoss ? 6 : 0); const spawnPos: Vec3 = v3(spawnX, landingY + MissionMonCompComp.MON_DROP_HEIGHT, 0); this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999; - mon.load(spawnPos, scale, monData.uuid, monData.isBoss, landingY, monLv, row); + mon.load(spawnPos, scale, monData.uuid, monData.isBoss, landingY, monLv, posIndex); // 设置渲染排序 const move = mon.get(MonMoveComp);