2023年7月7日发(作者:)
C#Unity⾏为树简单实现孙⼴东 2018.7.25
⾏为树的概念, 各种 Unity 插件 都没时间介绍。 代码是看了 Unity的 2D Game Kit , 提炼出来,测试! 对于学习和理解⾏为树会很有帮助 纯代码, 没有Unity插件的节点编辑导出配置等内容。 using BTAI;using UnityEngine;public class TestBT : MonoBehaviour, ugable{ Root aiRoot = (); private void OnEnable() { anch( (TestVisibleTarget).OpenBranch( (Aim), (Shoot) ), ce().OpenBranch( (Walk), (5.0f), (Turn), (1.0f), (Turn) ) ); } private void Turn() { ("执⾏了 Turn"); } private void Walk() { ("执⾏了 Walk"); } private void Shoot() { ("执⾏了 Shoot"); } private void Aim() { ("执⾏了 Aim"); } private bool TestVisibleTarget() { var isSuccess = (0, 2) == 1; ("执⾏了 TestVisibleTarget Result:" + isSuccess); return isSuccess; } private void Update() { (); } public Root GetAIRoot() { return aiRoot; }}using c;using UnityEngine;/// /// 这只是脚本系统/// ⾏为树会从Root节点开始遍历⼦节点。Update中执⾏/// 每个节点都有相关的操作,但是基本上就是返回三种状态/// ● Success: 节点成功完成任务/// ● Failure: 节点未通过任务/// ● Continue:节点尚未完成任务。/// 但是每个节点的⽗节点对⼦节点的结果处理⽅式还不同。 例如/// ● Test 节点: 测试节点将调⽤其⼦节点并在测试为真时返回⼦节点状态,如果测试为假,则返回Failure⽽不调⽤其⼦节点。/// ⾏为树的⼀种构造⽅式如下:/// Root aiRoot = ();
/// (
/// (TestVisibleTarget).Do(/// (Aim),/// (Shoot)/// ),/// ce().Do(/// (Walk),/// (5.0f),/// (Turn),/// (1.0f),/// (Turn)/// )/// );
///然后在Update中 调⽤ () 。 刚刚构造的⾏为树是怎么样的检查过程呢?
///1、⾸先检查TestVisibleTarget是否返回Ture,如果是继续执⾏⼦节点执⾏Aim函数和Shoot函数///2、TestVisibleTarget是否返回false,if节点将返回Failure, 然后Root 将转向下⼀个⼦节点。这是个Sequence节点,它从执⾏第⼀个⼦节点开始。/// 1)将调⽤Walk函数,直接返回 Success,以便Sequence将下⼀个⼦节点激活并执⾏它。///3、Update的执⾏,当Wait节点等待的时间到了的时候,将会返回Success, 以便序列将转到下⼀个孩⼦。///脚本中的Node列表
/// Sequence://⼀个接⼀个地执⾏⼦节点。如果⼦节点返回://●Success:Sequence将选择下⼀帧的下⼀个孩⼦开始。//●Failure:Sequence将返回到下⼀帧的第⼀个⼦节点(从头开始)。//●Continue:Sequence将在下⼀帧再次调⽤该节点。//RandomSequence:// 每次调⽤时,从⼦列表中执⾏⼀个随机⼦节点。您可以在构造函数中指定要应⽤于每个⼦项的权重列表作为int数组,以使某些⼦项更有可能被选中。//Selector ://按顺序执⾏所有⼦项,直到⼀个返回Success,然后退出⽽不执⾏其余⼦节点。如果没有返回Success,则此节点将返回Failure。// Condition :/// 2)执⾏Wait 节点,只是要等待5秒,还是第⼀次调⽤,所以肯定返回Running状态, 当Sequence从⼦节点上得到Running状态时,不会更改激活的⼦节点索引,下// Condition :// 如果给定函数返回true,则此节点返回Success;如果为false,则返回Failure。// 与其他依赖于⼦节点结果的节点链接时很有⽤(例如,Sequence,Selector等)// If ://调⽤给定的函数。// ●如果返回true,则调⽤当前活动的⼦级并返回其状态。// ●否则,它将在不调⽤其⼦项的情况下返回Failure// While://只要给定函数返回true,就返回Continue(因此,下⼀帧将再次从该节点开始,⽽不会评估所有先前的节点)。//⼦节点们将陆续被执⾏。//当函数返回false并且循环中断时,将返回Failure。// Call
//调⽤给定的函数,它将始终返回Success。是动作节点!//Repeat
//将连续执⾏给定次数的所有⼦节点。//始终返回Continue,直到达到计数,并返回Success。//Wait//将返回Continue,直到达到给定时间(⾸次调⽤时开始),然后返回Success。//Trigger
//允许在给定的动画师animator中设置Trigger参数(如果最后⼀个参数设置为false,则取消设置触发器)。始终返回成功。//SetBool//允许在给定的animator中设置布尔参数的值。始终返回成功//SetActive
//设置给定GameObject的活动/⾮活动状态。始终返回成功。/// namespace BTAI{ public enum BTState { Failure, Success, Continue, Abort } /// /// 节点 对象⼯⼚ /// public static class BT { public static Root Root() { return new Root(); } public static Sequence Sequence() { return new Sequence(); } public static Selector Selector(bool shuffle = false) { return new Selector(shuffle); } public static Action RunCoroutine(> coroutine) { return new Action(coroutine); } public static Action Call( fn) { return new Action(fn); } public static ConditionalBranch If( fn) { return new ConditionalBranch(fn); } public static While While( fn) { return new While(fn); } public static Condition Condition( fn) { return new Condition(fn); } public static Repeat Repeat(int count) { return new Repeat(count); } public static Wait Wait(float seconds) { return new Wait(seconds); } public static Trigger Trigger(Animator animator, string name, bool set = true) { return new Trigger(animator, name, set); } public static SetBool SetBool(Animator animator, string name, bool value) { return new SetBool(animator, name, value); } public static SetActive SetActive(GameObject gameObject, bool active) { return new SetActive(gameObject, active); } public static Terminate Terminate() { return new Terminate(); } public static Log Log(string msg) { return new Log(msg); } public static RandomSequence RandomSequence(int[] weights = null) { return new Sequence(weights); } } /// /// 节点抽象类 /// public abstract class BTNode { public static WaitForAnimatorState WaitForAnimatorState(Animator animator, string name, int layer = 0) { return new WaitForAnimatorState(animator, name, public static WaitForAnimatorSignal WaitForAnimatorSignal(Animator animator, string name, string state, int layer = 0) { return new WaitForAnimatorSignal(a { public abstract BTState Tick(); } /// /// 包含⼦节点的组合 节点基类 /// public abstract class Branch : BTNode { protected int activeChild; protected List children = new List(); public virtual Branch OpenBranch(params BTNode[] children) { for (var i = 0; i < ; i++) (children[i]); return this; } public List Children() { return children; } public int ActiveChild() { return activeChild; } public virtual void ResetChildren() { activeChild = 0; for (var i = 0; i < ; i++) { Branch b = children[i] as Branch; if (b != null) { hildren(); } } } } /// /// 装饰节点 只包含⼀个⼦节点,⽤于某种⽅式改变这个节点的⾏为 /// 或者 Limit 节点,⽤于指定某个⼦节点的最⼤运⾏次数 /// 或者 Timer节点,设置了⼀个计时器,不会⽴即执⾏⼦节点,⽽是等⼀段时间,时间到了开始执⾏⼦节点 /// 或者 TimerLimit节点,⽤于指定某个⼦节点的最长运⾏时间。 /// 或者 ⽤于产⽣某个返回状态, /// public abstract class Decorator : BTNode { protected BTNode child; public Decorator Do(BTNode child) { = child; return this; } } /// /// 顺序节点 (从左到右依次执⾏所有⼦节点,只要⼦节点返回Success就继续执⾏后续⼦节点,直到遇到Failure或者Runing,
/// 停⽌后续执⾏,并把这个节点返回给⽗节点,只有它的所有⼦节点都是Success他才会向⽗节点返回Success) /// public class Sequence : Branch { /// ⽐如过滤器(⽤于决定是否允许⼦节点运⾏的,如:Until Success, Until Fail等),这种节点的⼦节点应该是条件节点,条件节点⼀直检测“视线中是否有敌⼈”,知 { public override BTState Tick() { var childState = children[activeChild].Tick(); switch (childState) { case s: activeChild++; if (activeChild == ) { activeChild = 0; return s; } else return ue; case e: activeChild = 0; return e; case ue: return ue; case : activeChild = 0; return ; } throw new ion("This should never happen, but clearly it has."); } } /// /// 所有⼦节点都是Fail, 那么向⽗节点凡湖Fail /// 利⽤选择节点,他会尝试寻找Cover,失败后在试图逃离危险区域。 /// public class Selector : Branch { public Selector(bool shuffle) { if (shuffle) { var n = ; while (n > 1) { n--; var k = oInt( * (n + 1)); var value = children[k]; children[k] = children[n]; children[n] = value; } } } public override BTState Tick() { var childState = children[activeChild].Tick(); switch (childState) { case s: activeChild = 0; return s; case e: activeChild++; if (activeChild == ) { activeChild = 0; return e; /// 选择节点从左到右依次执⾏所有⼦节点 ,只要遇到failure就继续执⾏后续⼦节点,直到遇到⼀个节点返回Success或Running为⽌。向⽗节点返回Success或Runn /// 选择节点 ⽤来在可能的⾏为集合中选择第⼀个成功的。 ⽐如⼀个试图躲避枪击的AI⾓⾊, 它可以通过寻找隐蔽点, 或离开危险区域, 或寻找援助等多种⽅式实 return e; } else return ue; case ue: return ue; case : activeChild = 0; return ; } throw new ion("This should never happen, but clearly it has."); } } /// /// ⾏为节点 调⽤⽅法,或运⾏协程。完成实际⼯作, 例如播放动画,让⾓⾊移动位置,感知敌⼈,更换武器,播放声⾳,增加⽣命值等。 /// public class Action : BTNode { fn; > coroutineFactory; IEnumerator coroutine; public Action( fn) { = fn; } public Action(> coroutineFactory) { ineFactory = coroutineFactory; } public override BTState Tick() { if (fn != null) { fn(); return s; } else { if (coroutine == null) coroutine = coroutineFactory(); if (!xt()) { coroutine = null; return s; } var result = t; if (result == ue) return ue; else { coroutine = null; return result; } } } public override string ToString() { return "Action : " + ng(); } } /// /// 条件节点 调⽤⽅法,如果⽅法返回true则返回成功,否则返回失败。 /// ⽤来测试当前是否满⾜某些性质或条件,例如“玩家是否在20⽶之内?”“是否能看到玩家?”“⽣命值是否⼤于50?”“弹药是否⾜够?”等 /// ⽤来测试当前是否满⾜某些性质或条件,例如“玩家是否在20⽶之内?”“是否能看到玩家?”“⽣命值是否⼤于50?”“弹药是否⾜够?”等 /// public class Condition : BTNode { public fn; public Condition( fn) { = fn; } public override BTState Tick() { return fn() ? s : e; } public override string ToString() { return "Condition : " + ng(); } } /// /// 当⽅法为True的时候 尝试执⾏当前 ⼦节点 /// public class ConditionalBranch : Block { public fn; bool tested = false; public ConditionalBranch( fn) { = fn; } public override BTState Tick() { if (!tested) { tested = fn(); } if (tested) { // 当前⼦节点执⾏完就进⼊下⼀个节点(超上限就返回到第⼀个) var result = (); // 没执⾏完 if (result == ue) return ue; else { tested = false; // 最后⼀个⼦节点执⾏完,才会为Ture return result; } } else { return e; } } public override string ToString() { return "ConditionalBranch : " + ng(); } } /// /// While节点 只要⽅法 返回True 就执⾏所有⼦节点, 否则返回 Failure /// While节点 只要⽅法 返回True 就执⾏所有⼦节点, 否则返回 Failure /// public class While : Block { public fn; public While( fn) { = fn; } public override BTState Tick() { if (fn()) (); else { //if we exit the loop ResetChildren(); return e; } return ue; } public override string ToString() { return "While : " + ng(); } } /// /// 阻塞节点 如果当前⼦节点是Continue 说明没有执⾏完,阻塞着,执⾏完之后在继续它后⾯的兄弟节点 不管成功失败。 /// 如果当前结点是最后⼀个节点并执⾏完毕,说明成功!否则就是处于Continue状态。
/// ⼏个基本上是抽象节点, 像是让所有⼦节点都执⾏⼀遍, 当前⼦节点执⾏完就进⼊下⼀个节点(超上限就返回到第⼀个) /// public abstract class Block : Branch { public override BTState Tick() { switch (children[activeChild].Tick()) { case ue: return ue; default: activeChild++; if (activeChild == ) { activeChild = 0; return s; } return ue; } } } public class Root : Block { public bool isTerminated = false; public override BTState Tick() { if (isTerminated) return ; while (true) { switch (children[activeChild].Tick()) { case ue: return ue; case : isTerminated = true; return ; default: activeChild++; if (activeChild == ) { activeChild = 0; return s; } continue; } } } } /// /// 多次运⾏⼦节点(⼀个⼦节点执⾏⼀次就算⼀次) /// public class Repeat : Block { public int count = 1; int currentCount = 0; public Repeat(int count) { = count; } public override BTState Tick() { if (count > 0 && currentCount < count) { var result = (); switch (result) { case ue: return ue; default: currentCount++; if (currentCount == count) { currentCount = 0; return s; } return ue; } } return s; } public override string ToString() { return "Repeat Until : " + currentCount + " / " + count; } } /// /// 随机的顺序 执⾏⼦节点
/// public class RandomSequence : Block { int[] m_Weight = null; int[] m_AddedWeight = null; /// /// 每次再次触发时,将选择⼀个随机⼦节点 /// /// 保留null,以便所有⼦节点具有相同的权重。 /// 如果权重低于⼦节点, 则后续⼦节点的权重都为1 public RandomSequence(int[] weight = null) { activeChild = -1; m_Weight = weight; } public override Branch OpenBranch(params BTNode[] children) { m_AddedWeight = new int[]; for (int i = 0; i < ; ++i) { int weight = 0; int previousWeight = 0; if (m_Weight == null || m_ <= i) {//如果没有那个权重, 就将权重 设置为1 weight = 1; } else weight = m_Weight[i]; if (i > 0) previousWeight = m_AddedWeight[i - 1]; m_AddedWeight[i] = weight + previousWeight; } return anch(children); } public override BTState Tick() { if (activeChild == -1) PickNewChild(); var result = children[activeChild].Tick(); switch (result) { case ue: return ue; default: PickNewChild(); return result; } } void PickNewChild() { int choice = (0, m_AddedWeight[m_ - 1]); for (int i = 0; i < m_; ++i) { if (choice - m_AddedWeight[i] <= 0) { activeChild = i; break; } } } public override string ToString() { return "Random Sequence : " + activeChild + "/" + ; } } /// /// 暂停执⾏⼏秒钟。 /// public class Wait : BTNode { public float seconds = 0; float future = -1; public Wait(float seconds) { s = seconds; } public override BTState Tick() { if (future < 0) future = + seconds; if ( >= future) { future = -1; return s; } else return ue; } public override string ToString() { return "Wait : " + (future - ) + " / " + seconds; } } /// /// 设置动画 trigger 参数 /// public class Trigger : BTNode { Animator animator; int id; string triggerName; bool set = true; //如果 set == false, 则重置trigger⽽不是设置它。 public Trigger(Animator animator, string name, bool set = true) { = ToHash(name); or = animator; rName = name; = set; } public override BTState Tick() { if (set) gger(id); else rigger(id); return s; } public override string ToString() { return "Trigger : " + triggerName; } } /// /// 设置动画 boolean 参数 /// public class SetBool : BTNode { Animator animator; int id; bool value; string triggerName; public SetBool(Animator animator, string name, bool value) { = ToHash(name); or = animator; = value; rName = name; } public override BTState Tick() { l(id, value); return s; } public override string ToString() { return "SetBool : " + triggerName + " = " + ng(); } } /// /// 等待animator达到⼀个状态。 /// public class WaitForAnimatorState : BTNode { Animator animator; int id; int layer; string stateName; public WaitForAnimatorState(Animator animator, string name, int layer = 0) { = ToHash(name); if (!te(layer, )) { or("The animator does not have state: " + name); } or = animator; = layer; ame = name; } public override BTState Tick() { var state = rentAnimatorStateInfo(layer); if (thHash == || ameHash == ) return s; return ue; } public override string ToString() { return "Wait For State : " + stateName; } } /// /// 设置 GameObject 的激活状态 /// public class SetActive : BTNode { GameObject gameObject; bool active; public SetActive(GameObject gameObject, bool active) { ject = gameObject; = active; } public override BTState Tick() { ive(); return s; } public override string ToString() { return "Set Active : " + + " = " + active; } } /// /// 等待animator从SendSignal状态机⾏为 接收信号。 SendSignal : StateMachineBehaviour /// public class WaitForAnimatorSignal : BTNode { // 进⼊或退出动画都为 False, 只有执⾏中为True internal bool isSet = false; string name; int id; public WaitForAnimatorSignal(Animator animator, string name, string state, int layer = 0) { = name; = ToHash(name); if (!te(layer, )) { or("The animator does not have state: " + name); } else { er(animator, name, this); } } public override BTState Tick() { if (!isSet) return ue; else { isSet = false; return s; } } public override string ToString() { return "Wait For Animator Signal : " + name; } } /// /// 终⽌节点 切换到中⽌ 状态 /// public class Terminate : BTNode { public override BTState Tick() { return ; } } /// /// Log 输出Log 的节点 /// public class Log : BTNode { string msg; public Log(string msg) { = msg; } public override BTState Tick() { (msg); return s; } }}#if UNITY_EDITORnamespace BTAI{ public interface IBTDebugable { Root GetAIRoot(); }}#endif
using BTAI;using c;using UnityEditor;using UnityEngine;namespace Gamekit2D{ /// /// 运⾏是查看 ⾏为树中所有节点的状态
/// public class BTDebug : EditorWindow { protected _currentRoot = null; [MenuItem("Kit Tools/Behaviour Tree Debug")] static void OpenWindow() { BTDebug btdebug = GetWindow(); (); } private void OnGUI() { if (!ing) { x("Only work during play mode.", ); } else { if (_currentRoot == null) FindRoot(); else { RecursiveTreeParsing(_currentRoot, 0, true); } } } void Update() { Repaint(); } void RecursiveTreeParsing(Branch branch, int indent, bool parentIsActive) { List nodes = en(); for (int i = 0; i < ; ++i) { Level = indent; bool isActiveChild = Child() == i; = (isActiveChild && parentIsActive) ? : ; ield(nodes[i].ToString()); if (nodes[i] is Branch) RecursiveTreeParsing(nodes[i] as Branch, indent + 1, isActiveChild); } } } void FindRoot() { if (GameObject == null) { _currentRoot = null; return; } IBTDebugable debugable = ponentInChildren(); if (debugable != null) { _currentRoot = oot(); } } }} 就是在 菜单 “Kit Tools/Behaviour Tree Debug" 可以查看 对象所在⾏为树
发布者:admin,转转请注明出处:http://www.yc00.com/news/1688702575a163802.html
评论列表(0条)