All Projects → quabug → Entitiesbt

quabug / Entitiesbt

Licence: mit
Behavior Tree for Unity ECS (DOTS) framework

Labels

Projects that are alternatives of or similar to Entitiesbt

Deadsimple Pixel Perfect Camera
An exceedingly easy-to-use pixel perfect orthographic camera script for 2D scenes in Unity. Punch in a few specs and you've got a working pixel perfect camera. It's that easy.
Stars: ✭ 186 (-11.43%)
Mutual labels:  unity3d
Naughtybeziercurves
Bezier Curve Game Object for Unity
Stars: ✭ 195 (-7.14%)
Mutual labels:  unity3d
Shinyeffectforugui
Shiny effect of uGUI, which does not need mask or normal map.
Stars: ✭ 204 (-2.86%)
Mutual labels:  unity3d
Rtltmpro
Right-To-Left Text Mesh Pro for Unity. This plugin adds support for Persian and Arabic languages to TextMeshPro.
Stars: ✭ 187 (-10.95%)
Mutual labels:  unity3d
Ez Camera Shake Unity
A free powerful asset for achieving easy and quality camera shake in Unity. Open-sourced with the permission of Road Turtle Games. 📷
Stars: ✭ 191 (-9.05%)
Mutual labels:  unity3d
Unity Shaders
✨ Shader demo - More than 300 examples
Stars: ✭ 198 (-5.71%)
Mutual labels:  unity3d
Videolabtest
OP-Z videolab examples
Stars: ✭ 186 (-11.43%)
Mutual labels:  unity3d
Vfxgraphtestbed
My testbed for Unity VFX Graph
Stars: ✭ 209 (-0.48%)
Mutual labels:  unity3d
Fluent State Machine
Fluent API for creating state machines in C#
Stars: ✭ 195 (-7.14%)
Mutual labels:  unity3d
Delight
Delight is an open source component-oriented framework for Unity.
Stars: ✭ 201 (-4.29%)
Mutual labels:  unity3d
Arcadecarphysics
Arcade Car Physics - Vehicle Simulation for Unity3D
Stars: ✭ 189 (-10%)
Mutual labels:  unity3d
Libtessdotnet
C# port of the famous GLU Tessellator - prebuilt binaries now available in "releases" tab
Stars: ✭ 191 (-9.05%)
Mutual labels:  unity3d
Lomenui
Stylish UI package for Unity engine.
Stars: ✭ 199 (-5.24%)
Mutual labels:  unity3d
Isaura
An attempt at making a aura thingie with a isoline shader.
Stars: ✭ 187 (-10.95%)
Mutual labels:  unity3d
Nice Lua
基于xlua的MVVM框架,支持Addressables, 统一渲染管线等Unity新特性
Stars: ✭ 207 (-1.43%)
Mutual labels:  unity3d
Unitysingleton
The best way to implement singleton pattern in Unity.
Stars: ✭ 185 (-11.9%)
Mutual labels:  unity3d
Ssgi Urp
Screen Space Global Illumination for Unity Universal Render Pipeline
Stars: ✭ 198 (-5.71%)
Mutual labels:  unity3d
Klakhap
HAP video player plugin for Unity
Stars: ✭ 209 (-0.48%)
Mutual labels:  unity3d
Csharpgameframework
基于unity3d引擎与c#语言的游戏框架/架构(包括客户端与服务器)。使用ServerPlatform作为服务端通信基础设施。
Stars: ✭ 208 (-0.95%)
Mutual labels:  unity3d
Unityhook
Platform to hook into Unity3D assemblies
Stars: ✭ 198 (-5.71%)
Mutual labels:  unity3d
_____      _   _ _   _           ______ _____ 
|  ___|    | | (_) | (_)          | ___ \_   _|
| |__ _ __ | |_ _| |_ _  ___  ___ | |_/ / | |  
|  __| '_ \| __| | __| |/ _ \/ __|| ___ \ | |  
| |__| | | | |_| | |_| |  __/\__ \| |_/ / | |  
\____/_| |_|\__|_|\__|_|\___||___/\____/  \_/  
                                               

Table of contents

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

Why another Behavior Tree framework?

