26 KiB
动态图块移动系统
本文档为 动态图块系统 的移动子系统设计文档, 重点描述双方向模型、
IDynamicMover计划式移动接口及执行流程。 本文档中新增的类型与接口同样需要添加至@user/data-base/src/map/types.ts。
背景与动机
IDynamicLayer.moveDynamicStep / moveDynamicWith 是命令式简单接口,适用于
一次性的简单移动,但对以下场景力不从心:
- 后退移动:角色面朝上、向下移动时,朝向不变但位移反向,
单一
FaceDirection参数无法同时描述朝向和移动方向; - 连续路径中途动态追加步骤:命令式接口执行后无法继续追加;
- 移动速度切换:路径中途需要变速,命令式接口不支持;
- 复杂动画编排:需要按序执行多段计划时,纯
Promise链表达不够直观。
因此引入以图块为粒度绑定的 IDynamicMover,采用先建计划、再执行的设计理念,
参考 @motajs/animate/types.ts 的链式计划构建模式,
以及 @user/data-state/legacy/move.ts 中 ObjectMoverBase 的双方向与队列设计。
实现思路
1. 双方向模型
每个动态图块维护两个方向字段:
| 字段 | 含义 | 影响 |
|---|---|---|
faceDirection |
朝向,视觉上「面朝哪里」 | 通过 IRoleFaceBinder 决定渲染图块数字 |
moveDirection |
移动方向,「向哪边走」 | 结合 getFaceMovement 计算位移 |
两者分离后可以表达:
- 后退:
moveDirection = Up,执行backward()步,moveDirection = Down,faceDirection = Down,位移向下,朝向同步翻转。 - 横向移动不转身:使用
stepFace(move, face)单独指定朝向,moveDirection与faceDirection独立设置。
绝对方向步骤(传入 FaceDirection)默认将两者同时更新;
forward() 沿当前 moveDirection 移动并对齐 faceDirection;
backward() 将 moveDirection 取反并同步 faceDirection。
2. 方向步语义
绝对方向步(IDynamicMoveStepDir / IDynamicMoveStepDirFace)直接传入 FaceDirection:
step(dir, count?: number):追加count(默认 1)个绝对方向步,moveDirection和faceDirection均更新为dir;stepFace(move, face, count?: number):追加count(默认 1)个绝对方向步,moveDirection更新为move,faceDirection更新为face——用于移动方向与朝向不一致的场景(如斜切移动保持正向)。
相对方向步(IDynamicMoveStepSpecial)通过独立方法构建:
forward(count?):沿当前tile.moveDirection移动,moveDirection不变,faceDirection对齐为moveDirection;backward(count?):沿opposite(tile.moveDirection)移动,moveDirection取反,faceDirection同步更新为新的moveDirection,自动使用逆向动画。
3. DynamicMoveStep 类型
步骤类型通过 const enum 区分,避免字符串比较。
前进/后退方向用独立的 DynamicSpecialStep 枚举表示;
动画播放方向用独立的 DynamicAnimDirection 枚举表示,两者互不依赖:
const enum DynamicMoveStepType {
/** 绝对方向步,同步更新 moveDirection 和 faceDirection */
Dir,
/** 绝对方向步,单独指定 faceDirection */
DirFace,
/** 速度步 */
Speed,
/** 纯转向步,不产生位移 */
Face,
/** 特殊步(前进或后退) */
Special,
/** 动画方向步,不影响 moveDirection/faceDirection */
AnimDir
}
/** 特殊步方向:前进或后退 */
const enum DynamicSpecialStep {
Forward,
Backward
}
/** 动画播放方向:正向或逆向 */
const enum DynamicAnimDirection {
Forward,
Backward
}
/** 绝对方向步:move 方向既是移动方向也是朝向 */
interface IDynamicMoveStepDir {
type: DynamicMoveStepType.Dir;
move: FaceDirection;
}
/** 绝对方向步:move 是移动方向,face 是显式指定的朝向 */
interface IDynamicMoveStepDirFace {
type: DynamicMoveStepType.DirFace;
move: FaceDirection;
face: FaceDirection;
}
/** 速度步:修改后续移动的每格耗时(单位 ms,越小越快) */
interface IDynamicMoveStepSpeed {
type: DynamicMoveStepType.Speed;
value: number;
}
/** 转向步:仅更新 faceDirection,不产生位移,等待渲染动画完成 */
interface IDynamicMoveStepFace {
type: DynamicMoveStepType.Face;
value: FaceDirection;
}
/** 特殊步:前进(Forward)或后退(Backward)。`forward(n)`/`backward(n)` 构建时 n>1 会 push n 个相同步骤 */
interface IDynamicMoveStepSpecial {
type: DynamicMoveStepType.Special;
direction: DynamicSpecialStep;
}
/** 动画方向步:指定后续步骤动画正向播放(Forward)还是逆向播放(Backward) */
interface IDynamicMoveAnimDir {
type: DynamicMoveStepType.AnimDir;
dir: DynamicAnimDirection;
}
type DynamicMoveStep =
| IDynamicMoveStepDir
| IDynamicMoveStepDirFace
| IDynamicMoveStepSpeed
| IDynamicMoveStepFace
| IDynamicMoveStepSpecial
| IDynamicMoveAnimDir;
4. IMoverController 接口
IDynamicMover.start() 返回 IMoverController,用于控制进行中的移动。
interface IMoverController {
/** 本次移动是否已全部完成 */
readonly done: boolean;
/** 当本次移动全部步骤完成时兑现 */
readonly onEnd: Promise<void>;
/**
* 向当前队列末尾追加步骤(仅在移动进行中有效,完成后追加无效)
*/
push(...steps: DynamicMoveStep[]): void;
/**
* 停止移动,等待当前步骤完成后停止,返回的 Promise 在停止后兑现
*/
stop(): Promise<void>;
}
5. IDynamicMover 接口
每个 IDynamicTile 持有一个绑定的 IDynamicMover 实例(tile.mover),
无需单独创建或管理生命周期。
5.1 状态读取
// 以下状态属性来自 IObjectMover<IDynamicTile>,IDynamicMover 全部继承
interface IObjectMover<T extends IObjectMovable> {
/** 是否正在移动 */
readonly moving: boolean;
/** 当前朝向(与 tile.faceDirection 同步) */
readonly faceDirection: FaceDirection;
/** 当前移动方向(与 tile.moveDirection 同步) */
readonly moveDirection: FaceDirection;
/** 所属的可移动对象 */
readonly tile: T;
// ...
}
5.2 计划构建(链式)
所有构建方法均返回 this,支持链式调用。
计划在调用 start() 前不执行,仅追加到内部队列。
interface IDynamicMover {
// ...
/** 追加 count(默认 1)个绝对方向步,同步更新 moveDirection 和 faceDirection */
step(dir: FaceDirection, count?: number): this;
/** 追加 count(默认 1)个绝对方向步,单独指定 faceDirection(用于移动方向与朝向不一致的场景) */
stepFace(move: FaceDirection, face: FaceDirection, count?: number): this;
/** 追加前进步,沿当前 moveDirection 移动,faceDirection 对齐为 moveDirection */
forward(count?: number): this;
/** 追加后退步,moveDirection 取反,faceDirection 同步更新,自动使用逆向动画 */
backward(count?: number): this;
/** 追加一个速度步,改变后续移动的每格耗时(ms) */
speed(value: number): this;
/** 追加一个纯转向步,仅更新 faceDirection,不产生位移 */
face(dir: FaceDirection): this;
/** 追加一个动画方向步,控制后续步骤的动画播放方向(Forward 正向 / Backward 逆向) */
animDir(dir: DynamicAnimDirection): this;
/** 清空尚未执行的计划队列 */
clear(): this;
// ...
}
用法示例:
// 先向下走 2 步,切换为半速,再向右走 1 步
tile.mover
.step(FaceDirection.Down, 2)
.speed(200)
.step(FaceDirection.Right)
.start();
// 后退 3 步(moveDirection/faceDirection 翻转,逆向动画)
tile.mover.backward(3).start();
// 向右移动但保持朝下(如横向滑步)
tile.mover.stepFace(FaceDirection.Right, FaceDirection.Down, 2).start();
5.3 执行控制
interface IDynamicMover {
// ...
/**
* 开始执行计划队列,返回控制对象。
* 若已在移动则返回 null(不中断当前移动)。
*/
start(): IMoverController | null;
}
6. 移动执行流程
每次 start() 启动后,执行器按以下总流程处理队列:
触发 onMoveStart → 循环每步:[
abstract onStepStart(step, tile, mover) 执行并返回 code: number(子类计算本步场景)→
触发 IObjectMoverHooks.onStepStart(code, step, tile, mover) 钩子(渲染预处理,此时 tile.x/y 为移动前坐标)→
等待所有 Promise →
abstract onStepEnd(code, step, tile, mover) 执行逻辑效果(子类根据 code 决定如何执行)→
触发 IObjectMoverHooks.onStepEnd(code, step, tile, mover) 钩子(此时 tile.x/y 为移动后坐标)
] → 触发 onMoveEnd。
6.1 绝对方向步(DynamicMoveStepType.Dir)
onStepStart 阶段(tile.x/y 为原始坐标):
abstract onStepStart(step, tile, mover)执行,子类计算本步code(正常移动、碰墙等)并返回Promise<number>。- 父类拿到
code,触发IObjectMoverHooks.onStepStart(code, step, tile, mover)钩子——渲染端在此阶段播放移动动画, 可由step.move与当前tile.x/y推算目标位置。 await Promise.all(promises)——等待全部动画完成。
onStepEnd 阶段(逻辑效果):
abstract onStepEnd(code, step, tile, mover)执行——子类依据code决定本步实际效果: 更新tile.moveDirection = step.move。- 计算
{dx, dy} = getFaceMovement(step.move),立即更新tile.x += dx、tile.y += dy。 - 同步更新
posMap。 - 执行转向逻辑(见 dynamic-tile.md §8),
更新
tile.faceDirection与tile.num。 - 子类返回后,父类触发
IObjectMoverHooks.onStepEnd(code, step, tile, mover)钩子,此时tile.x/y已为新坐标。
6.1.1 含显式朝向的绝对方向步(DynamicMoveStepType.DirFace)
与 Dir 步骤相同,但步骤 5 改为直接将 tile.faceDirection 设为 step.face,
并通过 getFaceOf(tile.num, step.face) 更新 tile.num,不经过 degradeFace 降级。
6.2 速度步(DynamicMoveStepType.Speed)
更新内部速度状态,立即生效,无位移,无需等待。
6.3 纯转向步(DynamicMoveStepType.Face)
经由 onStepStart 触发钩子(tile.x/y 不变,step.type === DynamicMoveStepType.Face 供渲染端区分);
等待渲染端动画完成后,onStepEnd 阶段执行转向逻辑,更新 faceDirection 与 tile.num,
触发 onStepEnd 钩子。
6.4 特殊步(DynamicMoveStepType.Special)
语义说明:forward/backward 以上一步的移动方向(即当前 tile.moveDirection)为基准;
若无上一步,则回退到当前 faceDirection;若图块无朝向绑定,则抛出错误。
Forward(DynamicSpecialStep.Forward):
actualDir = tile.moveDirection(不改变 moveDirection);- 执行与
Dir步骤相同的位移流程; - 步骤 2:跳过(
moveDirection保持不变); - 步骤 5:执行转向逻辑,以
actualDir更新tile.faceDirection与tile.num。
Backward(DynamicSpecialStep.Backward):
actualDir = opposite(tile.moveDirection);- 在执行前临时将逆向动画标志设为开启(步骤完成后恢复);
- 执行与
Dir步骤相同的位移流程; - 步骤 2:
tile.moveDirection = actualDir; - 步骤 5:执行转向逻辑,以
actualDir更新tile.faceDirection与tile.num。
6.5 动画方向步(DynamicMoveStepType.AnimDir)
指定后续步骤动画是正向播放(DynamicAnimDirection.Forward)还是逆向播放(DynamicAnimDirection.Backward),
与图块朝向无关(朝向始终由 faceDirection 决定)。
修改 mover 内部的 currentAnimDir 状态。
渲染端在 onStepStart 收到 AnimDir 步骤时读取并更新本地动画方向状态,后续步骤的 onStepStart 触发时依据此状态决定播放方向。
onStepEnd 阶段无逻辑操作,立即完成,不影响坐标与方向。
7. onStepStart / onStepEnd 钩子返回值
IObjectMoverHooks.onStepStart 与 onStepEnd 均为可选钩子(?),返回类型为 Promise<void>,
执行器始终通过 Promise.all 等待所有订阅方完成后再继续。
关键时序:onStepStart 所有 Promise 兑现后,才执行逻辑效果(abstract onStepEnd);
onStepEnd 全部兑现后,才进入下一步。
多个订阅方通过 Promise.all 并行等待,与 forEachHook 返回所有值的设计天然契合——
const promises = forEachHook(hook => hook.onStepStart?.(code, step, tile, mover));
await Promise.all(promises);
8. 与 IDynamicLayer / IDynamicTile 接口的关系
IDynamicLayer 和 IDynamicTile 仅保留 step(dir) 单步便捷接口
(等价于 tile.mover.step(dir).start()),不再提供 moveDynamicWith / moveWith 等批量接口。
复杂移动能力(forward/backward/animDir 等)通过 tile.mover 访问。
渲染端订阅移动事件的流程:
- 订阅
IDynamicLayer.onCreateDynamicTile; - 创建图块时获取
tile.mover引用,直接向其添加钩子; - 移动事件从
tile.mover触发,无需经过IDynamicLayer转发。
为此,IDynamicMover 扩展为可订阅对象,
实现 IHookable<IObjectMoverHooks<IDynamicTile>>(见接口定义汇总)。
涉及文件
需要修改的文件
@user/data-base/src/map/types.ts
修改现有接口(移动相关新增类型已移至 move/types.ts):
IDynamicTile新增readonly mover: IDynamicMoverIDynamicTile已有readonly faceDirection: FaceDirection与readonly moveDirection: FaceDirection(已在主文档确认)IObjectMovable接口(x、y、moveDirection、faceDirection只读属性 +setPos/setMoveDirection/setFaceDirection,定义在dynamic-tile.md)IDynamicLayer/IDynamicTile仅保留step(dir)便捷接口, 移除moveDynamicWith/moveDynamicStep/moveWith/moveStep(主文档接口汇总需同步更新)
@user/data-base/src/map/types.ts
DynamicMoveStepTypeconst enum: 含Dir/DirFace/Speed/Face/Special/AnimDir六个成员DynamicSpecialStepconst enum:含Forward/Backward两个成员DynamicAnimDirectionconst enum:含Forward/Backward两个成员IDynamicMoveStepDir接口(type: DynamicMoveStepType.Dir,move: FaceDirection)IDynamicMoveStepDirFace接口(type: DynamicMoveStepType.DirFace,move/face: FaceDirection)IDynamicMoveStepSpeed接口(type: DynamicMoveStepType.Speed)IDynamicMoveStepFace接口(type: DynamicMoveStepType.Face)IDynamicMoveStepSpecial接口(type: Special,direction: DynamicSpecialStep;无count字段,forward(n)/backward(n)改为 push n 个步骤对象)IDynamicMoveAnimDir接口(type: DynamicMoveStepType.AnimDir,dir: DynamicAnimDirection)DynamicMoveStep联合类型(含以上六种步骤)IMoverController接口IObjectMoverHooks<T extends IObjectMovable>接口(含onMoveStart/onMoveEnd/onStepStart/onStepEnd;onStepStart/onStepEnd参数顺序为(code, step, tile, mover),按使用频率排列)IObjectMover<T extends IObjectMovable>接口(含faceDirection/moveDirection只读属性及全部计划构建方法;step/stepFace含count?参数)IDynamicMover extends IObjectMover<IDynamicTile>(无额外成员,继承基类全部能力;IDynamicMoverHooks已移除,直接使用IObjectMoverHooks<IDynamicTile>)
@user/data-base/src/map/mover.ts
abstract class ObjectMover<T extends IObjectMovable>抽象基类:- 实现
IObjectMover<T>,在此提供全部计划构建方法(step/stepFace/forward/backward/speed/face/animDir/clear/start)及队列执行调度逻辑; - 含四个抽象方法,子类必须实现,组成移动核心控制流:
abstract onMoveStart(tile, mover): Promise<void>: 整次移动开始时调用abstract onMoveEnd(tile, mover): Promise<void>: 整次移动结束时调用abstract onStepStart(step, tile, mover): Promise<number>: 单步逻辑执行前调用, 子类计算并返回code;父类将code传给IObjectMoverHooks.onStepStart钩子后等待abstract onStepEnd(code, step, tile, mover): Promise<void>: 单步钩子等待完成后调用,子类依据code执行本步实际逻辑效果
- 实现
class DynamicMover extends ObjectMover<IDynamicTile> implements IDynamicMover: 持有tile: IDynamicTile引用,维护moveQueue、moving、faceDirection、moveDirection状态;实现四个抽象方法(含 posMap 更新、转向逻辑等)DynamicTile类新增readonly mover: DynamicMover, 在构造时以this为参数创建
接口定义汇总
const enum DynamicMoveStepType {
Dir,
DirFace,
Speed,
Face,
Special,
AnimDir
}
const enum DynamicSpecialStep {
Forward,
Backward
}
const enum DynamicAnimDirection {
Forward,
Backward
}
interface IDynamicMoveStepDir {
type: DynamicMoveStepType.Dir;
move: FaceDirection;
}
interface IDynamicMoveStepDirFace {
type: DynamicMoveStepType.DirFace;
move: FaceDirection;
face: FaceDirection;
}
interface IDynamicMoveStepSpeed {
type: DynamicMoveStepType.Speed;
value: number;
}
interface IDynamicMoveStepFace {
type: DynamicMoveStepType.Face;
value: FaceDirection;
}
/** forward(n)/backward(n) 构建时 n>1 会 push n 个相同步骤,步骤对象本身不携带 count */
interface IDynamicMoveStepSpecial {
type: DynamicMoveStepType.Special;
direction: DynamicSpecialStep;
}
interface IDynamicMoveAnimDir {
type: DynamicMoveStepType.AnimDir;
dir: DynamicAnimDirection;
}
type DynamicMoveStep =
| IDynamicMoveStepDir
| IDynamicMoveStepDirFace
| IDynamicMoveStepSpeed
| IDynamicMoveStepFace
| IDynamicMoveStepSpecial
| IDynamicMoveAnimDir;
interface IMoverController {
readonly done: boolean;
readonly onEnd: Promise<void>;
push(...steps: DynamicMoveStep[]): void;
stop(): Promise<void>;
}
interface IObjectMoverHooks<T extends IObjectMovable> extends IHookBase {
onMoveStart(mover: IObjectMover<T>, tile: T): Promise<void>;
onMoveEnd(mover: IObjectMover<T>, tile: T): Promise<void>;
/** 触发时 tile.x/y 为移动前坐标,适合渲染端在此播放动画 */
onStepStart?(
code: number,
step: DynamicMoveStep,
tile: T,
mover: IObjectMover<T>
): Promise<void>;
/** 触发时 tile.x/y 已更新为移动后坐标 */
onStepEnd?(
code: number,
step: DynamicMoveStep,
tile: T,
mover: IObjectMover<T>
): Promise<void>;
}
interface IObjectMover<T extends IObjectMovable> extends IHookable<
IObjectMoverHooks<T>
> {
readonly moving: boolean;
readonly tile: T;
readonly faceDirection: FaceDirection;
readonly moveDirection: FaceDirection;
step(dir: FaceDirection, count?: number): this;
stepFace(move: FaceDirection, face: FaceDirection, count?: number): this;
forward(count?: number): this;
backward(count?: number): this;
speed(value: number): this;
face(dir: FaceDirection): this;
animDir(dir: DynamicAnimDirection): this;
clear(): this;
start(): IMoverController | null;
}
// 这里的 onStepStart 不再接受 code 作为参数,因为这应当是其返回值 `Promise<number>`,
// 具体来说,这四个方法是整个移动的核心方法而非钩子,它应当提供接口让子类实现,
// 子类应当真正执行移动效果,并进行一定的控制。显然子类是没办法知道 onStepStart 中的信息的,
// 所以才提供了一个 code,用于子类向 ObjectMover 提供信息,然后父类再传递给子类,
// 从而子类了解到 onStepStart 中的信息,再进一步决定这一步真正应该如何执行。
// 而对于钩子,实际上是在子类的 onStepStart 执行完毕后进行的,所以是知道 code 的,所以才是参数。
// 你应该好好想一想这之间的关系。
// 以及子类是要求必须实现的,所以不应该会有返回 `void` 的场景,四个方法都应该返回 `Promise<void>`。
// 总的来说,父类是一个“系统级”的流程控制器,它通过这四个接口来实现真正的移动控制,
// 就像 legacy ObjectMoverBase 中的 startMove 一样。
abstract class ObjectMover<
T extends IObjectMovable
> implements IObjectMover<T> {
abstract onMoveStart(tile: T, mover: IObjectMover<T>): Promise<void>;
abstract onMoveEnd(tile: T, mover: IObjectMover<T>): Promise<void>;
/** 子类计算并返回本步 code;父类取得 code 后再触发 IObjectMoverHooks.onStepStart 钩子 */
abstract onStepStart(
step: DynamicMoveStep,
tile: T,
mover: IObjectMover<T>
): Promise<number>;
/** 子类依据 code 执行本步实际逻辑效果;父类在此之后触发 IObjectMoverHooks.onStepEnd 钩子 */
abstract onStepEnd(
code: number,
step: DynamicMoveStep,
tile: T,
mover: IObjectMover<T>
): Promise<void>;
// 计划构建方法(step/stepFace/forward/backward/speed/face/animDir/clear/start)在此实现
}
// faceDirection/moveDirection 已提升至 IObjectMover,IDynamicMover 无需额外声明
// IDynamicMoverHooks 已移除,直接使用 IObjectMoverHooks<IDynamicTile>
interface IDynamicMover extends IObjectMover<IDynamicTile> {
// 继承自 IObjectMover:moving, faceDirection, moveDirection, tile, 全部计划构建方法及钩子
}
问题
-
backward()的移动方向基准 ✅ 已确定backward()以opposite(tile.moveDirection)作为移动方向(基于moveDirection取反,非faceDirection)。moveDirection更新为反向,faceDirection通过转向逻辑跟随同步更新。 -
forward()是否更新 faceDirection ✅ 已确定forward()沿当前moveDirection移动,moveDirection不变,faceDirection通过转向逻辑对齐为moveDirection。 -
转向步触发钩子方式 ✅ 已确定
统一经由
onStepStart(code, step, tile, mover)钩子处理,在步骤逻辑执行前触发。 渲染端通过step.type === DynamicMoveStepType.Face区分纯转向步与位移步;onStepStart触发时tile.x/y为原始坐标,渲染端可直接读取,无需反推。 转向逻辑(faceDirection/num更新)在onStepEnd阶段执行。 -
animDir与渲染钩子集成 ✅ 已确定onStepStart统一接收所有步骤,IDynamicMoveAnimDir步骤本身即为信息载体。 渲染端在onStepStart收到AnimDir步骤时更新本地动画方向状态; 后续方向步的onStepStart触发时,渲染端依据本地状态决定动画播放方向。 -
玩家移动与图块移动的复用 ✅ 已确定
玩家(勇士)移动逻辑与
IDynamicMover高度一致,共同抽象为IObjectMover<T extends IObjectMovable>(参考 legacyObjectMoverBase,适配新 API); 核心抽象类为abstract class ObjectMover<T>,含四个抽象方法组成控制流:- 计划构建方法(
step/stepFace/forward/backward/speed/face/animDir/clear/start)全部在ObjectMover基类实现,子类无需重复声明。 - 移动流程钩子(
IObjectMoverHooks<T>,实现IHookable<IObjectMoverHooks<T>>):onMoveStart(mover, tile):整次移动队列开始时触发;onMoveEnd(mover, tile):整次移动队列结束时触发;onStepStart(code, step, tile, mover):单步开始前触发(tile.x/y为移动前坐标);onStepEnd(code, step, tile, mover):单步完成后触发(tile.x/y已更新); 其中code: number标识移动场景(如碰墙、遇敌、循环地图等),由具体实现层设置。
- 四个抽象方法(
onMoveStart/onMoveEnd/onStepStart/onStepEnd):子类(DynamicMover/HeroMover) 通过实现这四个方法完全控制移动行为,执行流程由ObjectMover基类统一调度。 关键:onStepStart返回Promise<number>(code),父类将此 code 传给钩子后再调用onStepEnd(code, ...), 子类在onStepEnd中依据 code 执行实际状态变更。四个方法均返回Promise,无void选项。 - 泛型参数
T extends IObjectMovable:IObjectMovable是可移动对象的最小接口 (x、y、moveDirection、faceDirection只读属性 +setPos/setMoveDirection/setFaceDirection方法),定义在dynamic-tile.md;IDynamicTile extends IObjectMovable,玩家侧使用IHeroMovable extends IObjectMovable。 IDynamicMover extends IObjectMover<IDynamicTile>: 无额外成员,直接继承基类全部能力(含faceDirection/moveDirection及IObjectMoverHooks)。
- 计划构建方法(