fix(移动系统): 修正近战攻击距离和友军间距逻辑

- 将 `minSpacingX` 拆分为 `meleeAttackRange` 和 `allySpacingX`,明确区分攻击范围和友军间距
- 在 `moveEntity` 方法中添加 `stopAtX` 参数,确保英雄在攻击范围内停止移动
- 新增 `clampXByAllies` 方法,防止友军单位在移动时相互重叠
- 更新 `hasAnyActorTooClose` 方法使用新的 `allySpacingX` 常量
This commit is contained in:
panw
2026-03-16 14:49:17 +08:00
parent 662ae9a6c9
commit 4171865efb

View File

@@ -37,7 +37,8 @@ interface MoveFacConfig {
@ecs.register('MoveSystem')
export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
private readonly minSpacingX = 50;
private readonly meleeAttackRange = 52;
private readonly allySpacingX = 40;
private readonly minSpacingY = 30;
private readonly renderSortInterval = 0.05;
private renderSortElapsed = 0;
@@ -124,7 +125,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const currentX = view.node.position.x;
const enemyX = enemy.node.position.x;
const dist = Math.abs(currentX - enemyX);
const maxRange = this.minSpacingX;
const maxRange = this.meleeAttackRange;
move.direction = enemyX > currentX ? 1 : -1;
@@ -133,7 +134,8 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
model.is_atking = true;
} else {
const speed = model.speed / 3;
this.moveEntity(view, move.direction, speed);
const stopAtX = enemyX - move.direction * maxRange;
this.moveEntity(view, move.direction, speed, stopAtX);
model.is_atking = true;
}
}
@@ -233,7 +235,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
return 0;
}
private moveEntity(view: HeroViewComp, direction: number, speed: number) {
private moveEntity(view: HeroViewComp, direction: number, speed: number, stopAtX?: number) {
const model = view.ent.get(HeroAttrsComp);
const move = view.ent.get(MoveComp);
if (!model || !move) return;
@@ -252,10 +254,42 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
return;
}
newX = Math.max(moveMinX, Math.min(moveMaxX, newX));
if (stopAtX !== undefined) {
newX = direction > 0 ? Math.min(newX, stopAtX) : Math.max(newX, stopAtX);
}
newX = this.clampXByAllies(view.ent, model.fac, move.baseY, currentX, newX, direction);
if (Math.abs(newX - currentX) < 0.01) {
view.status_change("idle");
return;
}
view.node.setPosition(newX, move.baseY, 0);
view.status_change("move");
}
private clampXByAllies(self: ecs.Entity, fac: number, baseY: number, currentX: number, proposedX: number, direction: number): number {
let nearestAheadX = Infinity;
let nearestBehindX = -Infinity;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp, MoveComp)).forEach(e => {
if (e === self) return;
const attrs = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
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 x = view.node.position.x;
if (x > currentX && x < nearestAheadX) nearestAheadX = x;
if (x < currentX && x > nearestBehindX) nearestBehindX = x;
});
if (direction > 0 && nearestAheadX !== Infinity) {
return Math.min(proposedX, nearestAheadX - this.allySpacingX);
}
if (direction < 0 && nearestBehindX !== -Infinity) {
return Math.max(proposedX, nearestBehindX + this.allySpacingX);
}
return proposedX;
}
private hasAnyActorTooClose(self: ecs.Entity, x: number, y: number): boolean {
const myAttrs = self.get(HeroAttrsComp);
if (!myAttrs) return false;
@@ -266,7 +300,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
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.minSpacingX
return Math.abs(view.node.position.x - x) < this.allySpacingX
&& Math.abs(view.node.position.y - y) < this.minSpacingY;
});
}