Files
heros/assets/script/game/common/ecs/position/BattleMoveSystem.ts
2025-10-16 16:52:27 +08:00

272 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { HeroViewComp } from "../../../hero/HeroViewComp";
import { BattleMoveComp } from "./BattleMoveComp";
import { ecs } from "../../../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { smc } from "../../SingletonModuleComp";
import { FacSet } from "../../config/BoxSet";
import { HType } from "../../config/heroSet";
@ecs.register('BattleMoveSystem')
export class BattleMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(BattleMoveComp, HeroViewComp);
}
update(e: ecs.Entity) {
if(!smc.mission.play||smc.mission.pause) return
const move = e.get(BattleMoveComp);
const view = e.get(HeroViewComp);
if (!move.moving) return;
const shouldStop = this.checkEnemiesInFace(e);
view.is_atking = this.checkEnemiesInRange(e, view.dis);
// 更新渲染层级
this.updateRenderOrder(e);
// 同步状态
if (!shouldStop) { //在攻击范围内停止移动
// if(view.fac==1){
if(view.is_stop||view.is_dead||view.DEBUFF_STUN>0 ||view.DEBUFF_FROST>0) {
view.status_change("idle");
return; //停止移动或者死亡不移动
}
// 新增墓地位置判断,如果已经在墓地则不再移动
if (view.node.position.x === -1000 || view.node.position.x === 1000) {
view.status_change("idle");
return;
}
// 英雄阵营特殊逻辑:根据职业区分行为
if (view.fac == FacSet.HERO) {
const hasEnemies = this.checkEnemiesExist(e);
const isWarrior = view.type === HType.warrior;
// 战士职业:有敌人就向敌人前进
if (isWarrior && hasEnemies) {
const nearestEnemy = this.findNearestEnemy(e);
if (nearestEnemy) {
const enemyX = nearestEnemy.node.position.x;
const currentX = view.node.position.x;
// 根据敌人位置调整移动方向和朝向
if (enemyX > currentX) {
move.direction = 1; // 向右移动
view.node.setScale(1, 1, 1); // 面向右侧
view.node.getChildByName("top").setScale(1, 1, 1); // 面向右侧
} else {
move.direction = -1; // 向左移动
view.node.setScale(-1, 1, 1); // 面向左侧
view.node.getChildByName("top").setScale(-1, 1, 1); // 面向左侧
}
// 继续向敌人方向移动
const delta = ((view.speed-view.DEBUFF_SLOW)/3) * this.dt * move.direction;
const newX = view.node.position.x + delta;
// 对于战士,允许更自由的移动范围
if (newX >= -420 && newX <= 420) { // 使用地图边界
view.status_change("move");
view.node.setPosition(newX, view.node.position.y, 0);
} else {
view.status_change("idle");
}
}
return;
}
// 其他职业或战士无敌人时:回到预定点
const currentX = view.node.position.x;
let finalTargetX = move.targetX;
// 检查预定点是否已被占用
if (this.isPositionOccupied(finalTargetX, e)) {
finalTargetX = move.targetX - 50; // 往前50的位置
}
// 如果不在目标位置,移动到目标位置
if (Math.abs(currentX - finalTargetX) > 1) {
// 确定移动方向
const direction = currentX > finalTargetX ? -1 : 1;
const delta = ((view.speed-view.DEBUFF_SLOW)/3) * this.dt * direction;
const newX = view.node.position.x + delta;
// 设置朝向
if (direction === 1) {
view.node.setScale(1, 1, 1); // 面向右侧
view.node.getChildByName("top").setScale(1, 1, 1); // 面向右侧
} else {
view.node.setScale(-1, 1, 1); // 面向左侧
view.node.getChildByName("top").setScale(-1, 1, 1); // 面向左侧
}
// 确保不会超过目标位置
if (direction === 1 && newX > finalTargetX) {
view.node.setPosition(finalTargetX, view.node.position.y, 0);
} else if (direction === -1 && newX < finalTargetX) {
view.node.setPosition(finalTargetX, view.node.position.y, 0);
} else {
view.node.setPosition(newX, view.node.position.y, 0);
}
view.status_change("move");
} else {
view.status_change("idle");
// 到达目标位置后,面向右侧(敌人方向)
move.direction = 1;
view.node.setScale(1, 1, 1); // 面向右侧
view.node.getChildByName("top").setScale(1, 1, 1); // 面向右侧
}
return;
}
// 计算移动量
const delta = ((view.speed-view.DEBUFF_SLOW)/3) * this.dt * move.direction;
const newX = view.node.position.x + delta;
// 限制移动范围
if (this.validatePosition(newX, move)) {
view.status_change("move");
view.node.setPosition(newX, view.node.position.y, 0);
} else {
// 当达到目标位置边界时也切换为idle状态
view.status_change("idle");
// 达到边界是永久停止设置moving为false
move.moving = false;
}
}
else{
view.status_change("idle");
// 因为敌人在面前而暂时停止不设置moving为false保持检查状态
}
// console.log(`[${view.hero_name}] 类型:${view.type} 是否停止:${shouldStop} 方向:${move.direction} 位置:${view.node.position.x.toFixed(1)}`);
}
/** 检查是否存在敌人 */
private checkEnemiesExist(entity: ecs.Entity): boolean {
const team = entity.get(HeroViewComp).fac;
let hasEnemies = false;
ecs.query(ecs.allOf(HeroViewComp)).some(e => {
const view = e.get(HeroViewComp);
if (view.fac !== team && !view.is_dead) {
hasEnemies = true;
return true;
}
});
return hasEnemies;
}
/** 找到最近的敌人 */
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
const currentPos = entity.get(HeroViewComp).node.position;
const team = entity.get(HeroViewComp).fac;
let nearestEnemy: HeroViewComp | null = null;
let minDistance = Infinity;
ecs.query(ecs.allOf(HeroViewComp)).forEach(e => {
const view = e.get(HeroViewComp);
if (view.fac !== team && !view.is_dead) {
const distance = Math.abs(currentPos.x - view.node.position.x);
if (distance < minDistance) {
minDistance = distance;
nearestEnemy = view;
}
}
});
return nearestEnemy;
}
/** 验证目标位置有效性 */
private validatePosition(newX: number, move: BattleMoveComp): boolean {
// 我方不能超过右边界,敌方不能超过左边界
return move.direction === 1 ?
newX <= move.targetX :
newX >= move.targetX;
}
/** 检测是否在墓地 */
private checkInGrave(entity: ecs.Entity): boolean {
const view = entity.get(HeroViewComp);
return view.node.position.x === -1000 || view.node.position.x === 1000;
}
/** 检测攻击范围内敌人 */
private checkEnemiesInRange(entity: ecs.Entity, range: number): boolean {
const currentPos = entity.get(HeroViewComp).node.position;
const team = entity.get(HeroViewComp).fac;
let found = false;
ecs.query(ecs.allOf(HeroViewComp)).some(e => {
const view = e.get(HeroViewComp);
const distance = Math.abs(currentPos.x - view.node.position.x);
if (view.fac !== team) {
if (distance <= range) {
found = true;
return true;
}
}
});
return found;
}
private checkEnemiesInFace(entity: ecs.Entity): boolean {
const currentPos = entity.get(HeroViewComp).node.position;
const team = entity.get(HeroViewComp).fac;
let found = false;
ecs.query(ecs.allOf(HeroViewComp)).some(e => {
const view = e.get(HeroViewComp);
const distance = Math.abs(currentPos.x - view.node.position.x);
if (view.fac !== team) {
if (distance <= 75) {
found = true;
return true;
}
}
});
return found;
}
/** 更新渲染层级 */
private updateRenderOrder(entity: ecs.Entity) {
const current = entity.get(HeroViewComp);
// 查找所有单位
const allUnits = ecs.query(ecs.allOf(HeroViewComp))
.filter(e => {
const other = e.get(HeroViewComp);
return other.fac === current.fac; // 按阵营分组
})
.map(e => e);
// 按x坐标排序x坐标越大越前面的显示在上层
const sortedUnits = allUnits.sort((a, b) => {
const posA = a.get(HeroViewComp).node.position.x;
const posB = b.get(HeroViewComp).node.position.x;
return posA - posB; // x坐标从小到大排序
});
// 设置渲染顺序x坐标越大的显示在上层index越大层级越高
sortedUnits.forEach((unit, index) => {
const view = unit.get(HeroViewComp);
view.node.setSiblingIndex(index); // 直接使用indexx坐标大的index大层级高
});
}
/** 检查指定位置是否已被占用 */
private isPositionOccupied(targetX: number, currentEntity: ecs.Entity): boolean {
const currentView = currentEntity.get(HeroViewComp);
const occupationRange = 30; // 定义占用范围为30像素
return ecs.query(ecs.allOf(HeroViewComp)).some(e => {
if (e === currentEntity) return false; // 排除自己
const view = e.get(HeroViewComp);
if (view.fac !== currentView.fac) return false; // 只检查同阵营
if (view.is_dead) return false; // 排除死亡单位
const distance = Math.abs(view.node.position.x - targetX);
return distance < occupationRange; // 如果距离小于占用范围,认为被占用
});
}
}