You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I managed to do this approach when you code a behavior tree you don't have to call End(), and you don't need to do any manual formatting.
The concept is simple, you just need to add this wrapper class to the standard version of Fluid Behavior Tree:
(I may have changed some task names but the concept is really simple, so you can change this script below to fit your own tasks easily)
using System;
using CleverCrow.Fluid.BTs.Trees;
using CleverCrow.Fluid.BTs.Tasks;
using UnityEngine;
namespace BehaviorTreeFormatter
{
// DSL wrapper to allow block-style (curly-brace) formatting for behavior trees.
public class BehaviorTreeFormatter
{
private BehaviorTreeBuilder Builder;
public BehaviorTreeFormatter(GameObject owner, string name)
{
Builder = new BehaviorTreeBuilder(owner, name);
}
public BehaviorTree Build()
{
return Builder.Build();
}
public void Selector(string name, Action block)
{
Builder.Selector(name);
block();
End();
}
public void Sequence(string name, Action block)
{
Builder.Sequence(name);
block();
End();
}
public void Simultaneous(int successesToStop, int failuresToStop, int continuousToStop, string name,
Action block)
{
Builder.Simultaneous(successesToStop, failuresToStop, continuousToStop, name);
block();
End();
}
public void Decorator(string name, Func<ITask, TaskStatus> logic, Action block)
{
Builder.Decorator(name, logic);
block();
End();
}
public void While(string name, Func<ITask, TaskStatus> logic, Action block)
{
Builder.While(name, logic);
block();
End();
}
public void Condition(string name, Func<bool> condition)
{
Builder.Condition(name, condition);
}
public void Do(string name, Func<TaskStatus> action)
{
Builder.Do(name, action);
}
public void ReturnSuccess(string name, Action block)
{
Builder.ReturnSuccess(name);
block();
End();
}
public void ReturnFailure(string name, Action _block)
{
Builder.ReturnFailure(name);
_block();
End();
}
public void AddNode(ITask node, string name = null)
{
Builder.AddNode(node, name);
}
public void ResetTree()
{
Builder.ResetTree();
}
private void End()
{
Builder.End();
}
}
public static class BT
{
public static BehaviorTree CreateTree(GameObject owner, string name, Action<BehaviorTreeFormatter> buildAction)
{
var dsl = new BehaviorTreeFormatter(owner, name);
buildAction(dsl);
return dsl.Build();
}
}
}
Here's how it looks like when using the approach from the wrapper class above in an implementation.
var behaviorTree = BT.CreateTree(gameObject, nameof(Tree_Ally_Fighter), bt =>
{
bt.Selector("Root Selector", () =>
{
bt.Condition("Is Disabled?", IsDisabled);
bt.Selector("Bhv Selector", () =>
{
bt.Sequence("Skill command", () =>
{
bt.Condition("Is Skill Commanded?", IsSkillCommanded);
bt.ReturnFailure("Return failure on success", () => { bt.AddNode(Node_DoSkillCommand()); });
});
bt.Sequence("Attack command", () =>
{
bt.Condition("Is AttackMove Commanded?", IsAttackMoveCommanded);
bt.Simultaneous(1, -1, -1, "Simultaneous run", () =>
{
bt.Do("Search for new targets", () =>
{
var targetToAttack = GetTargetToAttack(transform.position);
CombatTracker.Attack(targetToAttack);
return TaskStatus.Continue;
});
bt.Selector("Attack, move or exit", () =>
{
bt.Decorator("Stop if no attack target", _task =>
{
if (CombatTracker.HasAttackTarget)
{
var result = _task.Update();
if (result == TaskStatus.Success) return TaskStatus.Failure;
return result;
}
else
{
return TaskStatus.Failure;
}
}, () => { bt.AddNode(Node_DoUnitCombat()); });
bt.Decorator("Stop when has attack target", _task =>
{
if (!CombatTracker.HasAttackTarget)
{
var result = _task.Update();
if (result == TaskStatus.Success) return TaskStatus.Failure;
return result;
}
else
{
return TaskStatus.Failure;
}
}, () => { bt.AddNode(Node_DoMoveCommand()); });
bt.Condition("Reached final destination",
() => Pather.FinishedPathing());
});
});
});
});
});
});
The text was updated successfully, but these errors were encountered:
JeremyVansnick
changed the title
Make the behavior tree creation more readable / easy
Make the behavior tree creation more readable
Feb 1, 2025
The usage example on this looks really good and is far more readable. I'll have to prototype out some changes. But I'm thinking the old commands should be marked deprecated and this should be put in as a new feature for feedback. I'll have to find some time to give this a whirl and prototype out a few things. It all looks good in your code though.
I'm really enjoying using this approach so far! It's much easier to use.
I agree it's best to deprecate the old approach.
Now when working on a unit AI, I no longer need to enter runtime just to see how the tree looks like or manage these complex indentations - I can fully concentrate on the behavior of the tree without losing focus. What a joy!
I managed to do this approach when you code a behavior tree you don't have to call End(), and you don't need to do any manual formatting.
The concept is simple, you just need to add this wrapper class to the standard version of Fluid Behavior Tree:
(I may have changed some task names but the concept is really simple, so you can change this script below to fit your own tasks easily)
Here's how it looks like when using the approach from the wrapper class above in an implementation.
The text was updated successfully, but these errors were encountered: