// GDnD wiki example // Demonstrates: a minimal behaviour tree framework (status, node, sequence, selector, inverter, condition and action leaves) // Related pages: [[behaviour-trees]], [[ai-state-machine-pattern]], [[game-ai-agent-design]] using System; using System.Collections.Generic; // Every node answers one question when ticked: "how did that go?" public enum NodeState { Success, Failure, Running } // Plain C# classes, not MonoBehaviours: the agent owns the tree and ticks it. public abstract class BTNode { public string Name { get; } protected BTNode(string name) => Name = name; public abstract NodeState Tick(); } // AND: runs children in order and stops at the first one that does not succeed. // Use it for "check this, then do that" steps. public sealed class SequenceNode : BTNode { private readonly List children; public SequenceNode(string name, params BTNode[] children) : base(name) => this.children = new List(children); public override NodeState Tick() { foreach (BTNode child in children) { NodeState result = child.Tick(); if (result != NodeState.Success) return result; // Failure or Running stops the sequence here } return NodeState.Success; } } // OR: tries children in priority order and stops at the first one that does not fail. // Put the most urgent branch first. public sealed class SelectorNode : BTNode { private readonly List children; public SelectorNode(string name, params BTNode[] children) : base(name) => this.children = new List(children); public override NodeState Tick() { foreach (BTNode child in children) { NodeState result = child.Tick(); if (result != NodeState.Failure) return result; // Success or Running stops the selector here } return NodeState.Failure; } } // Decorator: flips Success and Failure; Running passes through unchanged. public sealed class InverterNode : BTNode { private readonly BTNode child; public InverterNode(string name, BTNode child) : base(name) => this.child = child; public override NodeState Tick() { NodeState result = child.Tick(); if (result == NodeState.Success) return NodeState.Failure; if (result == NodeState.Failure) return NodeState.Success; return NodeState.Running; } } // Leaf: asks a yes/no question about the world. Never Running. public sealed class ConditionNode : BTNode { private readonly Func test; public ConditionNode(string name, Func test) : base(name) => this.test = test; public override NodeState Tick() => test() ? NodeState.Success : NodeState.Failure; } // Leaf: does one tick's worth of work and reports how it is going. public sealed class ActionNode : BTNode { private readonly Func act; public ActionNode(string name, Func act) : base(name) => this.act = act; public override NodeState Tick() => act(); }