CsTrees 1.0.5
dotnet add package CsTrees --version 1.0.5
NuGet\Install-Package CsTrees -Version 1.0.5
<PackageReference Include="CsTrees" Version="1.0.5" />
<PackageVersion Include="CsTrees" Version="1.0.5" />
<PackageReference Include="CsTrees" />
paket add CsTrees --version 1.0.5
#r "nuget: CsTrees, 1.0.5"
#:package CsTrees@1.0.5
#addin nuget:?package=CsTrees&version=1.0.5
#tool nuget:?package=CsTrees&version=1.0.5
CsTrees 
一个 .NET 行为树框架,基本设计复刻了 py_trees。
特性
- 复刻py_trees的节点类型和大部分API:Behaviours、Composites和Decorators
- 基于C#改造的黑板系统:类型安全的键值对共享状态,访问控制的源生成器
- 流式构建器:基于栈的声明式 API,支持黑板作用域嵌套,可继承的泛型 CRTP 基类
- 行为源生成:通过
[GenerateTreeBuilderExtension]自动生成扩展方法,或通过IBehaviourCatalog构建领域特定构建器 - 扩展的显示:除了自带ASCII渲染以外,可按需扩展;构建中也可输出预览
- 交班机制:柔性中断,循环调用
Handoff()让树有序地完成在途补偿并释放资源
快速开始
直接构建行为树
using CsTrees;
using CsTrees.Composites;
using CsTrees.Behaviours;
var tree = new Selector("Root", children: new[]
{
new Sequence("Check & Act", children: new[]
{
new Success("Condition Check"),
new Success("Execute Action")
}),
new Failure("Fallback")
});
await tree.TickOnce();
流式构建器
using CsTrees.FluentBuilder;
var bb = new CsTrees.Blackboard.Blackboard();
var tree = new DefaultTreeBuilder()
.Selector("Root")
.Sequence("Check & Act")
.WithBlackboard(bb)
.MyCheck("Check")
.MyBehaviour("Act")
.End()
.End()
.Failure("Fallback")
.End()
.Build();
显示行为树
using CsTrees.Display;
string ascii = Display.AsciiTree(tree, showStatus: true, showFeedbackMessage: true);
Console.WriteLine(ascii);
经典节点类型
CsTrees 实现了 py_trees 的大多数节点类型,详细的节点说明请参阅 py_trees 文档。
其他复刻的功能点也请翻阅仓库代码并和原版比对。
交班(Handoff)
托比欧(Doppio):不敢相信……居然在岸边找到公用电话……Boss,你在哪里?
Boss:我现在要去你那里,你保持好伪装不要引起他们的注意!
托比欧:我应该怎么做?Boss!
Boss:用「Handoff」!你至少把鱼竿收了……(鱼已上钩,而托比欧却面目呆滞、握着鱼竿愣在那里)
交班是一种全新设计的行为树语义。它从根出发去寻找 Running 的节点,并尝试运行预埋在行为树内的子树,来完成中断和收尾。
// 交班:反复调用直到根节点交接完毕
while (tree.Root.Status == Status.Running)
await tree.Handoff();
经典节点的交班方式:
| 节点 | 交班行为 |
|---|---|
| 叶子 | 直接置为 Invalid |
| Sequence / Selector | 仅解除当前运行的子树(CurrentChild),其余子树保持原状 |
| Parallel | 所有 Running 子树各推进一步,互不阻塞 |
| Decorator | 转发给被装饰节点,待其解除完毕后再作废自身 |
交班特化节点:Try 与 Always
新节点把补偿语义做进了 Handoff——交班过程中能推进必须跑完的清理工作。它们建立在 Mediator 基础设施之上(按命名槽位容纳子树的复合节点基类),目前已经实现了 Try 、Always 。
Try(try/finally)
提供 try/finally 语义:先执行 try 子树,无论其成败,finally 子树必定随后执行;仅当两棵子树都成功时整体才成功。即使 try 已失败,finally 仍会运行。交班时先解除 body,再逐 tick 推进 finally 补偿子树。
var tree = new DefaultTreeBuilder()
.Try("HandleRequest")
.Success("Process")
.Finally()
.Success("Cleanup")
.End()
.Build();
Always(always/then)
Tick 采用无记忆语义:每轮都先执行 always 守卫子树,仅当其成功才执行 then 子树;守卫失败则整体立即失败。交班时若 then 仍在运行,须每轮重新通过守卫才推进 then 的解除。
var tree = new DefaultTreeBuilder()
.Always("GuardedWork")
.Success("CheckPermission")
.Then()
.Success("DoWork")
.End()
.Build();
黑板(Blackboard)
基本理念和 py_trees 黑板 相同。
CsTrees 的黑板并不是单例的,且没有采用Client的设计。
CsTrees 提供了两种方式与黑板交互:手动注册端口和通过 [BlackboardKey] 特性自动生成。
通过 [BlackboardKey] 特性声明端口(推荐)
在 partial 行为类上,使用 [BlackboardKey] 特性标记 BehaviourKeyAccess<T> 类型的属性即可。源生成器(CsTrees.SourceGenerator)会自动生成以下代码:
- 带黑板参数的构造函数重载 — 自动注册所有端口,无需手动调用
GrantRead/GrantWrite SetupPorts(Blackboard)方法 — 手动注册端口(可选)
注意:TreeBuilder 扩展方法不会自动生成。需要额外标注
[GenerateTreeBuilderExtension]特性才会生成(见下方说明)。
using CsTrees;
using CsTrees.Blackboard;
using CsTrees.FluentBuilder;
using System.Threading.Tasks;
[GenerateTreeBuilderExtension]
public partial class DetectButton : Behaviour
{
[BlackboardKey("btn_x", Access = Access.Write)]
public BehaviourKeyAccess<int> X { get; private set; } = null!;
[BlackboardKey("btn_y", Access = Access.Write)]
public BehaviourKeyAccess<int> Y { get; private set; } = null!;
// 构造函数需为 private,源生成器会生成带 Blackboard 参数的 public 重载
private DetectButton(string name) : base(name) { }
protected override async Task<Status> Update()
{
X.Set(42);
Y.Set(99);
return Status.Success;
}
}
[GenerateTreeBuilderExtension] 自动生成 TreeBuilder 扩展方法
在 Behaviour 子类上标注 [GenerateTreeBuilderExtension] 后,源生成器会为该类生成静态扩展方法类(如 DetectButtonBuilderExtensions),包含一个或多个扩展方法(对应每个 private 构造函数一个)。这些方法:
- 接收
this TBuilder builder和string name参数 - 自动从构建器当前黑板作用域获取
Blackboard并注入构造函数 - 支持可选的端口 key 覆盖参数(如
xKey: "custom_x")
// 生成的扩展方法使用方式
var tree = new DefaultTreeBuilder()
.Sequence("Pipeline")
.WithBlackboard(bb)
.DetectButton("检测按钮") // SG 生成的扩展方法,bb 自动注入
.End()
.End()
.Build();
使用建议:通用行为(如传感器检测、基础移动)使用
[GenerateTreeBuilderExtension]生成全局扩展方法;业务预设行为通过IBehaviourCatalog构建领域构建器(见下方)。
手动注册端口
不依赖源生成器时,可以手动调用 GrantRead/GrantWrite/GrantExclusiveWrite:
using CsTrees.Blackboard;
var bb = new Blackboard();
var readAccess = bb.GrantRead<int>(behaviour, "/sensor/value");
var writeAccess = bb.GrantWrite<string>(behaviour, "/actor/state");
var exclusiveAccess = bb.GrantExclusiveWrite<bool>(behaviour, "/locked");
// 在行为的 Update() 中
var value = readAccess.Get();
writeAccess.Set("active");
exclusiveAccess.Set(true);
readAccess.Unset();
流式构建器(TreeBuilder)
TreeBuilder 提供基于栈的声明式 API,是更流行的选择。
使用 DefaultTreeBuilder
DefaultTreeBuilder 是框架内置的构建器,内置三个 Catalog(CompositesCatalog、DecoratorsCatalog、DefaultBehavioursCatalog),涵盖了所有标准节点类型。大多数情况下直接使用即可:
var tree = new DefaultTreeBuilder()
.Sequence("Root")
.Retry("Attempt", numFailures: 3)
.Success("Action")
.End()
.End()
.Build();
配合黑板使用(推荐)
在大多数时刻我们只用到一块黑板,因此可以通过黑板作用域的设计来简化代码
通过 WithBlackboard() 为子节点注入黑板作用域。标注了 [GenerateTreeBuilderExtension] 的行为会生成 TreeBuilder 扩展方法,黑板会自动从当前作用域获取:
var bb = new Blackboard();
var tree = new DefaultTreeBuilder()
.Selector("Root")
.Sequence("Pipeline")
.WithBlackboard(bb)
// 利用源生成器生成的扩展方法,可以在 WithBlackboard 作用域内省略 bb 的显式注入
.DetectButton("检测按钮")
.MoveTo("移动到目标")
.End()
.End()
.End()
.Build();
预览
Preview() 方法可以在构建过程中随时预览当前的树结构:
var builder = new DefaultTreeBuilder()
.Selector("Root")
.Sequence("Part1")
.Success("Step1");
// 预览当前状态(Part1 未闭合)
var preview = builder.Preview();
Console.WriteLine(Display.AsciiTree(preview));
// 继续构建
builder
.Success("Step2")
.End()
.End()
.Build();
继承 TreeBuilder 构建领域特定构建器
TreeBuilder 基于 CRTP 模式实现为泛型基类 TreeBuilder<TBuilder>,支持继承以创建领域特定的行为树构建器。
框架自带 DefaultTreeBuilder,内置了常用的 Composites、Decorators、Leaf 节点,直接可用。你也可以通过继承 TreeBuilder<TBuilder> 自定义领域构建器。
通过 IBehaviourCatalog 自动生成构建方法
源生成器会扫描 TreeBuilder<TBuilder> 子类中声明的私有 IBehaviourCatalog 类型字段或属性,自动为 Catalog 中的每个 public 工厂方法生成对应的 fluent 构建方法。
支持同时使用多个 Catalog,意味着你可以拼配构建器的能力。
// 1. 定义行为目录类
public class GameComposites : IBehaviourCatalog
{
public Composite CombatSequence(string name, bool memory, IEnumerable<Behaviour> children)
=> new Composites.Sequence(name, memory, children);
}
public class GameBehaviours : IBehaviourCatalog
{
public Behaviour MakePlayerIdle(string name) => new Idle(name);
public Behaviour MakeCollectItem(string name, Blackboard bb)
=> new CollectItem(name, bb);
}
// 2. 定义领域构建器
public partial class GameBuilder : TreeBuilder<GameBuilder>
{
private readonly CompositesCatalog compositesCatalog = new();
private readonly DecoratorsCatalog decoratorsCatalog = new();
private readonly GameComposites gameComposites = new();
private readonly GameBehaviours gameBehaviours = new();
}
// 3. 使用
var tree = new GameBuilder()
.CombatSequence("Gameplay")
.Sequence("顺序执行", memory: true)
.MakePlayerIdle("待机")
.MakeCollectItem("拾取物品")
.End()
.End()
.Build();
Catalog 工厂方法的参数规则:
- 返回类型必须为
Behaviour或其子类 - 如果参数中包含
Blackboard类型,生成的构建方法会自动注入当前作用域的黑板 - 如果参数中包含
IEnumerable<Behaviour>类型,会被识别为 Composite 的 children 参数 - 如果参数中包含
Behaviour类型,会被识别为 Decorator 的 child 参数(Mediator 工厂方法中则作为槽位参数,按声明顺序与[MediatorSlots]槽位配对) - 参数默认值会被保留到生成的方法签名中
- 如果返回类型是
Mediator子类,其必须标注[MediatorSlots]声明槽位名与顺序,源生成器才会为它生成构建方法与切槽方法
[GenerateTreeBuilderExtension]和IBehaviourCatalog是两种互补的代码生成路径:前者生成全局静态扩展方法,适合通用行为;后者生成实例方法挂在领域构建器上,适合业务预设。
扩展包
- CsTrees.MEAI — 将 TreeBuilder 的
IBehaviourCatalog工厂方法自动转换为 AI Agent 可调用的工具方法(配合 Microsoft.Extensions.AI 使用),让 LLM 能逐步构建和运行行为树。
许可证
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on CsTrees:
| Package | Downloads |
|---|---|
|
CsTrees.MEAI
MEAI integration for CsTrees behaviour tree framework |
GitHub repositories
This package is not used by any popular GitHub repositories.