Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Behavior Tree for Unity ECS (DOTS) framework

License

NotificationsYou must be signed in to change notification settings

quabug/EntitiesBT

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

_____      _   _ _   _           ______ _____ |  ___|    | | (_) | (_)          | ___ \_   _|| |__ _ __ | |_ _| |_ _  ___  ___ | |_/ / | |  |  __| '_ \| __| | __| |/ _ \/ __|| ___ \ | |  | |__| | | | |_| | |_| |  __/\__ \| |_/ / | |  \____/_| |_|\__|_|\__|_|\___||___/\____/  \_/

Behavior Tree framework based on and used for Unity Entities (DOTS)

Why another Behavior Tree framework?

Existing BT frameworks do not support Entities out of the box.

Features

  • Actions are easy to read/write data from/to entity.
  • Use Component of Unity directly instead of own editor window to maximize compatibility of other plugins.
  • Data-oriented design, save all nodes data into a continuous data blob (NodeBlob.cs)
  • Node has no internal states.
  • Separate runtime nodes and editor nodes.
  • Easy to extend.
  • Also compatible with Unity GameObject without entity.
  • Able to serialize the behavior tree into a binary file.
  • Flexible thread control: force on the main thread, force on job thread, controlled by the behavior tree.
  • Runtime debug window to show the states of nodes.
  • Optimized. 0 GC allocated by behavior tree itself after initializing, only 64Byte GC allocated every tick byCreateArchetypeChunkArrayAsync.

Disadvantages

  • Incompatible with burst.
  • Incompatible with il2cpp.
  • Lack of action nodes. (Will add some actions as extensions if I need them)
  • Difficult to change tree structure at runtime.
  • Node data must be compatible withBlob and created byBlobBuilder

Packages

HowTo

Installation

Requirement: Unity >= 2020.2 and entities package >= 0.14.0-preview.19

Install the packages either by

UPM:modifyPackages/manifest.json as below:

{  "dependencies": {    ...    "com.quabug.entities-bt.builder.graphview": "1.4.0",  },  "scopedRegistries": [    {      "name": "package.openupm.com",      "url": "https://package.openupm.com",      "scopes": [        "com.quabug"      ]    }  ]}

or

OpenUPM:

openupm add com.quabug.entities-bt.builder.graphview

Usage

GraphView Builder

Create behavior tree graph

create graph

Attach graph of behavior tree ontoEntity

attach graph

Component Builder

Create behavior tree

create

Attach behavior tree ontoEntity

attach

Serialization

save-to-file

Thread control

thread-control

  • Force Run on Main Thread: running on the main thread only, will not use job to tick behavior tree. Safe to callUnityEngine method.
  • Force Run on Job: running on job threads only, will not use the main thread to tick the behavior tree. Not safe to callUnityEngine method.
  • Controlled by Behavior Tree: Running on job threads by default, but will switch to main thread once meet decorator ofRunOnMainThread

Variant

Variant Types
  • BlobVariantReader: read-only variant
  • BlobVariantWriter: write-only variant
  • BlobVariantReaderAndWriter: read-write variant, able to link to the same source.
Variant Sources
  • LocalVariant: regular variable, custom value will save intoNodeData.

  • ComponentVariant: fetch data fromComponent onEntity

    • Component Value Name: which value should be accessed from the component
    • Copy To Local Node: Will read component data into a local node and never write back into component data. (ForceReadOnly access)
  • NodeVariant: fetch data from the blob of another node

    • Node Object: another node should be accessed by this variable, and must be in the same behavior tree.
    • Value Field Name: the name of the data field in another node.
    • Access Runtime Data:
      • false: will copy data to local blob node while building, value change ofNode Object won't affect variable once build.
      • true: will access data field ofNode Object at runtime, something like reference value ofNode Object.
  • ScriptableObjectVariant

    • Scriptable Object: target SO.
    • Scriptable Object Value: target field.
