feat(移动系统): 实现多线站位和防重叠机制

- 将移动边界配置从 min/max 改为 front/back 以支持双向移动逻辑
- 增加四条站位线(LINE1~LINE4)实现纵向分层站位
- 添加防重叠检测,防止同阵营单位位置冲突
- 调整游戏地平线高度和地图元素位置以适配新站位系统
- 禁用地图中多余的游戏对象以优化性能
This commit is contained in:
walkpan
2026-03-13 16:04:53 +08:00
parent b12b421823
commit 8d4ebcfdd8
3 changed files with 80 additions and 24 deletions

View File

@@ -584,7 +584,7 @@
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 0, "x": 0,
"y": -295.746, "y": -245.746,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -856,7 +856,7 @@
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 0, "x": 0,
"y": -278.047, "y": -228.047,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -992,7 +992,7 @@
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 0, "x": 0,
"y": -282.963, "y": -232.963,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -1128,7 +1128,7 @@
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 0, "x": 0,
"y": -580.208, "y": -530.208,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -1264,7 +1264,7 @@
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 0, "x": 0,
"y": -320, "y": -270,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@@ -1433,7 +1433,7 @@
"__id__": 62 "__id__": 62
} }
], ],
"_active": true, "_active": false,
"_components": [ "_components": [
{ {
"__id__": 70 "__id__": 70

View File

@@ -18,7 +18,7 @@ export enum BoxSet {
LETF_END = -420, LETF_END = -420,
RIGHT_END = 420, RIGHT_END = 420,
//游戏地平线 //游戏地平线
GAME_LINE = -215, GAME_LINE = -165
//攻击距离 //攻击距离
} }

View File

@@ -28,26 +28,35 @@ export class MoveComp extends ecs.Comp {
} }
interface MoveFacConfig { interface MoveFacConfig {
moveMinX: number; moveFrontX: number;
moveMaxX: number; moveBackX: number;
retreatMinX: number; retreatFrontX: number;
retreatMaxX: number; retreatBackX: number;
} }
@ecs.register('MoveSystem') @ecs.register('MoveSystem')
export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
private readonly ySlots = [
{ offset: 0, lineName: "LINE2" },
{ offset: 5, lineName: "LINE1" },
{ offset: -5, lineName: "LINE3" },
{ offset: -10, lineName: "LINE4" },
];
private readonly samePointXThreshold = 12;
private readonly samePointYThreshold = 3;
private readonly facConfigs: Record<number, MoveFacConfig> = { private readonly facConfigs: Record<number, MoveFacConfig> = {
[FacSet.HERO]: { [FacSet.HERO]: {
moveMinX: -320, moveFrontX: 320,
moveMaxX: 320, moveBackX: -320,
retreatMinX: -300, retreatFrontX: 300,
retreatMaxX: 300, retreatBackX: -300,
}, },
[FacSet.MON]: { [FacSet.MON]: {
moveMinX: -320, moveFrontX: -320,
moveMaxX: 320, moveBackX: 320,
retreatMinX: -300, retreatFrontX: -300,
retreatMaxX: 300, retreatBackX: 300,
} }
}; };
@@ -72,6 +81,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
return; return;
} }
this.applyLineAndY(e, view, move, view.node.position.x);
this.updateRenderOrder(e); this.updateRenderOrder(e);
const nearestEnemy = this.findNearestEnemy(e); const nearestEnemy = this.findNearestEnemy(e);
if (nearestEnemy) { if (nearestEnemy) {
@@ -177,8 +187,10 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
private performRetreat(view: HeroViewComp, move: MoveComp, model: HeroAttrsComp, currentX: number) { private performRetreat(view: HeroViewComp, move: MoveComp, model: HeroAttrsComp, currentX: number) {
const cfg = this.facConfigs[model.fac] || this.facConfigs[FacSet.HERO]; 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; const safeRetreatX = currentX - move.direction * 50;
if (safeRetreatX >= cfg.retreatMinX && safeRetreatX <= cfg.retreatMaxX) { if (safeRetreatX >= retreatMinX && safeRetreatX <= retreatMaxX) {
const retreatSpeed = (model.speed / 3) * 0.8; const retreatSpeed = (model.speed / 3) * 0.8;
this.moveEntity(view, -move.direction, retreatSpeed); this.moveEntity(view, -move.direction, retreatSpeed);
model.is_atking = false; model.is_atking = false;
@@ -233,23 +245,67 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const move = view.ent.get(MoveComp); const move = view.ent.get(MoveComp);
if (!model || !move) return; if (!model || !move) return;
const cfg = this.facConfigs[model.fac] || this.facConfigs[FacSet.HERO]; const cfg = this.facConfigs[model.fac] || this.facConfigs[FacSet.HERO];
const moveMinX = Math.min(cfg.moveBackX, cfg.moveFrontX);
const moveMaxX = Math.max(cfg.moveBackX, cfg.moveFrontX);
const currentX = view.node.position.x; const currentX = view.node.position.x;
const delta = speed * this.dt * direction; const delta = speed * this.dt * direction;
let newX = view.node.position.x + delta; let newX = view.node.position.x + delta;
if (currentX < cfg.moveMinX && direction < 0) { if (currentX < moveMinX && direction < 0) {
view.status_change("idle"); view.status_change("idle");
return; return;
} }
if (currentX > cfg.moveMaxX && direction > 0) { if (currentX > moveMaxX && direction > 0) {
view.status_change("idle"); view.status_change("idle");
return; return;
} }
newX = Math.max(cfg.moveMinX, Math.min(cfg.moveMaxX, newX)); newX = Math.max(moveMinX, Math.min(moveMaxX, newX));
const newY = view.node.position.y; const newY = this.applyLineAndY(view.ent, view, move, newX);
view.node.setPosition(newX, newY, 0); view.node.setPosition(newX, newY, 0);
view.status_change("move"); view.status_change("move");
} }
private applyLineAndY(entity: ecs.Entity, view: HeroViewComp, move: MoveComp, x: number): number {
if (!view.node) return 0;
const baseY = move.baseY;
for (const slot of this.ySlots) {
const y = baseY + slot.offset;
if (!this.hasTeammateAtPoint(entity, x, y)) {
const lineNode = this.getLineNode(slot.lineName);
if (lineNode && view.node.parent !== lineNode) {
view.node.parent = lineNode;
}
return y;
}
}
const fallback = this.ySlots[0];
const fallbackNode = this.getLineNode(fallback.lineName);
if (fallbackNode && view.node.parent !== fallbackNode) {
view.node.parent = fallbackNode;
}
return baseY + fallback.offset;
}
private getLineNode(lineName: string) {
const scene = smc.map?.MapView?.scene;
const layerRoot = scene?.entityLayer?.node;
if (!layerRoot) return null;
return layerRoot.getChildByName(lineName);
}
private hasTeammateAtPoint(self: ecs.Entity, x: number, y: number): boolean {
const myAttrs = self.get(HeroAttrsComp);
if (!myAttrs) return false;
return ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
if (e === self) return false;
const attrs = e.get(HeroAttrsComp);
if (!attrs || attrs.fac !== myAttrs.fac || attrs.is_dead) return false;
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
return Math.abs(view.node.position.x - x) <= this.samePointXThreshold
&& Math.abs(view.node.position.y - y) <= this.samePointYThreshold;
});
}
private resolveCombatRange(model: HeroAttrsComp, defaultMin: number, defaultMax: number): [number, number] { private resolveCombatRange(model: HeroAttrsComp, defaultMin: number, defaultMax: number): [number, number] {
const minRange = model.getCachedMinSkillDistance(); const minRange = model.getCachedMinSkillDistance();
const maxRange = model.getCachedMaxSkillDistance(); const maxRange = model.getCachedMaxSkillDistance();