import { Node, Vec3 } from "cc"; import { Timer } from "../../../../../../extensions/oops-plugin-framework/assets/core/common/timer/Timer"; import { Vec3Util } from "../../../../../../extensions/oops-plugin-framework/assets/core/utils/Vec3Util"; import { ecs } from "../../../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; /** 向目标移动,移动过程中目标位置变化会自动修正移动目标点,直到未修正前移动到目标点停止 */ @ecs.register('MoveToPath') export class MoveToPathComp extends ecs.Comp { /** 移动节点 */ node: Node = null!; /** 移动速度(每秒移动的像素距离) */ speed: number = 0; /** 移动方向 */ velocity: Vec3 = Vec3Util.zero; /** 路径数组 */ paths: Vec3[] | null = null; /** 坐标标(默认本地坐标) */ ns: number = Node.NodeSpace.LOCAL; /** 移动完成回调 */ onComplete: Function | null = null; /** 距离变化时 */ onChange: Function | null = null; reset() { this.ns = Node.NodeSpace.LOCAL; this.onComplete = null; this.onChange = null; } } @ecs.register('VariableMoveToPath') class VariableMoveToPathComponent extends ecs.Comp { /** 延时触发器 */ timer: Timer = new Timer(); /** 距离 */ distance: number = 0; /** 起点备份 */ start: Vec3 | null = null; /** 终点备份 */ end: Vec3 | null = null; /** 目标位置 */ target!: Vec3; isComplete: boolean = false reset() { this.end = null; this.isComplete = false this.timer.reset(); } } /** 按照路径移动 */ export class MoveToPathSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem, ecs.ISystemUpdate { filter(): ecs.IMatcher { return ecs.allOf(MoveToPathComp); } entityEnter(e: ecs.Entity): void { e.add(VariableMoveToPathComponent); } entityRemove(e: ecs.Entity): void { e.remove(VariableMoveToPathComponent); } update(e: ecs.Entity) { let move = e.get(MoveToPathComp); let mtv = e.get(VariableMoveToPathComponent); // console.assert(move.speed > 0, "移动速度必须要大于零"); // 如果没有路径点或者已经完成当前路径点的移动,则获取下一个路径点 if ((move.paths && move.paths.length > 0)) { if ((mtv.end == null || mtv.isComplete)) { mtv.reset() let end = move.paths.shift(); this.prepareMovement(e, move, mtv, end); } } else if (mtv.end == null || mtv.isComplete) { this.exit(e) } // 执行移动 if (move.speed > 0 && mtv.end != null && !mtv.isComplete) { let trans = Vec3Util.mul(move.velocity, move.speed * this.dt); let start = move.ns == Node.NodeSpace.WORLD ? move.node.worldPosition : move.node.position; let end = Vec3Util.add(start, trans) if (Vec3Util.sub(end, mtv.start).length() > mtv.distance||mtv.timer.update(this.dt)) { this.finishMovement(e, move, mtv); mtv.isComplete = true } else { move.node.translate(trans, move.ns); move.node.forward = trans } } } private prepareMovement(e: ecs.Entity, move: MoveToPathComp, mtv: VariableMoveToPathComponent, end: Vec3) { let target = end.clone(); let start = move.ns == Node.NodeSpace.WORLD ? move.node.worldPosition : move.node.position; move.velocity = Vec3Util.sub(target, start).normalize(); let distance = Vec3.distance(start, target); move.onChange?.call(this); if (distance <= 0) { // this.exit(e); mtv.isComplete = true } else { mtv.timer.step = distance / move.speed; mtv.end = end.clone(); mtv.target = target; mtv.start = start.clone() mtv.distance = Vec3.distance(mtv.end, mtv.start); } } private finishMovement(e: ecs.Entity, move: MoveToPathComp, mtv: VariableMoveToPathComponent) { if (move.ns == Node.NodeSpace.WORLD) move.node.worldPosition = mtv.target; else move.node.position = mtv.target; // this.exit(e); } private exit(e: ecs.Entity) { let move = e.get(MoveToPathComp); move.onComplete?.call(this); e.remove(VariableMoveToPathComponent); e.remove(MoveToPathComp); } }