Existing BT frameworks are not support Entities out of 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 any entity.
  • Able to serialize behavior tree into binary file.
  • Flexible thread control: force on main thread, force on job thread, controlled by behavior tree.
  • Runtime debug window to show the states of nodes.
  • Optimized. 0 GC allocated by behavior tree itself after initialized, only 64Byte GC allocated every tick by CreateArchetypeChunkArrayAsync.

Disadvantages

  • Incompatible with burst.
  • Lack of action nodes. (Will add some actions as extension if I personally need them)
  • Not easy to modify tree structure at runtime.
  • Node data must be compatible with Blob and created by BlobBuilder

Packages

HowTo

Installation

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

Install the packages either by

UPM: modify Packages/manifest.json as below

{
  "dependencies": {
    ...
    "com.quabug.entities-bt.essential": "1.1.1",
    "com.quabug.entities-bt.codegen": "1.0.0",
    "com.quabug.entities-bt.builder.component": "1.0.0",
    "com.quabug.entities-bt.debug.component-viewer": "1.0.0",
    "com.quabug.entities-bt.variable.scriptable-object": "1.0.0"
  },
  "scopedRegistries": [
    {
      "name": "package.openupm.com",
      "url": "https://package.openupm.com",
      "scopes": [
        "com.quabug.entities-bt"
      ]
    }
  ]
}

or

OpenUPM:

openupm add com.quabug.entities-bt.essential
openupm add com.quabug.entities-bt.builder.component
openupm add com.quabug.entities-bt.debug.component-viewer
openupm add com.quabug.entities-bt.variable.scriptable-object

Usage

Create behavior tree

create

Attach behavior tree onto Entity

attach

Serialization

save-to-file

Thread control

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

Variant

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

  • ComponentVariant: fetch data from Component on Entity

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

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

    • Scriptable Object: target SO.
    • Scriptable Object Value: target field.
Code Example
    public class BTVariantNode : BTNode<VariantNode>
    {
        // have to generate an interface of `Int32VariantReader` to make it possible to serialize and display by Unity.
        // see "Generate specific types` below
        [SerializeReference, SerializeReferenceButton] // neccessary for editor
        public Int32VariantReader IntReader; // an `int` variant reader

        public SingleSerializedReaderAndWriterVariant FloatRW;

        protected override unsafe void Build(ref VariantNode data, BlobBuilder builder, ITreeNode<INodeDataBuilder>[] tree)
        {
            // save `Int32VariantReader` as `BlobVariantReader<int>` of `VariantNode`
            IntReader.Allocate(ref builder, ref data.IntVariant, this, tree);
            FloatRW.Allocate(ref builder, ref data.FloatVariant, this, tree);
        }
    }

    [BehaviorNode("867BFC14-4293-4D4E-B3F0-280AD4BAA403")]
    public struct VariantNode : INodeData
    {
        public BlobVariantReader<int> IntVariant;
        public BlobVariantReaderAndWriter<float> FloatVariant;

        public NodeState Tick<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard blackboard)
            where TNodeBlob : struct, INodeBlob
            where TBlackboard : struct, IBlackboard
        {
            var intVariant = IntVariant.Read(index, ref blob, ref blackboard); // get variable value
            var floatVariant = FloatVariant.Read(index, ref blob, ref blackboard);
            FloatVariant.Write(index, ref blob, ref blackboard, floatVariant + 1);
            return NodeState.Success;
        }

        public void Reset<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard bb)
            where TNodeBlob : struct, INodeBlob
            where TBlackboard : struct, IBlackboard
        {}
    }
Generate specific types of IVariant<T>

Generic IVariant<T> cannot be serialized in Unity, since [SerializeReference] is not allowed on a generic type. A specific type of IVariant<T> must be declared before use.

  • First create a Scriptable Object of VariantGeneratorSetting
Snipaste_2020-03-18_18-57-30
  • Fill which Types you want to use as variable property.
  • Fill Filename, Namespace, etc.
  • Create script from this setting and save it in Assets
Snipaste_2020-03-18_18-57-36
  • And now you are free to use specific type properties, like float2Property etc.

Multiple Trees