Code Example
[BehaviorNode("867BFC14-4293-4D4E-B3F0-280AD4BAA403")]publicstructVariantNode:INodeData{publicBlobVariantReader<int>IntVariant;publicBlobVariantReaderAndWriter<float>FloatVariant;publicNodeStateTick<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardblackboard)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{varintVariant=IntVariant.Read(index,refblob,refblackboard);// get variable valuevarfloatVariant=FloatVariant.Read(index,refblob,refblackboard);FloatVariant.Write(index,refblob,refblackboard,floatVariant+1);returnNodeState.Success;}publicvoidReset<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardbb)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{}}

Multiple Trees

Adding multipleBehaviorTreeRoot onto a single entity gameobject will create numerous behavior trees to control this single entity.Behavior tree sorted byOrder ofBehaviorTreeRoot.

Debug

debug

Custom behavior node

Action

// most important part of node, actual logic on runtime.[Serializable]// for debug view only[BehaviorNode("F5C2EE7E-690A-4B5C-9489-FB362C949192")]// must add this attribute to indicate a class is a `BehaviorNode`publicstructEntityMoveNode:INodeData{publicfloat3Velocity;// node data saved in `INodeBlob`publicNodeStateTick<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardbb)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{// access and modify node datarefvartranslation=refbb.GetDataRef<Translation>();// get blackboard data by ref (read/write)vardeltaTime=bb.GetData<BehaviorTreeTickDeltaTime>();// get blackboard data by value (readonly)translation.Value+=Velocity*deltaTime.Value;returnNodeState.Running;}publicvoidReset<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardbb)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{}}// debug view (optional)publicclassEntityMoveDebugView:BTDebugView<EntityMoveNode>{}

Decorator

// runtime behavior[Serializable]// for debug view only[BehaviorNode("A13666BD-48E3-414A-BD13-5C696F2EA87E",BehaviorNodeType.Decorate/*decorator must explicit declared*/)]publicstructRepeatForeverNode:INodeData{publicNodeStateBreakStates;publicNodeStateTick<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardblackboard)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{// short-cut to tick first only childrenvarchildState=blob.TickChildrenReturnFirstOrDefault(index,blackboard);if(childState==0)// 0 means no child was ticked// tick an already completed `Sequence` or `Selector` will return 0{blob.ResetChildren(index,blackboard);childState=blob.TickChildrenReturnFirstOrDefault(index,blackboard);}if(BreakStates.HasFlag(childState))returnchildState;returnNodeState.Running;}publicvoidReset<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardbb)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{}}// debug view (optional)publicclassBTDebugRepeatForever:BTDebugView<RepeatForeverNode>{}

Composite

// runtime behavior[StructLayout(LayoutKind.Explicit)]// sizeof(SelectorNode) == 0[BehaviorNode("BD4C1D8F-BA8E-4D74-9039-7D1E6010B058",BehaviorNodeType.Composite/*composite must explicit declared*/)]publicstructSelectorNode:INodeData{publicNodeStateTick<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardblackboard)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{// tick children and break if the child state is running or success.returnblob.TickChildrenReturnLastOrDefault(index,blackboard,breakCheck: state=>state.IsRunningOrSuccess());}publicvoidReset<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardbb)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{}}// avoid debugging view since there's nothing that needs to debug for `Selector`

EntityQuery

The behavior tree needs some extra information for generatingEntityQuery.

publicstructSomeNode:INodeData{// read-only accessBlobVariantReader<int>IntVariable;// read-write access (there's no write-only access)BlobVariantWriter<float>FloatVariable;// read-write accessBlobVariantReaderAndWriter<double>FloatVariable;// leave method attribute to be empty and will generate right access of this methodpublicNodeStateTick<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardblackboard)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{// generate `[ReadOnly(typeof(ReadOnlyComponent)]` on `Tick` methodbb.GetData<ReadOnlyComponent>();// generate `[ReadWrite(typeof(ReadWriteComponent)]` on `Tick` methodbb.GetDataRef<ReadWriteComponent>();returnNodeState.Success;}// or manually declare right access types for this method[EntitiesBT.Core.ReadWrite(typeof(ReadWriteComponentData))][EntitiesBT.Core.ReadOnly(typeof(ReadOnlyComponentData))]publicvoidReset<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardbb)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{// generate `[ReadOnly(typeof(ReadOnlyComponent)]` on `Reset` methodbb.GetData<ReadOnlyComponent>();// generate `[ReadWrite(typeof(ReadWriteComponent)]` on `Reset` methodbb.GetDataRef<ReadWriteComponent>();// ...}}

make sure to mark the outside method call with the proper access attributes to generate the appropriate access type onTick orReset method of the node

publicstaticclassExtension{[ReadOnly(typeof(FooComponent)),ReadWrite(typeof(BarComponent))]publicstaticvoidCall<[ReadWrite]T,[ReadOnly]U>([ReadOnly]Typetype){/* ... */}}publicstructSomeNode:INodeData{// leave method attribute to be empty to generate automaticallypublicNodeStateTick<TNodeBlob,TBlackboard>(intindex,refTNodeBlobblob,refTBlackboardblackboard)whereTNodeBlob:struct,INodeBlobwhereTBlackboard:struct,IBlackboard{// the following call will generate access attributes on `Tick` as below:// [ReadOnly(typeof(FooComponent))]// [ReadWrite(typeof(BarComponent))]// [ReadWrite(typeof(int))]// [ReadOnly(typeof(float))]// [ReadOnly(typeof(long))]Extension.Call<int,float>(typeof(long));returnNodeState.Success;}}

Advanced: customize debug view

Advanced: access other node data

NodeBlob stores all the behavioral tree's internal data, which can be accessed from any node.To access specific node data, just store its index and access it byINodeData.GetNodeData<T>(index).

Advanced: behavior tree component

[BehaviorTreeComponent]// mark a component data as `BehaviorTreeComponent`publicstructBehaviorTreeTickDeltaTime:IComponentData{publicfloatValue;}[UpdateBefore(typeof(VirtualMachineSystem))]publicclassBehaviorTreeDeltaTimeSystem:ComponentSystem{protectedoverridevoidOnUpdate(){Entities.ForEach((refBehaviorTreeTickDeltaTimedeltaTime)=>deltaTime.Value=Time.DeltaTime);}}

The components of behavior will automatically add toEntity on the stage of convertingGameObject toEntity, ifAutoAddBehaviorTreeComponents is enabled.

Advanced: virtual node builder

A single builder node can produce multiple behavior nodes while building.

publicclassBTSequence:BTNode<SequenceNode>{[Tooltip("Enable this will re-evaluate node state from the first child until running node instead of skip to the running node directly.")][SerializeField]privatebool_recursiveResetStatesBeforeTick;publicoverrideINodeDataBuilderSelf=>_recursiveResetStatesBeforeTick// add `RecursiveResetStateNode` as the parent of `this` node?newBTVirtualDecorator<RecursiveResetStateNode>(this):base.Self;}

Data Structure

publicstructNodeBlob{// default data (serializable data)publicBlobArray<int>Types;// type id of behavior node, generated from `Guid` of `BehaviorNodeAttribute`publicBlobArray<int>EndIndices;// range of node branch must be in [nodeIndex, nodeEndIndex)publicBlobArray<int>Offsets;// data offset of `DefaultDataBlob` of this nodepublicBlobArray<byte>DefaultDataBlob;// nodes data// runtime only data (only exist on runtime)publicBlobArray<NodeState>States;// nodes states// initialize from `DefaultDataBlob`publicBlobArray<byte>RuntimeDataBlob;// same as `DefaultNodeData` but only available at runtime and will reset to `DefaultNodeData` once reset.}

data-structure

About

Behavior Tree for Unity ECS (DOTS) framework

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp