@cc-component/cc-pathfinding 1.0.9 → 1.1.1

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,6 +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
8
 
8
9
  declare module "cc" {
9
10
 
@@ -56,9 +57,18 @@ declare global {
56
57
 
57
58
  //#region 移动控制
58
59
  /** 内部创建并绑定移动控制器 */
59
- export function InitMovementController(scheduler: Component, options?: { retryDelay?: number; moveDuration?: number }): void
60
- /** 设置单位位置 */
61
- export function SetPosition(playerNode: Node, position: { row: number, col: number }): void
60
+ export function InitMovementController(scheduler: Component, options?: { retryDelay?: number; moveDuration?: number; tweenFactory?: GridTweenFactory }): void
61
+ /**
62
+ * 注入自定义 tween 实现(比如换成项目自己的动画系统)
63
+ * @param factory 传 null 或从未调用时,自动使用 cc 自带 tween
64
+ */
65
+ export function SetTweenFactory(factory: GridTweenFactory | null): void
66
+ /**
67
+ * 设置单位位置
68
+ * @param useBottomPosition false(默认)用格子中心 GetMapPosition,
69
+ * true 用格子底部 GetMapModel(...).bottom_positon
70
+ */
71
+ export function SetPosition(playerNode: Node, position: { row: number, col: number }, useBottomPosition?: boolean): void
62
72
  /**
63
73
  * 让单位移动到目标格子
64
74
  * @param allowOverlap 为 true 时该单位进入"允许重叠"模式,
@@ -66,8 +76,10 @@ declare global {
66
76
  * 与终点是否相同无关
67
77
  * @param moveDuration 本次移动的"单格移动时长",单位秒(越小越快);
68
78
  * 不传或传 <= 0 时使用 InitMovementController 配置的默认值
79
+ * @param useBottomPosition 节点对齐方式:false(默认)用格子中心 GetMapPosition,
80
+ * true 用格子底部 GetMapModel(...).bottom_positon
69
81
  */
70
- export function MoveTo(playerNode: Node, target: { row: number, col: number }, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap?: boolean, moveDuration?: number): void
82
+ export function MoveTo(playerNode: Node, target: { row: number, col: number }, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap?: boolean, moveDuration?: number, useBottomPosition?: boolean): void
71
83
  /** 取消移动 */
72
84
  export function CancelMove(playerNode: Node, preserveCurrent?: boolean): void
73
85
  /** 销毁移动控制器 */
@@ -1,14 +1,34 @@
1
- import { Component, Node, Tween, tween } from 'cc';
1
+ import { Component, Node, Tween, tween, Vec3 } from 'cc';
2
2
  import { AttackDistance } from './Common';
3
3
  import { Cell } from './GridMap';
4
4
  import { PathGridStateManager } from './PathGridStateManager';
5
5
  import { PathfindingModule } from './PathfindingModule';
6
6
 
7
+ /** 外部 tween 对象需要满足的最小接口(与 cc.Tween 用法一致) */
8
+ export interface GridTween {
9
+ to(duration: number, props: object): GridTween;
10
+ call(callback: () => void): GridTween;
11
+ start(): GridTween;
12
+ }
13
+
14
+ /**
15
+ * tween 工厂:外部注入后,移动控制器用它创建/停止 tween。
16
+ * 未注入(null)时自动使用 cc 自带的 tween。
17
+ */
18
+ export interface GridTweenFactory {
19
+ /** 创建一个绑定在 target 上的 tween */
20
+ tween(target: Node): GridTween;
21
+ /** 停止 target 上的所有 tween */
22
+ stopAllByTarget(target: Node): void;
23
+ }
24
+
7
25
  export interface GridMovementOptions {
8
26
  /** 每次重试前的等待时间,单位秒 */
9
27
  retryDelay?: number;
10
28
  /** 单格移动动画时长,单位秒 */
11
29
  moveDuration?: number;
30
+ /** 自定义 tween 实现,不传则用 cc 自带 tween */
31
+ tweenFactory?: GridTweenFactory;
12
32
  }
13
33
 
14
34
  /**
@@ -23,14 +43,22 @@ export class GridMovementController {
23
43
  private readonly movementOwners = new Map<Node, symbol>();
24
44
  private readonly movementRetryCallbacks = new Map<Node, () => void>();
25
45
  private readonly positions = new Map<Node, Cell>();
46
+ /** 未注入外部实现时使用的系统 tween */
47
+ private static readonly SYSTEM_TWEEN_FACTORY: GridTweenFactory = {
48
+ tween: (target: Node) => tween(target),
49
+ stopAllByTarget: (target: Node) => Tween.stopAllByTarget(target),
50
+ };
51
+
26
52
  private readonly retryDelay: number;
27
53
  private readonly moveDuration: number;
28
54
  private readonly pathState: PathGridStateManager;
55
+ private customTweenFactory: GridTweenFactory | null;
29
56
 
30
57
  constructor(private readonly scheduler: Component, options: GridMovementOptions = {}) {
31
58
  // 允许外部按场景调整重试间隔和动画时长,默认值保持兼容。
32
59
  this.retryDelay = options.retryDelay ?? 1;
33
60
  this.moveDuration = options.moveDuration ?? 0.5;
61
+ this.customTweenFactory = options.tweenFactory ?? null;
34
62
  this.pathState = new PathGridStateManager({
35
63
  isOccupied: (row, col) => PathfindingModule.IsOccupied(row, col),
36
64
  occupy: (row, col) => PathfindingModule.OccupyNode(row, col, true),
@@ -38,10 +66,23 @@ export class GridMovementController {
38
66
  });
39
67
  }
40
68
 
41
- /** 设定单位当前格子,并同步到节点世界坐标。 */
42
- setPosition(playerNode: Node, position: Cell): void {
69
+ /** 注入自定义 tween 实现,传 null 恢复使用 cc 自带 tween */
70
+ setTweenFactory(factory: GridTweenFactory | null): void {
71
+ this.customTweenFactory = factory ?? null;
72
+ }
73
+
74
+ /** 外部注入优先,取不到就回退到 cc 自带 tween */
75
+ private get tweenFactory(): GridTweenFactory {
76
+ return this.customTweenFactory ?? GridMovementController.SYSTEM_TWEEN_FACTORY;
77
+ }
78
+
79
+ /**
80
+ * 设定单位当前格子,并同步到节点世界坐标。
81
+ * useBottomPosition:false(默认)对齐格子中心,true 对齐格子底部。
82
+ */
83
+ setPosition(playerNode: Node, position: Cell, useBottomPosition = false): void {
43
84
  this.positions.set(playerNode, { row: position.row, col: position.col });
44
- playerNode.worldPosition = PathfindingModule.GetMapPosition(position.row, position.col);
85
+ playerNode.worldPosition = this.resolveCellPosition(position, useBottomPosition);
45
86
  }
46
87
 
47
88
  /** 获取单位当前格子,未记录时返回默认起点。 */
@@ -59,8 +100,9 @@ export class GridMovementController {
59
100
  * 与其他同样开启该开关的单位可以走到同一格(互相穿过、不互相绕行、不互相等待),
60
101
  * 与终点是否相同无关;未开启的单位之间、以及开启与未开启之间仍保持互斥。
61
102
  * moveDuration:本次移动的"单格移动时长",单位秒;不传或传非法值时用控制器的默认值。
103
+ * useBottomPosition:为 true 时节点对齐到格子底部(bottom_positon),否则对齐到格子中心(GetMapPosition)。
62
104
  */
63
- moveTo(playerNode: Node, target: Cell, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap = false, moveDuration?: number): void {
105
+ moveTo(playerNode: Node, target: Cell, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap = false, moveDuration?: number, useBottomPosition = false): void {
64
106
  const start = this.getPosition(playerNode);
65
107
  if (start.row === target.row && start.col === target.col) {
66
108
  return;
@@ -69,18 +111,18 @@ export class GridMovementController {
69
111
  this.cancel(playerNode, true);
70
112
  const route = this.traceRoute(start, target, attackDistance, openColor, allowOverlap, this.movementOwners.get(playerNode));
71
113
  if (!route || route.length === 0 || this.isPathBlocked(start, target, route, attackDistance)) {
72
- this.retryPathfinding(playerNode, target, attackDistance, openColor, callBack, allowOverlap, moveDuration);
114
+ this.retryPathfinding(playerNode, target, attackDistance, openColor, callBack, allowOverlap, moveDuration, useBottomPosition);
73
115
  return;
74
116
  }
75
117
 
76
118
  this.clearPathfindingRetry(playerNode);
77
119
  route.shift();
78
- this.startMovement(playerNode, route, start, target, attackDistance, openColor, callBack, allowOverlap, moveDuration);
120
+ this.startMovement(playerNode, route, start, target, attackDistance, openColor, callBack, allowOverlap, moveDuration, useBottomPosition);
79
121
  }
80
122
 
81
123
  /** 强制中止当前移动,可选择保留当前格子的临时占用。 */
82
124
  cancel(playerNode: Node, preserveCurrent = false): void {
83
- Tween.stopAllByTarget(playerNode);
125
+ this.tweenFactory.stopAllByTarget(playerNode);
84
126
  this.clearPathfindingRetry(playerNode);
85
127
  this.clearMovementRetry(playerNode);
86
128
 
@@ -106,7 +148,7 @@ export class GridMovementController {
106
148
  this.movementVersions.clear();
107
149
  this.movementOwners.forEach((owner, playerNode) => {
108
150
  this.pathState.release(owner);
109
- Tween.stopAllByTarget(playerNode);
151
+ this.tweenFactory.stopAllByTarget(playerNode);
110
152
  });
111
153
  this.movementOwners.clear();
112
154
 
@@ -122,7 +164,7 @@ export class GridMovementController {
122
164
  * 执行逐格移动。
123
165
  * 每次移动前会尝试保留下一格,失败说明被占用,就重新规划路径或延时重试。
124
166
  */
125
- private startMovement(playerNode: Node, route: Cell[], start: Cell, target: Cell, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap = false, moveDuration?: number): void {
167
+ private startMovement(playerNode: Node, route: Cell[], start: Cell, target: Cell, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap = false, moveDuration?: number, useBottomPosition = false): void {
126
168
  const previousOwner = this.movementOwners.get(playerNode);
127
169
  if (previousOwner) {
128
170
  this.pathState.release(previousOwner);
@@ -196,8 +238,8 @@ export class GridMovementController {
196
238
  return;
197
239
  }
198
240
 
199
- tween(playerNode)
200
- .to(duration, { worldPosition: PathfindingModule.GetMapPosition(next.row, next.col) })
241
+ this.tweenFactory.tween(playerNode)
242
+ .to(duration, { worldPosition: this.resolveCellPosition(next, useBottomPosition) })
201
243
  .call(() => {
202
244
  this.pathState.completeMove(previous, next, owner, shareToken);
203
245
  this.positions.set(playerNode, { row: next.row, col: next.col });
@@ -239,12 +281,19 @@ export class GridMovementController {
239
281
  return (typeof moveDuration === 'number' && moveDuration > 0) ? moveDuration : this.moveDuration;
240
282
  }
241
283
 
284
+ /** 节点落到格子上的坐标:底部对齐用 bottom_positon,否则用格子中心 */
285
+ private resolveCellPosition(cell: Cell, useBottomPosition: boolean): Vec3 {
286
+ return useBottomPosition
287
+ ? PathfindingModule.GetMapModel(cell.row, cell.col).bottom_positon
288
+ : PathfindingModule.GetMapPosition(cell.row, cell.col);
289
+ }
290
+
242
291
  /** 路径规划失败时,做一次延迟重试,避免因为瞬时占用导致立刻失败。 */
243
- private retryPathfinding(playerNode: Node, target: Cell, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap = false, moveDuration?: number): void {
292
+ private retryPathfinding(playerNode: Node, target: Cell, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap = false, moveDuration?: number, useBottomPosition = false): void {
244
293
  this.clearPathfindingRetry(playerNode);
245
294
  const retry = () => {
246
295
  this.clearPathfindingRetry(playerNode);
247
- this.moveTo(playerNode, target, attackDistance, openColor, callBack, allowOverlap, moveDuration);
296
+ this.moveTo(playerNode, target, attackDistance, openColor, callBack, allowOverlap, moveDuration, useBottomPosition);
248
297
  };
249
298
  this.movementRetryCallbacks.set(playerNode, retry);
250
299
  this.scheduler.scheduleOnce(retry, this.retryDelay);
@@ -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 } from './GridMovementController';
7
+ import { GridMovementController, GridTweenFactory } from './GridMovementController';
8
8
  const { ccclass, property } = _decorator;
9
9
 
10
10
  @ccclass('PathfindingModule')
@@ -27,14 +27,26 @@ export class PathfindingModule {
27
27
  this.movementController = controller;
28
28
  }
29
29
 
30
- static InitMovementController(scheduler: Component, options?: { retryDelay?: number; moveDuration?: number }): void {
30
+ static InitMovementController(scheduler: Component, options?: { retryDelay?: number; moveDuration?: number; tweenFactory?: GridTweenFactory }): void {
31
31
  if (!this.movementController) {
32
32
  this.movementController = new GridMovementController(scheduler, options);
33
33
  }
34
34
  }
35
35
 
36
- static SetPosition(playerNode: Node, position: { row: number, col: number }): void {
37
- this.movementController?.setPosition(playerNode, position);
36
+ /**
37
+ * 注入自定义 tween 实现(比如换成项目自己的动画系统)。
38
+ * 传 null 或从未调用时,自动使用 cc 自带 tween。
39
+ */
40
+ static SetTweenFactory(factory: GridTweenFactory | null): void {
41
+ this.movementController?.setTweenFactory(factory);
42
+ }
43
+
44
+ /**
45
+ * 设置单位所在格子,并同步节点世界坐标。
46
+ * useBottomPosition:false(默认)用格子中心 GetMapPosition,true 用格子底部 bottom_positon。
47
+ */
48
+ static SetPosition(playerNode: Node, position: { row: number, col: number }, useBottomPosition = false): void {
49
+ this.movementController?.setPosition(playerNode, position, useBottomPosition);
38
50
  }
39
51
 
40
52
  static GetPosition(playerNode: Node): { row: number, col: number } {
@@ -48,9 +60,11 @@ export class PathfindingModule {
48
60
  * 与终点是否相同无关;未开启的单位之间、以及开启与未开启之间仍保持互斥。
49
61
  * moveDuration:本次移动的"单格移动时长",单位秒(数字越小走得越快)。
50
62
  * 不传或传 <= 0 时使用 InitMovementController 配置的默认值。
63
+ * useBottomPosition:节点对齐方式。false(默认)用格子中心 GetMapPosition,
64
+ * true 用格子底部 GetMapModel(...).bottom_positon(角色脚底踩在格子底边)。
51
65
  */
52
- static MoveTo(playerNode: Node, target: { row: number, col: number }, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap = false, moveDuration?: number): void {
53
- this.movementController?.moveTo(playerNode, target, attackDistance, openColor, callBack, allowOverlap, moveDuration);
66
+ static MoveTo(playerNode: Node, target: { row: number, col: number }, attackDistance: AttackDistance, openColor: boolean, callBack?: () => void, allowOverlap = false, moveDuration?: number, useBottomPosition = false): void {
67
+ this.movementController?.moveTo(playerNode, target, attackDistance, openColor, callBack, allowOverlap, moveDuration, useBottomPosition);
54
68
  }
55
69
 
56
70
  static CancelMove(playerNode: Node, preserveCurrent = false): void {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cc-component/cc-pathfinding",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "engine": ">=3.8.6",
5
5
  "description": "系统组件添加常用扩展方法",
6
6
  "main": "index.ts",