Add multiple BehaviorTreeRoot onto a single entity gameobject will create multiple behavior tree to control this single entity. Behavior tree sorted by Order of BehaviorTreeRoot.

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`
public struct EntityMoveNode : INodeData
{
    public float3 Velocity; // node data saved in `INodeBlob`
    
    public NodeState Tick<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard bb)
        where TNodeBlob : struct, INodeBlob
        where TBlackboard : struct, IBlackboard
    { // access and modify node data
        ref var translation = ref bb.GetDataRef<Translation>(); // get blackboard data by ref (read/write)
        var deltaTime = bb.GetData<BehaviorTreeTickDeltaTime>(); // get blackboard data by value (readonly)
        translation.Value += Velocity * deltaTime.Value;
        return NodeState.Running;
    }

    public void Reset<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard bb)
        where TNodeBlob : struct, INodeBlob
        where TBlackboard : struct, IBlackboard
    {}
}

// builder and editor part of node
public class EntityMove : BTNode<EntityMoveNode>
{
    public Vector3 Velocity;

    protected override void Build(ref EntityMoveNode data, BlobBuilder _, ITreeNode<INodeDataBuilder>[] __)
    {
        // set `NodeData` here
        data.Velocity = Velocity;
    }
}

// debug view (optional)
public class EntityMoveDebugView : BTDebugView<EntityMoveNode> {}

Decorator

// runtime behavior
[Serializable] // for debug view only
[BehaviorNode("A13666BD-48E3-414A-BD13-5C696F2EA87E", BehaviorNodeType.Decorate/*decorator must explicit declared*/)]
public struct RepeatForeverNode : INodeData
{
    public NodeState BreakStates;
    
    public NodeState Tick<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard blackboard)
        where TNodeBlob : struct, INodeBlob
        where TBlackboard : struct, IBlackboard
    {
        // short-cut to tick first only children
        var childState = blob.TickChildrenReturnFirstOrDefault(index, blackboard);
        if (childState == 0) // 0 means no child was ticked
                             // tick a already completed `Sequence` or `Selector` will return 0
        {
            blob.ResetChildren(index, blackboard);
            childState = blob.TickChildrenReturnFirstOrDefault(index, blackboard);
        }
        if (BreakStates.HasFlag(childState)) return childState;
        
        return NodeState.Running;
    }

    public void Reset<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard bb)
        where TNodeBlob : struct, INodeBlob
        where TBlackboard : struct, IBlackboard
    {}
}

// builder and editor
public class BTRepeat : BTNode<RepeatForeverNode>
{
    public NodeState BreakStates;
    
    public override void Build(ref RepeatForeverNode data, BlobBuilder _, ITreeNode<INodeDataBuilder>[] __)
    {
        data.BreakStates = BreakStates;
    }
}

// debug view (optional)
public class BTDebugRepeatForever : BTDebugView<RepeatForeverNode> {}

Composite

// runtime behavior
[StructLayout(LayoutKind.Explicit)] // sizeof(SelectorNode) == 0
[BehaviorNode("BD4C1D8F-BA8E-4D74-9039-7D1E6010B058", BehaviorNodeType.Composite/*composite must explicit declared*/)]
public struct SelectorNode : INodeData
{
    public NodeState Tick<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard blackboard)
        where TNodeBlob : struct, INodeBlob
        where TBlackboard : struct, IBlackboard
    {
        // tick children and break if child state is running or success.
        return blob.TickChildrenReturnLastOrDefault(index, blackboard, breakCheck: state => state.IsRunningOrSuccess());
    }

    public void Reset<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard bb)
        where TNodeBlob : struct, INodeBlob
        where TBlackboard : struct, IBlackboard
    {}
}

// builder and editor
public class BTSelector : BTNode<SelectorNode> {}

// avoid debug view since there's nothing need to be debug for `Selector`

EntityQuery

Behavior tree need some extra information for generating EntityQuery.

public struct SomeNode : INodeData
{
    // read-only access
    BlobVariantReader<int> IntVariable;
    
    // read-write access (there's no write-only access)
    BlobVariantWriter<float> FloatVariable;
    
    // read-write access
    BlobVariantReaderAndWriter<double> FloatVariable;

    // leave method attribute to be empty and will generate right access of this method
    public NodeState Tick<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard blackboard)
        where TNodeBlob : struct, INodeBlob
        where TBlackboard : struct, IBlackboard
    {
        // generate `[ReadOnly(typeof(ReadOnlyComponent)]` on `Tick` method
        bb.GetData<ReadOnlyComponent>();
        
        // generate `[ReadWrite(typeof(ReadWriteComponent)]` on `Tick` method
        bb.GetDataRef<ReadWriteComponent>();
        
        return NodeState.Success;
    }

    // or manually declare right access types for this method
    [EntitiesBT.Core.ReadWrite(typeof(ReadWriteComponentData))]
    [EntitiesBT.Core.ReadOnly(typeof(ReadOnlyComponentData))]
    public void Reset<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard bb)
        where TNodeBlob : struct, INodeBlob
        where TBlackboard : struct, IBlackboard
    {
        // generate `[ReadOnly(typeof(ReadOnlyComponent)]` on `Reset` method
        bb.GetData<ReadOnlyComponent>();
        
        // generate `[ReadWrite(typeof(ReadWriteComponent)]` on `Reset` method
        bb.GetDataRef<ReadWriteComponent>();
        
        // ...
    }
}

make sure to mark outside method call with right access attributes to generate right access type on Tick or Reset method of node

public static class Extension
{
    [ReadOnly(typeof(FooComponent)), ReadWrite(typeof(BarComponent))]
    public static void Call<[ReadWrite] T, [ReadOnly] U>([ReadOnly] Type type) { /* ... */ }
}

public struct SomeNode : INodeData
{
    // leave method attribute to be empty to generate automatically
    public NodeState Tick<TNodeBlob, TBlackboard>(int index, ref TNodeBlob blob, ref TBlackboard blackboard)
        where TNodeBlob : struct, INodeBlob
        where TBlackboard : struct, IBlackboard
    {
        // the following call will generate access attributes on `Tick` like below:
        // [ReadOnly(typeof(FooComponent))]
        // [ReadWrite(typeof(BarComponent))]
        // [ReadWrite(typeof(int))]
        // [ReadOnly(typeof(float))]
        // [ReadOnly(typeof(long))]
        Extension.Call<int, float>(typeof(long));
        return NodeState.Success;
    }
}

Advanced: automatically generate unity components from nodes

image
  1. Create a NodeComponentsGenerator scriptable object.
  2. Fill values of NodeComponentGenerator
  3. Click GenerateComponents in the menu of scriptable object.

Advanced: customize debug view

Advanced: access other node data

NodeBlob store all internal data of behavior tree, and it can be access from any node. To access specific node data, just store its index and access by INodeData.GetNodeData<T>(index).

Advanced: behavior tree component

[BehaviorTreeComponent] // mark a component data as `BehaviorTreeComponent`
public struct BehaviorTreeTickDeltaTime : IComponentData
{
    public float Value;
}

[UpdateBefore(typeof(VirtualMachineSystem))]
public class BehaviorTreeDeltaTimeSystem : ComponentSystem
{
    protected override void OnUpdate()
    {
        Entities.ForEach((ref BehaviorTreeTickDeltaTime deltaTime) => deltaTime.Value = Time.DeltaTime);
    }
}

The components of behavior will add into Entity automatically on the stage of convert GameObject to Entity, if AutoAddBehaviorTreeComponents is enabled.

Advanced: virtual node builder

A single builder node is able to product multiple behavior nodes while building.

public class BTSequence : BTNode<SequenceNode>
{
    [Tooltip("Enable this will re-evaluate node state from first child until running node instead of skip to running node directly.")]
    [SerializeField] private bool _recursiveResetStatesBeforeTick;

    public override INodeDataBuilder Self => _recursiveResetStatesBeforeTick
        // add `RecursiveResetStateNode` as parent of `this` node
        ? new BTVirtualDecorator<RecursiveResetStateNode>(this)
        : base.Self
    ;
}

Data Structure

public struct NodeBlob
{
    // default data (serializable data)
    public BlobArray<int> Types; // type id of behavior node, generated from `Guid` of `BehaviorNodeAttribute`
    public BlobArray<int> EndIndices; // range of node branch must be in [nodeIndex, nodeEndIndex)
    public BlobArray<int> Offsets; // data offset of `DefaultDataBlob` of this node
    public BlobArray<byte> DefaultDataBlob; // nodes data
    
    // runtime only data (only exist on runtime)
    public BlobArray<NodeState> States; // nodes states
    // initialize from `DefaultDataBlob`
    public BlobArray<byte> RuntimeDataBlob; // same as `DefaultNodeData` but only available at runtime and will reset to `DefaultNodeData` once reset.
}
data-structure
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].