using System;
using System.Collections.Generic;
using UnityEngine;
namespace SynapticPro.BehaviorTree
{
///
/// Node execution status
///
public enum BTStatus
{
Success,
Failure,
Running
}
///
/// Base class for all Behavior Tree nodes
///
public abstract class BTNode
{
public string Name { get; set; }
public BTNode Parent { get; protected set; }
public bool IsRunning { get; protected set; }
///
/// Context data shared across the tree
///
public BTContext Context { get; set; }
protected BTNode(string name = "Node")
{
Name = name;
}
///
/// Execute this node
///
public abstract BTStatus Tick();
///
/// Reset node state
///
public virtual void Reset()
{
IsRunning = false;
}
///
/// Called when node is aborted
///
public virtual void Abort()
{
IsRunning = false;
}
///
/// Set parent node
///
internal void SetParent(BTNode parent)
{
Parent = parent;
}
}
///
/// Base class for nodes with children
///
public abstract class BTComposite : BTNode
{
protected List children = new List();
protected int currentChildIndex = 0;
public IReadOnlyList Children => children;
protected BTComposite(string name = "Composite") : base(name) { }
///
/// Add a child node
///
public BTComposite AddChild(BTNode child)
{
if (child != null)
{
child.SetParent(this);
child.Context = Context;
children.Add(child);
}
return this;
}
///
/// Add multiple children
///
public BTComposite AddChildren(params BTNode[] nodes)
{
foreach (var node in nodes)
{
AddChild(node);
}
return this;
}
///
/// Remove a child node
///
public void RemoveChild(BTNode child)
{
if (child != null)
{
child.SetParent(null);
children.Remove(child);
}
}
///
/// Clear all children
///
public void ClearChildren()
{
foreach (var child in children)
{
child.SetParent(null);
}
children.Clear();
currentChildIndex = 0;
}
public override void Reset()
{
base.Reset();
currentChildIndex = 0;
foreach (var child in children)
{
child.Reset();
}
}
public override void Abort()
{
base.Abort();
foreach (var child in children)
{
child.Abort();
}
}
}
///
/// Base class for decorator nodes (single child)
///
public abstract class BTDecorator : BTNode
{
protected BTNode child;
public BTNode Child => child;
protected BTDecorator(string name = "Decorator") : base(name) { }
///
/// Set the child node
///
public BTDecorator SetChild(BTNode node)
{
if (child != null)
{
child.SetParent(null);
}
child = node;
if (child != null)
{
child.SetParent(this);
child.Context = Context;
}
return this;
}
public override void Reset()
{
base.Reset();
child?.Reset();
}
public override void Abort()
{
base.Abort();
child?.Abort();
}
}
///
/// Shared context data for the behavior tree
///
public class BTContext
{
private Dictionary data = new Dictionary();
///
/// The GameObject this tree is attached to
///
public GameObject GameObject { get; set; }
///
/// The Transform of the GameObject
///
public Transform Transform => GameObject?.transform;
///
/// Set a value in the context
///
public void Set(string key, T value)
{
data[key] = value;
}
///
/// Get a value from the context
///
public T Get(string key, T defaultValue = default)
{
if (data.TryGetValue(key, out var value) && value is T typedValue)
{
return typedValue;
}
return defaultValue;
}
///
/// Check if a key exists
///
public bool Has(string key)
{
return data.ContainsKey(key);
}
///
/// Remove a value
///
public void Remove(string key)
{
data.Remove(key);
}
///
/// Clear all data
///
public void Clear()
{
data.Clear();
}
}
}