From aa2bf8d6f6cc3dc95f6461039a9908b559453173 Mon Sep 17 00:00:00 2001 From: panw Date: Tue, 31 Mar 2026 15:00:07 +0800 Subject: [PATCH] =?UTF-8?q?refactor(game):=20=E7=AE=80=E5=8C=96=E9=98=B5?= =?UTF-8?q?=E5=9E=8B=E7=A7=BB=E5=8A=A8=E9=80=BB=E8=BE=91=E5=B9=B6=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E5=A4=8D=E6=9D=82=E7=A2=B0=E6=92=9E=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 `clampXByAllies` 及相关辅助方法,简化同排单位间的移动阻挡计算 - 重构 `getFormationSlotX` 方法,使用统一的战斗优先级和等级排序,不再按角色类型分层 - 新增 `moveToSlot` 方法集中处理向阵型位置移动的逻辑 - 引入 `isFormationParticipant` 方法,通过检查碰撞体启用状态更准确地筛选阵型参与者 - 删除多个未使用的移动调整参数,如 `meleeMinEnemyDistanceX`、`meleeOvertakeSpeedGap` 等 --- assets/script/game/hero/MoveComp.ts | 237 +++++----------------------- 1 file changed, 43 insertions(+), 194 deletions(-) diff --git a/assets/script/game/hero/MoveComp.ts b/assets/script/game/hero/MoveComp.ts index 85772797..35773a51 100644 --- a/assets/script/game/hero/MoveComp.ts +++ b/assets/script/game/hero/MoveComp.ts @@ -4,7 +4,7 @@ import { HeroAttrsComp } from "./HeroAttrsComp"; import { smc } from "../common/SingletonModuleComp"; import { FacSet } from "../common/config/GameSet"; import { HeroDisVal, HType, resolveFormationTargetX } from "../common/config/heroSet"; -import { Node } from "cc"; +import { BoxCollider2D, Node } from "cc"; @ecs.register('MoveComp') export class MoveComp extends ecs.Comp { @@ -49,20 +49,8 @@ interface MoveFacConfig { export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { /** 近战判定射程(来自 heroSet) */ private readonly meleeAttackRange = HeroDisVal[HType.Melee]; - /** 近战贴脸最小距离,避免完全重叠 */ - private readonly meleeMinEnemyDistanceX = 60; - /** 同优先级近战允许“超车”时,至少要快这么多 */ - private readonly meleeOvertakeSpeedGap = 20; /** 常规同阵营横向最小间距 */ private readonly allySpacingX = 60; - /** 允许临时压缩站位时的最小间距 */ - private readonly allyOverlapSpacingX = 14; - /** 友军偏离其目标点超过该值,可放宽让路 */ - private readonly displacementReleaseX = 10; - /** 即将进入攻击位的锁定阈值 */ - private readonly attackReadyLockX = 10; - /** 目标距离足够远才触发“借道前压” */ - private readonly attackPassThresholdX = 60; /** 纵向判定为同排的最大 Y 差 */ private readonly minSpacingY = 30; /** 渲染层级重排节流,避免每帧排序 */ @@ -212,56 +200,16 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate } private processFormationCombat(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp) { - const currentX = view.node.position.x; const targetX = this.getFormationSlotX(e, model, move.baseY); move.targetX = targetX; - const needMoveToFormation = Math.abs(currentX - targetX) > 5; - if (needMoveToFormation) { - const dir = targetX > currentX ? 1 : -1; - move.direction = dir; - const speed = model.speed / 3; - this.moveEntity(view, dir, speed); - model.is_atking = true; - } else { - view.status_change("idle"); - model.is_atking = true; - } - } - - private performRetreat(view: HeroViewComp, move: MoveComp, model: HeroAttrsComp, currentX: number) { - const cfg = this.facConfigs[model.fac] || this.facConfigs[FacSet.HERO]; - const retreatMinX = Math.min(cfg.retreatBackX, cfg.retreatFrontX); - const retreatMaxX = Math.max(cfg.retreatBackX, cfg.retreatFrontX); - const safeRetreatX = currentX - move.direction * 50; - if (safeRetreatX >= retreatMinX && safeRetreatX <= retreatMaxX) { - const retreatSpeed = (model.speed / 3) * 0.8; - this.moveEntity(view, -move.direction, retreatSpeed); - model.is_atking = false; - } else { - view.status_change("idle"); - model.is_atking = true; - } + this.moveToSlot(view, move, model, targetX); + model.is_atking = true; } private processReturnFormation(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp) { - const currentX = view.node.position.x; const targetX = this.getFormationSlotX(e, model, move.baseY); move.targetX = targetX; - - if (Math.abs(currentX - targetX) > 5) { - const dir = targetX > currentX ? 1 : -1; - const speed = model.speed / 3; - move.direction = dir; - this.moveEntity(view, dir, speed); - const newX = view.node.position.x; - if ((dir === 1 && newX > targetX) || (dir === -1 && newX < targetX)) { - if (!this.hasAnyActorTooClose(e, targetX, view.node.position.y)) { - view.node.setPosition(targetX, view.node.position.y, 0); - } - } - } else { - view.status_change("idle"); - } + this.moveToSlot(view, move, model, targetX); } private getFormationSlotX(self: ecs.Entity, model: HeroAttrsComp, baseY: number): number { @@ -269,24 +217,20 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate const moveMinX = Math.min(cfg.moveBackX, cfg.moveFrontX); const moveMaxX = Math.max(cfg.moveBackX, cfg.moveFrontX); const forwardDir = model.fac === FacSet.MON ? -1 : 1; - const role = model.type as HType; - const sameRoleAllies: ecs.Entity[] = []; - let hasMeleeAlly = false; - let hasMidAlly = false; + const laneAllies: ecs.Entity[] = []; ecs.query(this.getHeroMoveMatcher()).forEach(e => { - const attrs = e.get(HeroAttrsComp); - const view = e.get(HeroViewComp); - if (!attrs || !view?.node) return; - if (attrs.is_dead || attrs.is_reviving) return; - if (attrs.fac !== model.fac) return; - if (Math.abs(view.node.position.y - baseY) >= this.minSpacingY) return; - const allyRole = attrs.type as HType; - if (allyRole === HType.Melee) hasMeleeAlly = true; - if (allyRole === HType.Mid) hasMidAlly = true; - if ((attrs.type as HType) !== role) return; - sameRoleAllies.push(e); + if (!this.isFormationParticipant(e, model.fac, baseY)) return; + laneAllies.push(e); }); - sameRoleAllies.sort((a, b) => { + laneAllies.sort((a, b) => { + const attrsA = a.get(HeroAttrsComp); + const attrsB = b.get(HeroAttrsComp); + const priorityA = attrsA ? this.getCombatPriority(attrsA) : 0; + const priorityB = attrsB ? this.getCombatPriority(attrsB) : 0; + if (priorityA !== priorityB) return priorityB - priorityA; + const lvA = attrsA?.lv ?? 1; + const lvB = attrsB?.lv ?? 1; + if (lvA !== lvB) return lvB - lvA; const moveA = a.get(MoveComp); const moveB = b.get(MoveComp); const orderA = moveA?.spawnOrder ?? 0; @@ -294,18 +238,25 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate if (orderA !== orderB) return orderA - orderB; return a.eid - b.eid; }); - const slotIndex = Math.max(0, sameRoleAllies.findIndex(entity => entity === self)); - let roleDepth = 0; - if (role === HType.Mid) { - roleDepth = hasMeleeAlly ? 1 : 0; - } else if (role === HType.Long) { - roleDepth = (hasMeleeAlly ? 1 : 0) + (hasMidAlly ? 1 : 0); - } + const slotIndex = Math.max(0, laneAllies.findIndex(entity => entity === self)); const frontAnchorX = resolveFormationTargetX(model.fac, HType.Melee); - const targetX = frontAnchorX - forwardDir * (roleDepth + slotIndex) * this.allySpacingX; + const targetX = frontAnchorX - forwardDir * slotIndex * this.allySpacingX; return Math.max(moveMinX, Math.min(moveMaxX, targetX)); } + private moveToSlot(view: HeroViewComp, move: MoveComp, model: HeroAttrsComp, targetX: number) { + const currentX = view.node.position.x; + if (Math.abs(currentX - targetX) <= 2) { + view.node.setPosition(targetX, move.baseY, 0); + view.status_change("idle"); + return; + } + const dir = targetX > currentX ? 1 : -1; + move.direction = dir; + const speed = model.speed / 3; + this.moveEntity(view, dir, speed, targetX); + } + private moveEntity(view: HeroViewComp, direction: number, speed: number, stopAtX?: number) { const model = view.ent.get(HeroAttrsComp); const move = view.ent.get(MoveComp); @@ -330,13 +281,6 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate /** 指定停止点时,限制不越过 stopAtX */ newX = direction > 0 ? Math.min(newX, stopAtX) : Math.max(newX, stopAtX); } - /** 结合同排友军占位,做“让位/防重叠”裁剪 */ - newX = this.clampXByAllies(view.ent, model.fac, move.baseY, currentX, newX, direction); - if (direction > 0) { - newX = Math.max(currentX, newX); - } else { - newX = Math.min(currentX, newX); - } if (Math.abs(newX - currentX) < 0.01) { view.status_change("idle"); return; @@ -345,100 +289,6 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate view.status_change("move"); } - private clampXByAllies(self: ecs.Entity, fac: number, baseY: number, currentX: number, proposedX: number, direction: number): number { - const selfAttrs = self.get(HeroAttrsComp); - const selfMove = self.get(MoveComp); - const selfPriority = selfAttrs ? this.getCombatPriority(selfAttrs) : 0; - let clampedX = proposedX; - ecs.query(this.getHeroMoveMatcher()).forEach(e => { - if (e === self) return; - const attrs = e.get(HeroAttrsComp); - const view = e.get(HeroViewComp); - const allyMove = e.get(MoveComp); - /** 只处理同阵营且同排(Y 接近)的友军碰撞约束 */ - if (!attrs || !view?.node || attrs.is_dead) return; - if (attrs.fac !== fac) return; - if (Math.abs(view.node.position.y - baseY) >= this.minSpacingY) return; - const allyPriority = this.getCombatPriority(attrs); - const facForwardDir = fac === FacSet.MON ? -1 : 1; - const isRetreating = direction !== facForwardDir; - if (isRetreating && selfPriority < allyPriority) return; - if (allyPriority < selfPriority) return; - const x = view.node.position.x; - /** 近战同优先级在满足条件时可超车,不做阻挡 */ - if (this.shouldAllowMeleeOvertake(selfAttrs, selfMove, attrs, allyMove, currentX, x, direction, allyPriority, selfPriority)) return; - const spacing = this.resolveAllySpacing(selfAttrs, selfMove, currentX, direction, allyMove, x, allyPriority, selfPriority); - if (direction > 0 && x > currentX) { - clampedX = Math.min(clampedX, x - spacing); - } - if (direction < 0 && x < currentX) { - clampedX = Math.max(clampedX, x + spacing); - } - }); - return clampedX; - } - - private shouldAllowMeleeOvertake( - selfAttrs: HeroAttrsComp | null, - selfMove: MoveComp | null, - allyAttrs: HeroAttrsComp, - allyMove: MoveComp | null, - currentX: number, - allyX: number, - direction: number, - allyPriority: number, - selfPriority: number - ): boolean { - if (!selfAttrs || !selfMove || !allyMove) return false; - /** 仅近战对近战、且同优先级才进入超车判定 */ - if ((selfAttrs.type as HType) !== HType.Melee || (allyAttrs.type as HType) !== HType.Melee) return false; - if (allyPriority !== selfPriority) return false; - /** 我方更快,且双方都在前压且友军尚未到可攻击位,允许穿插 */ - if (selfAttrs.speed <= allyAttrs.speed + this.meleeOvertakeSpeedGap) return false; - if (direction > 0 && allyX <= currentX) return false; - if (direction < 0 && allyX >= currentX) return false; - const selfTargetX = selfMove.targetX; - const allyTargetX = allyMove.targetX; - if (Math.abs(selfTargetX) <= 0.01 || Math.abs(allyTargetX) <= 0.01) return false; - const selfNeedAdvance = direction > 0 ? selfTargetX > currentX + 2 : selfTargetX < currentX - 2; - if (!selfNeedAdvance) return false; - const allyCanAttackNow = allyAttrs.enemy_in_cast_range || Math.abs(allyTargetX - allyX) <= this.attackReadyLockX; - if (allyCanAttackNow) return false; - const allyStillAdvancing = direction > 0 ? allyTargetX > allyX + 2 : allyTargetX < allyX - 2; - if (!allyStillAdvancing) return false; - return true; - } - - private resolveAllySpacing( - selfAttrs: HeroAttrsComp | null, - selfMove: MoveComp | null, - currentX: number, - direction: number, - allyMove: MoveComp | null, - allyX: number, - allyPriority: number, - selfPriority: number - ): number { - /** 默认保持标准间距,仅在“需要抢位输出”时放宽 */ - if (!selfAttrs || !selfMove || !allyMove) return this.allySpacingX; - if ((selfAttrs.type as HType) !== HType.Melee) return this.allySpacingX; - if (allyPriority !== selfPriority) return this.allySpacingX; - const selfTargetX = selfMove.targetX; - const allyTargetX = allyMove.targetX; - const selfHasTarget = Math.abs(selfTargetX) > 0.01; - const allyHasTarget = Math.abs(allyTargetX) > 0.01; - if (!selfHasTarget || !allyHasTarget) return this.allySpacingX; - const selfDistToAttack = Math.abs(selfTargetX - currentX); - const canAttackNow = selfAttrs.enemy_in_cast_range || selfDistToAttack <= this.attackReadyLockX; - if (canAttackNow) return this.allySpacingX; - const targetTooFar = selfDistToAttack >= this.attackPassThresholdX; - if (!targetTooFar) return this.allySpacingX; - const allyDisplaced = Math.abs(allyX - allyTargetX) >= this.displacementReleaseX; - const selfNeedAdvance = direction > 0 ? selfTargetX > currentX + 2 : selfTargetX < currentX - 2; - if (allyDisplaced && selfNeedAdvance) return this.allyOverlapSpacingX; - return this.allySpacingX; - } - private getCombatPriority(model: HeroAttrsComp): number { /** 数值越大越靠前:近战 > 中程 > 远程 */ const rangeType = model.type as HType.Melee | HType.Mid | HType.Long; @@ -447,19 +297,18 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate return 1; } - private hasAnyActorTooClose(self: ecs.Entity, x: number, y: number): boolean { - const myAttrs = self.get(HeroAttrsComp); - if (!myAttrs) return false; - return ecs.query(this.getHeroViewMatcher()).some(e => { - if (e === self) return false; - const attrs = e.get(HeroAttrsComp); - if (!attrs || attrs.is_dead) return false; - if (attrs.fac !== myAttrs.fac) return false; - const view = e.get(HeroViewComp); - if (!view || !view.node) return false; - return Math.abs(view.node.position.x - x) < this.allySpacingX - && Math.abs(view.node.position.y - y) < this.minSpacingY; - }); + private isFormationParticipant(entity: ecs.Entity, fac: number, baseY: number): boolean { + const attrs = entity.get(HeroAttrsComp); + const view = entity.get(HeroViewComp); + const move = entity.get(MoveComp); + if (!attrs || !view?.node || !move) return false; + if (attrs.is_dead || attrs.is_reviving) return false; + if (attrs.fac !== fac) return false; + if (!move.moving) return false; + if (Math.abs(view.node.position.y - baseY) >= this.minSpacingY) return false; + const collider = view.node.getComponent(BoxCollider2D); + if (collider && !collider.enabled) return false; + return true; } private resolveCombatRange(model: HeroAttrsComp, defaultMin: number, defaultMax: number): [number, number] {