@cc-component/cc-pathfinding 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -4,7 +4,7 @@ import { AttackDistance } from "./base/Common";
|
|
|
4
4
|
import { MapBoxView } from "./base/MapBoxView";
|
|
5
5
|
import { Component } from "cc";
|
|
6
6
|
import { Node } from "cc";
|
|
7
|
-
import { GridTweenFactory } from "./base/GridMovementController";
|
|
7
|
+
import { DepthSortOptions, GridTweenFactory } from "./base/GridMovementController";
|
|
8
8
|
|
|
9
9
|
declare module "cc" {
|
|
10
10
|
|
|
@@ -57,7 +57,14 @@ declare global {
|
|
|
57
57
|
|
|
58
58
|
//#region 移动控制
|
|
59
59
|
/** 内部创建并绑定移动控制器 */
|
|
60
|
-
export function InitMovementController(scheduler: Component, options?: { retryDelay?: number; moveDuration?: number; tweenFactory?: GridTweenFactory }): void
|
|
60
|
+
export function InitMovementController(scheduler: Component, options?: { retryDelay?: number; moveDuration?: number; tweenFactory?: GridTweenFactory; depthSort?: boolean }): void
|
|
61
|
+
/**
|
|
62
|
+
* 开启 / 关闭深度排序
|
|
63
|
+
* @param setting 不传 → 内置插入式排序(默认按世界坐标 y,越小越靠前);
|
|
64
|
+
* 传 (node) => number → 内置排序 + 自定义排序键;
|
|
65
|
+
* 传 { handler: (nodes) => void } → 完全交给外部处理层级(一帧只回调一次)
|
|
66
|
+
*/
|
|
67
|
+
export function SetDepthSort(enabled: boolean, setting?: ((node: Node) => number) | DepthSortOptions): void
|
|
61
68
|
/**
|
|
62
69
|
* 注入自定义 tween 实现(比如换成项目自己的动画系统)
|
|
63
70
|
* @param factory 传 null 或从未调用时,自动使用 cc 自带 tween
|
|
@@ -78,6 +85,19 @@ declare global {
|
|
|
78
85
|
export function SetMoveDuration(playerNode: Node, moveDuration: number): void
|
|
79
86
|
/** 获取单位当前的单格移动时长(秒) */
|
|
80
87
|
export function GetMoveDuration(playerNode: Node): number
|
|
88
|
+
/**
|
|
89
|
+
* 监听移动过程中的位置更新:tween 每更新一次节点坐标就回调一次,
|
|
90
|
+
* 适合外部在这里刷新节点层级(按 y 做深度排序)
|
|
91
|
+
* @param handler (playerNode, worldPosition),worldPosition 是复用的临时向量,仅在回调内有效;
|
|
92
|
+
* 传 null 取消监听
|
|
93
|
+
*/
|
|
94
|
+
export function SetMovementUpdateHandler(handler: ((playerNode: Node, worldPosition: Vec3) => void) | null): void
|
|
95
|
+
/**
|
|
96
|
+
* 批量监听移动中的位置更新(多个单位时推荐):无论多少个单位在移动,每帧只回调一次,
|
|
97
|
+
* 参数是本帧坐标发生变化的单位数组
|
|
98
|
+
* @param handler 传 null 取消监听;与 SetMovementUpdateHandler 二选一
|
|
99
|
+
*/
|
|
100
|
+
export function SetMovementFrameHandler(handler: ((playerNodes: Node[]) => void) | null): void
|
|
81
101
|
/**
|
|
82
102
|
* 让单位移动到目标格子
|
|
83
103
|
* @param allowOverlap 为 true 时该单位进入"允许重叠"模式,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Node } from 'cc';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 增量深度排序器(配合移动中的位置更新回调使用)。
|
|
5
|
+
*
|
|
6
|
+
* 为什么不用全量排序:上百个单位每帧对 parent.children 做一次 sort + 逐个 setSiblingIndex,
|
|
7
|
+
* 开销是 O(N log N) 且会打乱整个 children 数组;而实际每帧真正需要挪动的往往只有少数几个节点。
|
|
8
|
+
*
|
|
9
|
+
* 这里的做法是"插入式调整":只对动过的节点,与相邻兄弟逐个比较并交换,
|
|
10
|
+
* 直到它落在正确位置。大多数帧只需要 0~1 次 setSiblingIndex,
|
|
11
|
+
* 单次开销 O(移动的位数),与单位总数无关。
|
|
12
|
+
*/
|
|
13
|
+
export class DepthSorter {
|
|
14
|
+
|
|
15
|
+
/** 默认排序键:y 越小(画面越靠下)越靠前显示 */
|
|
16
|
+
static readonly DEFAULT_ORDER = (node: Node): number => -node.worldPosition.y;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param getOrder 排序键:键越大越靠前显示(siblingIndex 越大,越后绘制、盖在上面)。
|
|
20
|
+
* 默认按世界坐标 y:(node) => -node.worldPosition.y
|
|
21
|
+
*/
|
|
22
|
+
constructor(private readonly getOrder: (node: Node) => number = DepthSorter.DEFAULT_ORDER) { }
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 把节点调整到正确的层级位置。位置已经正确时不做任何操作。
|
|
26
|
+
* @returns 是否发生过调整
|
|
27
|
+
*/
|
|
28
|
+
update(node: Node): boolean {
|
|
29
|
+
if (!node?.isValid) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const parent = node.parent;
|
|
33
|
+
if (!parent) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const order = this.getOrder(node);
|
|
38
|
+
const siblings = parent.children;
|
|
39
|
+
let index = node.getSiblingIndex();
|
|
40
|
+
const startIndex = index;
|
|
41
|
+
|
|
42
|
+
// 向前挪:前面的兄弟比自己更"靠前",就往后再换(index 变大 = 后绘制 = 盖在上面)
|
|
43
|
+
while (index < siblings.length - 1) {
|
|
44
|
+
const nextSibling = siblings[index + 1];
|
|
45
|
+
if (nextSibling === node || this.getOrder(nextSibling) >= order) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
node.setSiblingIndex(index + 1);
|
|
49
|
+
index++;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 向后挪:后面的兄弟比自己更"靠后",就往前换
|
|
53
|
+
while (index > 0) {
|
|
54
|
+
const prevSibling = siblings[index - 1];
|
|
55
|
+
if (prevSibling === node || this.getOrder(prevSibling) <= order) {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
node.setSiblingIndex(index - 1);
|
|
59
|
+
index--;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return index !== startIndex;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 批量调整:只对传入的(脏)节点做插入式调整 */
|
|
66
|
+
updateAll(nodes: Node[]): void {
|
|
67
|
+
for (const node of nodes) {
|
|
68
|
+
this.update(node);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { Component, Node, Tween, tween, Vec3 } from 'cc';
|
|
2
2
|
import { AttackDistance } from './Common';
|
|
3
|
+
import { DepthSorter } from './DepthSorter';
|
|
3
4
|
import { Cell } from './GridMap';
|
|
4
5
|
import { PathGridStateManager } from './PathGridStateManager';
|
|
5
6
|
import { PathfindingModule } from './PathfindingModule';
|
|
6
7
|
|
|
8
|
+
/** to() 的可选项(与 cc 的 ITweenOption 用法一致) */
|
|
9
|
+
export interface GridTweenOption {
|
|
10
|
+
/** 每帧回调:tween 更新完目标属性后触发 */
|
|
11
|
+
onUpdate?: (target: Node) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
/** 外部 tween 对象需要满足的最小接口(与 cc.Tween 用法一致) */
|
|
8
15
|
export interface GridTween {
|
|
9
|
-
to(duration: number, props: object): GridTween;
|
|
16
|
+
to(duration: number, props: object, opts?: GridTweenOption): GridTween;
|
|
10
17
|
call(callback: () => void): GridTween;
|
|
11
18
|
start(): GridTween;
|
|
12
19
|
}
|
|
@@ -27,6 +34,35 @@ export interface GridTweenFactory {
|
|
|
27
34
|
stopAllByTarget(target: Node): void;
|
|
28
35
|
}
|
|
29
36
|
|
|
37
|
+
/**
|
|
38
|
+
* 移动过程中"位置更新"的回调。tween 每更新一次节点坐标就触发一次,
|
|
39
|
+
* 常用于外部刷新节点层级(按 y 做深度排序,越靠下越靠前显示)。
|
|
40
|
+
* @param worldPosition 复用同一个临时向量,只在本次回调内有效,不要保存引用
|
|
41
|
+
*/
|
|
42
|
+
export type MovementUpdateHandler = (playerNode: Node, worldPosition: Vec3) => void;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 批量监听:多个单位同时移动时,每帧只回调一次,参数是"本帧坐标发生变化"的单位。
|
|
46
|
+
* 相比 MovementUpdateHandler(每个单位每帧各回调一次),把 N 次调用合并成 1 次,
|
|
47
|
+
* 排序等重活每帧只做一遍。单位越多收益越大。
|
|
48
|
+
*/
|
|
49
|
+
export type MovementFrameHandler = (playerNodes: Node[]) => void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 深度排序配置。两种用法二选一(handler 优先):
|
|
53
|
+
* - getOrder:交给内置插入式排序,只传排序键,最省事;
|
|
54
|
+
* - handler:完全交给外部处理层级,内部只负责"脏标记 + 每帧合并成一次回调"。
|
|
55
|
+
*/
|
|
56
|
+
export interface DepthSortOptions {
|
|
57
|
+
/** 内置插入式排序的排序键:键越大越靠前显示(siblingIndex 越大) */
|
|
58
|
+
getOrder?: (node: Node) => number;
|
|
59
|
+
/** 外部处理器:每帧一次,参数是本帧坐标发生变化的单位 */
|
|
60
|
+
handler?: (nodes: Node[]) => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 直接传一个排序键函数,等价于 { getOrder } */
|
|
64
|
+
export type DepthSortSetting = ((node: Node) => number) | DepthSortOptions;
|
|
65
|
+
|
|
30
66
|
export interface GridMovementOptions {
|
|
31
67
|
/** 每次重试前的等待时间,单位秒 */
|
|
32
68
|
retryDelay?: number;
|
|
@@ -34,6 +70,11 @@ export interface GridMovementOptions {
|
|
|
34
70
|
moveDuration?: number;
|
|
35
71
|
/** 自定义 tween 实现,不传则用 cc 自带 tween */
|
|
36
72
|
tweenFactory?: GridTweenFactory;
|
|
73
|
+
/**
|
|
74
|
+
* 是否开启内置深度排序:移动中的单位每帧自动调整层级(只调整动过的节点)。
|
|
75
|
+
* 默认 false。排序键可通过 SetDepthSort 自定义。
|
|
76
|
+
*/
|
|
77
|
+
depthSort?: boolean;
|
|
37
78
|
}
|
|
38
79
|
|
|
39
80
|
/**
|
|
@@ -43,6 +84,8 @@ export interface GridMovementOptions {
|
|
|
43
84
|
export class GridMovementController {
|
|
44
85
|
/** 允许重叠的单位共用的共享令牌 */
|
|
45
86
|
private static readonly OVERLAP_TOKEN = 'overlap';
|
|
87
|
+
/** 位置更新回调里复用,避免每帧分配向量 */
|
|
88
|
+
private static readonly TMP_POSITION = new Vec3();
|
|
46
89
|
|
|
47
90
|
private readonly movementVersions = new Map<Node, number>();
|
|
48
91
|
private readonly movementOwners = new Map<Node, symbol>();
|
|
@@ -64,12 +107,26 @@ export class GridMovementController {
|
|
|
64
107
|
/** 正在播放的那一格的"重放"入口,用于改速度时立即生效 */
|
|
65
108
|
private readonly activeStepReplayers = new Map<Node, () => void>();
|
|
66
109
|
private customTweenFactory: GridTweenFactory | null;
|
|
110
|
+
/** 移动中位置更新的外部回调(每单位每帧) */
|
|
111
|
+
private movementUpdateHandler: MovementUpdateHandler | null = null;
|
|
112
|
+
/** 移动中位置更新的外部回调(每帧合并一次) */
|
|
113
|
+
private movementFrameHandler: MovementFrameHandler | null = null;
|
|
114
|
+
/** 本帧坐标发生变化的单位,合并回调后清空 */
|
|
115
|
+
private readonly dirtyNodes = new Set<Node>();
|
|
116
|
+
private frameFlushScheduled = false;
|
|
117
|
+
/** 内置深度排序器,开启后每帧自动调整动过的单位层级 */
|
|
118
|
+
private depthSorter: DepthSorter | null = null;
|
|
119
|
+
/** 外部深度排序处理器,设置后内置排序不生效 */
|
|
120
|
+
private depthSortHandler: ((nodes: Node[]) => void) | null = null;
|
|
67
121
|
|
|
68
122
|
constructor(private readonly scheduler: Component, options: GridMovementOptions = {}) {
|
|
69
123
|
// 允许外部按场景调整重试间隔和动画时长,默认值保持兼容。
|
|
70
124
|
this.retryDelay = options.retryDelay ?? 1;
|
|
71
125
|
this.moveDuration = options.moveDuration ?? 0.5;
|
|
72
126
|
this.customTweenFactory = options.tweenFactory ?? null;
|
|
127
|
+
if (options.depthSort) {
|
|
128
|
+
this.setDepthSort(true);
|
|
129
|
+
}
|
|
73
130
|
this.pathState = new PathGridStateManager({
|
|
74
131
|
isOccupied: (row, col) => PathfindingModule.IsOccupied(row, col),
|
|
75
132
|
occupy: (row, col) => PathfindingModule.OccupyNode(row, col, true),
|
|
@@ -99,6 +156,79 @@ export class GridMovementController {
|
|
|
99
156
|
this.activeStepReplayers.get(playerNode)?.();
|
|
100
157
|
}
|
|
101
158
|
|
|
159
|
+
/**
|
|
160
|
+
* 设置"移动中位置更新"回调:tween 每更新一次节点坐标就触发一次,
|
|
161
|
+
* 外部可据此刷新节点层级(深度排序)。传 null 取消监听。
|
|
162
|
+
*/
|
|
163
|
+
setMovementUpdateHandler(handler: MovementUpdateHandler | null): void {
|
|
164
|
+
this.movementUpdateHandler = handler ?? null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 设置批量监听:多个单位同时移动时每帧只回调一次(参数是本帧动过的单位)。
|
|
169
|
+
* 传 null 取消监听。与 setMovementUpdateHandler 二选一即可,不要两个都设。
|
|
170
|
+
*/
|
|
171
|
+
setMovementFrameHandler(handler: MovementFrameHandler | null): void {
|
|
172
|
+
this.movementFrameHandler = handler ?? null;
|
|
173
|
+
if (!this.movementFrameHandler) {
|
|
174
|
+
this.dirtyNodes.clear();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 开启 / 关闭深度排序。
|
|
180
|
+
* @param setting 不传 → 内置插入式排序 + 默认键(世界坐标 y,越小越靠前);
|
|
181
|
+
* 传 (node) => number → 内置排序 + 自定义键;
|
|
182
|
+
* 传 { handler } → 完全交给外部处理层级(内部只做脏标记与每帧合并)。
|
|
183
|
+
*/
|
|
184
|
+
setDepthSort(enabled: boolean, setting?: DepthSortSetting): void {
|
|
185
|
+
if (!enabled) {
|
|
186
|
+
this.depthSorter = null;
|
|
187
|
+
this.depthSortHandler = null;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (typeof setting === 'function') {
|
|
191
|
+
this.depthSorter = new DepthSorter(setting);
|
|
192
|
+
this.depthSortHandler = null;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this.depthSortHandler = setting?.handler ?? null;
|
|
196
|
+
// 有外部处理器时内置排序不生效,避免两套逻辑重复跑
|
|
197
|
+
this.depthSorter = this.depthSortHandler ? null : new DepthSorter(setting?.getOrder);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** 是否需要在 tween 上挂每帧回调(外部监听 或 深度排序) */
|
|
201
|
+
private needsFrameUpdate(): boolean {
|
|
202
|
+
return this.movementUpdateHandler !== null
|
|
203
|
+
|| this.movementFrameHandler !== null
|
|
204
|
+
|| this.depthSorter !== null
|
|
205
|
+
|| this.depthSortHandler !== null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** 脏标记:只记录"动过",真正的回调合并到下一帧统一触发一次 */
|
|
209
|
+
private markDirty(playerNode: Node): void {
|
|
210
|
+
this.dirtyNodes.add(playerNode);
|
|
211
|
+
if (this.frameFlushScheduled) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.frameFlushScheduled = true;
|
|
215
|
+
this.scheduler.scheduleOnce(this.flushMovementFrame, 0);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private readonly flushMovementFrame = (): void => {
|
|
219
|
+
this.frameFlushScheduled = false;
|
|
220
|
+
const nodes: Node[] = [];
|
|
221
|
+
this.dirtyNodes.forEach(node => nodes.push(node));
|
|
222
|
+
this.dirtyNodes.clear();
|
|
223
|
+
// 深度排序:有外部处理器就交给外部,否则用内置插入式排序
|
|
224
|
+
if (this.depthSortHandler) {
|
|
225
|
+
this.depthSortHandler(nodes);
|
|
226
|
+
} else {
|
|
227
|
+
this.depthSorter?.updateAll(nodes);
|
|
228
|
+
}
|
|
229
|
+
this.movementFrameHandler?.(nodes);
|
|
230
|
+
};
|
|
231
|
+
|
|
102
232
|
/** 获取某个单位当前实际生效的单格移动时长(秒) */
|
|
103
233
|
getMoveDuration(playerNode: Node): number {
|
|
104
234
|
return this.resolveMoveDuration(
|
|
@@ -181,6 +311,9 @@ export class GridMovementController {
|
|
|
181
311
|
|
|
182
312
|
/** 销毁控制器,并清理所有单位的动画、重试和格子占用状态。 */
|
|
183
313
|
destroy(): void {
|
|
314
|
+
this.scheduler.unschedule(this.flushMovementFrame);
|
|
315
|
+
this.frameFlushScheduled = false;
|
|
316
|
+
this.dirtyNodes.clear();
|
|
184
317
|
this.movementVersions.clear();
|
|
185
318
|
this.movementOwners.forEach((owner, playerNode) => {
|
|
186
319
|
this.pathState.release(owner);
|
|
@@ -270,8 +403,21 @@ export class GridMovementController {
|
|
|
270
403
|
const ratio = total > 0 ? Math.min(1, remain / total) : 1;
|
|
271
404
|
|
|
272
405
|
stepping = true;
|
|
406
|
+
// 有外部监听时才挂每帧回调;批量监听只做脏标记(一次 Set.add),排序等重活每帧合并一次
|
|
407
|
+
const updateHandler = this.movementUpdateHandler;
|
|
408
|
+
const needOnUpdate = this.needsFrameUpdate();
|
|
409
|
+
const toOptions: GridTweenOption | undefined = needOnUpdate
|
|
410
|
+
? {
|
|
411
|
+
onUpdate: () => {
|
|
412
|
+
if (this.movementFrameHandler || this.depthSorter || this.depthSortHandler) {
|
|
413
|
+
this.markDirty(playerNode);
|
|
414
|
+
}
|
|
415
|
+
updateHandler?.(playerNode, playerNode.getWorldPosition(GridMovementController.TMP_POSITION));
|
|
416
|
+
},
|
|
417
|
+
}
|
|
418
|
+
: undefined;
|
|
273
419
|
this.tweenFactory.tween(playerNode)
|
|
274
|
-
.to(stepDuration() * ratio, { worldPosition: to })
|
|
420
|
+
.to(stepDuration() * ratio, { worldPosition: to }, toOptions)
|
|
275
421
|
.call(() => {
|
|
276
422
|
stepping = false;
|
|
277
423
|
this.pathState.completeMove(previous, next, owner, shareToken);
|
|
@@ -4,7 +4,7 @@ import { Vec3 } from 'cc';
|
|
|
4
4
|
import { MapViewModel } from './MapViewModel';
|
|
5
5
|
import { Color } from 'cc';
|
|
6
6
|
import { AttackDistance } from './Common';
|
|
7
|
-
import { GridMovementController, GridTweenFactory } from './GridMovementController';
|
|
7
|
+
import { DepthSortOptions, GridMovementController, GridTweenFactory } from './GridMovementController';
|
|
8
8
|
const { ccclass, property } = _decorator;
|
|
9
9
|
|
|
10
10
|
@ccclass('PathfindingModule')
|
|
@@ -27,7 +27,7 @@ export class PathfindingModule {
|
|
|
27
27
|
this.movementController = controller;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
static InitMovementController(scheduler: Component, options?: { retryDelay?: number; moveDuration?: number; tweenFactory?: GridTweenFactory }): void {
|
|
30
|
+
static InitMovementController(scheduler: Component, options?: { retryDelay?: number; moveDuration?: number; tweenFactory?: GridTweenFactory; depthSort?: boolean }): void {
|
|
31
31
|
if (!this.movementController) {
|
|
32
32
|
this.movementController = new GridMovementController(scheduler, options);
|
|
33
33
|
}
|
|
@@ -56,6 +56,39 @@ export class PathfindingModule {
|
|
|
56
56
|
this.movementController?.setMoveDuration(playerNode, moveDuration);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* 监听"移动过程中位置更新":tween 每更新一次节点坐标就回调一次,
|
|
61
|
+
* 适合外部在这里刷新节点层级(按 y 做深度排序、越靠下越靠前显示)。
|
|
62
|
+
* 传 null 取消监听。
|
|
63
|
+
* @param handler (playerNode, worldPosition),worldPosition 是复用的临时向量,仅在回调内有效
|
|
64
|
+
*/
|
|
65
|
+
static SetMovementUpdateHandler(handler: ((playerNode: Node, worldPosition: Vec3) => void) | null): void {
|
|
66
|
+
this.movementController?.setMovementUpdateHandler(handler);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 开启 / 关闭深度排序(三种用法):
|
|
71
|
+
* 1. SetDepthSort(true) → 内置插入式排序,默认按世界坐标 y(y 越小越靠前)
|
|
72
|
+
* 2. SetDepthSort(true, (node) => -node.worldPosition.y) → 内置排序 + 自定义排序键
|
|
73
|
+
* 3. SetDepthSort(true, { handler: (nodes) => {...} }) → 完全交给外部处理层级,
|
|
74
|
+
* 内部只负责脏标记并把一帧内的多次变化合并成一次回调(nodes 是本帧动过的单位)
|
|
75
|
+
*
|
|
76
|
+
* 传 false 关闭。内置排序只调整本帧动过的节点,不做全量排序,单位再多也是 O(移动位数)。
|
|
77
|
+
*/
|
|
78
|
+
static SetDepthSort(enabled: boolean, setting?: ((node: Node) => number) | DepthSortOptions): void {
|
|
79
|
+
this.movementController?.setDepthSort(enabled, setting);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 批量监听"移动过程中位置更新"(推荐,多个单位时用它):
|
|
84
|
+
* 无论多少个单位在移动,每帧只回调一次,参数是本帧坐标发生变化的单位数组。
|
|
85
|
+
* 单位越多收益越大:N 次回调 / N 次排序 收敛为 1 次。
|
|
86
|
+
* 传 null 取消监听。与 SetMovementUpdateHandler 二选一,不要两个都设。
|
|
87
|
+
*/
|
|
88
|
+
static SetMovementFrameHandler(handler: ((playerNodes: Node[]) => void) | null): void {
|
|
89
|
+
this.movementController?.setMovementFrameHandler(handler);
|
|
90
|
+
}
|
|
91
|
+
|
|
59
92
|
/** 获取单位当前的单格移动时长(秒) */
|
|
60
93
|
static GetMoveDuration(playerNode: Node): number {
|
|
61
94
|
return this.movementController?.getMoveDuration(playerNode) ?? 0;
|