Files
heros/assets/script/game/hero/HeroMove.ts

278 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 { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroViewComp } from "./HeroViewComp";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { smc } from "../common/SingletonModuleComp";
import { FacSet } from "../common/config/GameSet";
import { HType } from "../common/config/heroSet";
import { Attrs } from "../common/config/HeroAttrs";
/** 英雄移动组件 */
@ecs.register('HeroMove')
export class HeroMoveComp extends ecs.Comp {
/** 移动方向1向右-1向左 */
direction: number = 1;
/** 目标x坐标 */
targetX: number = 0;
/** 是否处于移动状态 */
moving: boolean = true;
reset() {
this.direction = 1;
this.targetX = 0;
this.moving = true;
}
}
/** 英雄移动系统 - 专门处理英雄的移动逻辑 */
@ecs.register('HeroMoveSystem')
export class HeroMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(HeroMoveComp, HeroViewComp, HeroAttrsComp);
}
update(e: ecs.Entity) {
if (!smc.mission.play || smc.mission.pause) return;
const move = e.get(HeroMoveComp);
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
// 只处理英雄
if (model.fac !== FacSet.HERO) return;
if (!move.moving) return;
const shouldStop = this.checkEnemiesInFace(e);
model.is_atking = this.checkEnemiesInRange(e, model.Attrs[Attrs.DIS]);
// 更新渲染层级
this.updateRenderOrder(e);
if (!shouldStop) {
if (model.is_stop || model.is_dead || model.isStun() || model.isFrost()) {
view.status_change("idle");
return;
}
// 新增墓地位置判断,如果已经在墓地则不再移动
if (view.node.position.x === -1000 || view.node.position.x === 1000) {
view.status_change("idle");
return;
}
// 英雄阵营特殊逻辑:根据职业区分行为
const hasEnemies = this.checkEnemiesExist(e);
const isWarrior = model.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 = (model.Attrs[Attrs.SPEED]/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 = (model.Attrs[Attrs.SPEED]/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); // 面向右侧
}
} else {
view.status_change("idle");
// 因为敌人在面前而暂时停止不设置moving为false保持检查状态
}
}
/** 检查是否存在敌人 */
private checkEnemiesExist(entity: ecs.Entity): boolean {
const team = entity.get(HeroAttrsComp).fac;
let hasEnemies = false;
ecs.query(ecs.allOf(HeroAttrsComp)).some(e => {
const model = e.get(HeroAttrsComp);
if (model.fac !== team && !model.is_dead) {
hasEnemies = true;
return true;
}
});
return hasEnemies;
}
/** 找到最近的敌人 */
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
const currentView = entity.get(HeroViewComp);
if (!currentView || !currentView.node) return null;
const currentPos = currentView.node.position;
const team = entity.get(HeroAttrsComp).fac;
let nearestEnemyView: HeroViewComp | null = null;
let minDistance = Infinity;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).forEach(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (model.fac !== team && !model.is_dead && view && view.node) {
const distance = Math.abs(currentPos.x - view.node.position.x);
if (distance < minDistance) {
minDistance = distance;
nearestEnemyView = view;
}
}
});
return nearestEnemyView;
}
/** 检测攻击范围内敌人 */
private checkEnemiesInRange(entity: ecs.Entity, range: number): boolean {
const currentView = entity.get(HeroViewComp);
if (!currentView || !currentView.node) return false;
const currentPos = currentView.node.position;
const team = entity.get(HeroAttrsComp).fac;
let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) {
if (distance <= range) {
found = true;
return true;
}
}
});
return found;
}
/** 检测面前是否有敌人 */
private checkEnemiesInFace(entity: ecs.Entity): boolean {
const currentView = entity.get(HeroViewComp);
if (!currentView || !currentView.node) return false;
const currentPos = currentView.node.position;
const team = entity.get(HeroAttrsComp).fac;
let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac !== team && !model.is_dead) {
if (distance <= 75) {
found = true;
return true;
}
}
});
return found;
}
/** 更新渲染层级 */
private updateRenderOrder(entity: ecs.Entity) {
const currentView = entity.get(HeroViewComp);
const currentModel = entity.get(HeroAttrsComp);
// 查找所有英雄单位
const allUnits = ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp))
.filter(e => {
const otherModel = e.get(HeroAttrsComp);
return otherModel.fac === currentModel.fac; // 按阵营分组
})
.map(e => e);
// 按x坐标排序x坐标越大越前面的显示在上层
const sortedUnits = allUnits.sort((a, b) => {
const viewA = a.get(HeroViewComp);
const viewB = b.get(HeroViewComp);
if (!viewA || !viewA.node || !viewB || !viewB.node) return 0;
const posA = viewA.node.position.x;
const posB = viewB.node.position.x;
return posA - posB; // x坐标从小到大排序
});
// 设置渲染顺序x坐标越大的显示在上层index越大层级越高
sortedUnits.forEach((unit, index) => {
const model = unit.get(HeroViewComp);
model.node.setSiblingIndex(index); // 直接使用indexx坐标大的index大层级高
});
}
/** 检查指定位置是否已被占用 */
private isPositionOccupied(targetX: number, currentEntity: ecs.Entity): boolean {
const currentModel = currentEntity.get(HeroAttrsComp);
const occupationRange = 30; // 定义占用范围为30像素
return ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
if (e === currentEntity) return false; // 排除自己
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (model.fac !== currentModel.fac) return false; // 只检查同阵营
if (model.is_dead) return false; // 排除死亡单位
const distance = Math.abs(view.node.position.x - targetX);
return distance < occupationRange; // 如果距离小于占用范围,认为被占用
});
}
}