Unity DOTS Behavior-Tree implementation
- High performance. Zero GC allocations.
- Easy to use, less to code.
- 100% ECS-ish. No blackboards, no unrelated systems to think about.
- Supports all classic composite node types: Selector, Sequence, Repeater.
- Conditional aborts support.
- Creating (and editing) a tree in Unity Editor.
- Compatible with burst and il2cpp.
Min. Requirements:
Unity >= 2019.4.18 and entities package >= 0.11.2-preview.1
Tested on:
Unity 2020.2.3 and entities package 0.17.0-preview.42
You can install the repository using UPM
:
Just add this line in Packages/manifest.json:
"com.nanory.unity.entities.bt": "https://https://github.com/SinyavtsevIlya/DOTS-BehaviorTree.git",
By right-clicking the Hierarchy > Behavior Tree > Root
(Selector/Sequence/Repeater) doing the same way as shown in first step.
To make it you need to create a pair: a component and a system.
Action Node example:
// 1) Create an action node component and (optional) add this attribute.
[GenerateAuthoringComponent]
public struct SeekEnemy : IComponentData { }
public sealed class BTSeekEnemySystem : SystemBase
{
protected override void OnUpdate()
{
var beginSimECB = this.CreateBeginSimECB();
Entities
.WithAll<SeekEnemy>() // 2) Simply add it to your Query.
.ForEach((Entity agentEntity,
// The parameters below are just up to you.
in EnemyLink enemyLink,
in LocalToWorld ltw,
in StoppingDistance stoppingDistance,
in BTActionNodeLink bTNodeLink) => // 3) But don't forget to add this component in the end.
{
var position = GetComponent<LocalToWorld>(enemyLink.Value).Position;
if (math.length((position - ltw.Position)) < stoppingDistance.Value)
{
// 4) When you decided that the action was completed successfully, then you need to send the result.
beginSimECB.AddComponent(bTNodeLink.Value, BTResult.Success);
}
else
beginSimECB.AddComponent(agentEntity, new MoveToDestinationRequest() { Position = position, Speed = 1f });
})
.ScheduleParallel();
}
}
Conditional Node example is very similar except two things:
// 1) NOTE: in this case we need to implement interface IConditional. (It's only for conversion/validation purposes, not for runtime)
[GenerateAuthoringComponent]
public struct BTIsEnemyReachable : IComponentData, IConditional{ }
public sealed class BTIsEnemyReachableSystem : SystemBase
{
protected override void OnUpdate()
{
var beginSimECB = this.CreateBeginSimECB();
Entities
.WithAll<BTIsEnemyReachable>()
.ForEach(
(DynamicBuffer<InteractableElement> interactableElements,
in EnemyLink enemyLink,
in Name name,
// 2) NOTE: we need to pass a Conditional Node reference.
in BTConditionalNodeLink bTNodeLink) =>
{
for (int i = 0; i < interactableElements.Length; i++)
{
if (interactableElements[i].value == enemyLink.Value)
{
beginSimECB.AddComponent(bTNodeLink.Value, BTResult.Success);
return;
}
}
// you also able to send "Fail" results.
beginSimECB.AddComponent(bTNodeLink.Value, BTResult.Fail);
})
.WithoutBurst()
.Run();
}
}
// TODO
// TODO
// TODO (basic usage, priorities)
// TODO (enter/exit events)
Yes. Since the tree is just a prefab it's easy to make using nested prefabs feature.