# UCommandRouter ## 基本信息 - **类型**: UCLASS(BlueprintType, EditInlineNew, DefaultToInstanced) - **父类**: UObject, implements ICommandEndpoint - **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandRouter.h - **模块**: CharacterControl ## 功能概述 核心命令路由引擎。管理子端点注册表(TMap)。实现基于标签的匹配,包含两种模式:连续模式(标签 + ContinuousFriendly 检查)和离散模式(标签 ALL/ANY 匹配)。标签聚合系统通过引用计数将子标签向上合并。受跳数限制保护的调度。子端点输出命令的向上传播。自身身份保护防止循环注册。 ## 设计用意 插件的核心。基于标签的发布-订阅总线,替代硬引用。支持层级树拓扑(路由器嵌套路由器)。跳数限制防止无限循环。标签聚合实现高效的父级过滤。EditInlineNew/DefaultToInstanced 用于嵌入组件中。 ## 职责范围 命令路由与端点管理。注册/注销端点。匹配并调度命令。聚合并管理标签订阅。不生成命令(由输入组件负责)。 ## 项目内依赖 | 依赖项 | 关系 | 源文件 | |--------|------|--------| | CommandEndpoint.h | #include | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h | ## 对外接口 UCommandRouter 是命令路由系统的核心引擎。作为 UObject(EditInlineNew, DefaultToInstanced) 且实现 ICommandEndpoint,它既可以嵌入组件又可以参与上级路由。蓝图和 C++ 均可调用以下 UFUNCTION: **端点管理(BlueprintCallable)**: - **RegisterEndpoint(TScriptInterface) -> bool**: 注册一个端点。检查 GUID 有效性、重复注册、自注册防护。注册成功后绑定端点的两个委托并执行标签聚合。返回 false 表示注册失败 - **UnregisterEndpoint(const FGuid&) -> bool**: 按 GUID 注销端点。解绑委托、移除标签聚合、从注册表删除 - **UnregisterEndpointByInterface(TScriptInterface) -> bool**: 按接口指针注销端点,内部提取 GUID 后转调 UnregisterEndpoint - **ClearAllEndpoints()**: 批量注销所有已注册端点。遍历收集所有端点后逐个调用 UnregisterEndpointByInterface - **GetEndpointCount() -> int32**: 返回当前已注册端点数量 **路由器自身配置(BlueprintCallable)**: - **SetRouterState(const FEndpointState&)**: 设置路由器作为端点时的配置。只更新 InterestedTags/bIsActive/bIsContinuousFriendly/bIsAsynchronous 字段,保留 EndpointGuid。更新后广播 StateChangedDelegate - **GetRouterState() -> FEndpointState**: 返回当前路由器的端点状态 **命令输入(BlueprintCallable)**: - **InputCommand(const FCommandPacket&)**: 向路由器注入命令,直接调用 ReceiveCommand 触发路由逻辑。可用于外部系统或上级路由器向本路由器投递命令 **委托(public field)**: - **StateChangedDelegate** (FOnEndpointStateChanged): 路由器自身状态变更时广播 - **CommandOutputDelegate** (FOnCommandOutput): 路由器作为端点向上级输出的命令通过此委托广播 ## 使用方法 UCommandRouter 通常不直接使用,而是通过 UCommandRouterComponent 包裹后挂载到 Actor: - **组件嵌入选框**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouterComponent.cpp:15` -- UCommandRouterComponent 通过 CreateDefaultSubobject 创建 InternalRouter - **端点注册入口**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:45-84` -- RegisterEndpoint 的完整注册流程 - **命令分发**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:13-31` -- ReceiveCommand 入口:HopLimit 检查 -> 递减 -> 按类型分发 - **离散路由**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:164-197` -- RouteDiscreteCommand 遍历注册表,提取离散元数据,对每个匹配端点投递 - **连续路由**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:199-218` -- RouteContinuousCommand 遍历注册表,对每个 bIsContinuousFriendly 且 Tag 匹配端点投递 - **端点注册**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:38` -- UEndpointComponent::BeginPlay 找到兄弟 UCommandRouterComponent 后调用 `Router->InternalRouter->RegisterEndpoint(this)` ## 用例 - **命令注入**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:145-162` -- OnRegisteredEndpointOutputCommand 接收子端点产出的命令:上行命令先投递本地再转发上级,非上行命令直接投递本地端点 - **端点注册完整流程**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:45-84` -- 验证 -> 注册 -> 委托绑定(BindDynamic) -> 标签聚合(AddEndpointToAggregation),全在 RegisterEndpoint 中完成 - **上行传播**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:148-155` -- bIsUpward 命令处理:拷贝命令清除上行标志后本地投递,同时对原始命令做额外 HopLimit 递减后通过 CommandOutputDelegate 向上转发 - **标签聚合更新**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:137-143` -- OnRegisteredEndpointStateChanged 收到端点状态变更后调用 UpdateEndpointInAggregation 做差分更新 - **自注册防护**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:65-69` -- RegisterEndpoint 检查端点 GUID 是否等于自身 GUID,防止路由器将自己注册为子端点造成环路