C#/基于Unity 行为树的实现步骤【纯代码】

互联网 18-7-26
以下是纯代码参考, 没有Unity插件的节点编辑导出配置等内容。 行为树的概念, 各种 Unity 插件在这里都没有进行介绍。 代码是根据Unity的 2D Game Kit ,总结提炼的, 对于学习和理解行为树会很有帮助!
using BTAI; using UnityEngine;  public class TestBT : MonoBehaviour, BTAI.IBTDebugable {     Root aiRoot = BT.Root();       private void OnEnable()     {         aiRoot.OpenBranch(                 BT.If(TestVisibleTarget).OpenBranch(                 BT.Call(Aim),                 BT.Call(Shoot)                  ),                 BT.Sequence().OpenBranch(                 BT.Call(Walk),                 BT.Wait(5.0f),                 BT.Call(Turn),                 BT.Wait(1.0f),                 BT.Call(Turn)              )         );     }      private void Turn()     {         Debug.Log("执行了 Turn");     }      private void Walk()     {         Debug.Log("执行了 Walk");     }      private void Shoot()     {         Debug.Log("执行了 Shoot");     }      private void Aim()     {         Debug.Log("执行了 Aim");     }      private bool TestVisibleTarget()     {         var isSuccess = UnityEngine.Random.Range(0, 2) == 1;         Debug.Log("执行了 TestVisibleTarget    Result:" + isSuccess);          return isSuccess;     }      private void Update()     {         aiRoot.Tick();     }      public Root GetAIRoot()     {         return aiRoot;     } }

using System.Collections.Generic; using UnityEngine;  /// <summary> /// 这只是脚本系统 /// 行为树会从Root节点开始遍历子节点。Update中执行 /// 每个节点都有相关的操作,但是基本上就是返回三种状态 /// ● Success: 节点成功完成任务 /// ● Failure: 节点未通过任务 /// ● Continue:节点尚未完成任务。 /// 但是每个节点的父节点对子节点的结果处理方式还不同。  例如 /// ● Test 节点: 测试节点将调用其子节点并在测试为真时返回子节点状态,如果测试为假,则返回Failure而不调用其子节点。 /// 行为树的一种构造方式如下: /// Root aiRoot = BT.Root();  /// aiRoot.Do(   /// BT.If(TestVisibleTarget).Do( ///  BT.Call(Aim), ///  BT.Call(Shoot) ///  ), ///  BT.Sequence().Do( ///  BT.Call(Walk), ///  BT.Wait(5.0f), ///  BT.Call(Turn), ///  BT.Wait(1.0f), ///  BT.Call(Turn) ///  ) /// );  ///然后在Update中 调用   aiRoot.Tick() 。  刚刚构造的行为树是怎么样的检查过程呢?   ///1、首先检查TestVisibleTarget是否返回Ture,如果是继续执行子节点执行Aim函数和Shoot函数 ///2、TestVisibleTarget是否返回false,if节点将返回Failure, 然后Root 将转向下一个子节点。这是个Sequence节点,它从执行第一个子节点开始。 ///   1)将调用Walk函数,直接返回 Success,以便Sequence将下一个子节点激活并执行它。 ///   2)执行Wait 节点,只是要等待5秒,还是第一次调用,所以肯定返回Running状态, 当Sequence从子节点上得到Running状态时,不会更改激活的子节点索引,下次Update的时候还是从这个节点开始执行 ///3、Update的执行,当Wait节点等待的时间到了的时候,将会返回Success, 以便序列将转到下一个孩子。 ///脚本中的Node列表  /// Sequence: //一个接一个地执行子节点。如果子节点返回: //●Success:Sequence将选择下一帧的下一个孩子开始。 //●Failure:Sequence将返回到下一帧的第一个子节点(从头开始)。 //●Continue:Sequence将在下一帧再次调用该节点。 //RandomSequence: // 每次调用时,从子列表中执行一个随机子节点。您可以在构造函数中指定要应用于每个子项的权重列表作为int数组,以使某些子项更有可能被选中。 //Selector : //按顺序执行所有子项,直到一个返回Success,然后退出而不执行其余子节点。如果没有返回Success,则此节点将返回Failure。  // 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的活动/非活动状态。始终返回成功。 /// </summary> namespace BTAI {     public enum BTState     {         Failure,         Success,         Continue,         Abort     }      /// <summary>     /// 节点 对象工厂     /// </summary>     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(System.Func<IEnumerator<BTState>> coroutine) { return new Action(coroutine); }         public static Action Call(System.Action fn) { return new Action(fn); }         public static ConditionalBranch If(System.Func<bool> fn) { return new ConditionalBranch(fn); }         public static While While(System.Func<bool> fn) { return new While(fn); }         public static Condition Condition(System.Func<bool> 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 WaitForAnimatorState WaitForAnimatorState(Animator animator, string name, int layer = 0) { return new WaitForAnimatorState(animator, name, layer); }         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 WaitForAnimatorSignal WaitForAnimatorSignal(Animator animator, string name, string state, int layer = 0) { return new WaitForAnimatorSignal(animator, name, state, layer); }         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 BTAI.RandomSequence(weights); }      }      /// <summary>     /// 节点抽象类     /// </summary>     public abstract class BTNode     {         public abstract BTState Tick();     }      /// <summary>     /// 包含子节点的组合 节点基类     /// </summary>     public abstract class Branch : BTNode     {         protected int activeChild;         protected List<BTNode> children = new List<BTNode>();         public virtual Branch OpenBranch(params BTNode[] children)         {             for (var i = 0; i < children.Length; i++)                 this.children.Add(children[i]);             return this;         }          public List<BTNode> Children()         {             return children;         }          public int ActiveChild()         {             return activeChild;         }          public virtual void ResetChildren()         {             activeChild = 0;             for (var i = 0; i < children.Count; i++)             {                 Branch b = children[i] as Branch;                 if (b != null)                 {                     b.ResetChildren();                 }             }         }     }      /// <summary>     /// 装饰节点    只包含一个子节点,用于某种方式改变这个节点的行为     /// 比如过滤器(用于决定是否允许子节点运行的,如:Until Success, Until Fail等),这种节点的子节点应该是条件节点,条件节点一直检测“视线中是否有敌人”,知道发现敌人为止。     /// 或者 Limit 节点,用于指定某个子节点的最大运行次数     /// 或者 Timer节点,设置了一个计时器,不会立即执行子节点,而是等一段时间,时间到了开始执行子节点     /// 或者 TimerLimit节点,用于指定某个子节点的最长运行时间。     /// 或者 用于产生某个返回状态,     /// </summary>     public abstract class Decorator : BTNode     {         protected BTNode child;         public Decorator Do(BTNode child)         {             this.child = child;             return this;         }     }      /// <summary>     /// 顺序节点 (从左到右依次执行所有子节点,只要子节点返回Success就继续执行后续子节点,直到遇到Failure或者Runing,      /// 停止后续执行,并把这个节点返回给父节点,只有它的所有子节点都是Success他才会向父节点返回Success)     /// </summary>     public class Sequence : Branch     {         public override BTState Tick()         {             var childState = children[activeChild].Tick();             switch (childState)             {                 case BTState.Success:                     activeChild++;                     if (activeChild == children.Count)                     {                         activeChild = 0;                         return BTState.Success;                     }                     else                         return BTState.Continue;                 case BTState.Failure:                     activeChild = 0;                     return BTState.Failure;                 case BTState.Continue:                     return BTState.Continue;                 case BTState.Abort:                     activeChild = 0;                     return BTState.Abort;             }             throw new System.Exception("This should never happen, but clearly it has.");         }     }      /// <summary>     /// 选择节点从左到右依次执行所有子节点 ,只要遇到failure就继续执行后续子节点,直到遇到一个节点返回Success或Running为止。向父节点返回Success或Running     /// 所有子节点都是Fail, 那么向父节点凡湖Fail     /// 选择节点 用来在可能的行为集合中选择第一个成功的。 比如一个试图躲避枪击的AI角色, 它可以通过寻找隐蔽点, 或离开危险区域, 或寻找援助等多种方式实现目标。     /// 利用选择节点,他会尝试寻找Cover,失败后在试图逃离危险区域。     /// </summary>     public class Selector : Branch     {         public Selector(bool shuffle)         {             if (shuffle)             {                 var n = children.Count;                 while (n > 1)                 {                     n--;                     var k = Mathf.FloorToInt(Random.value * (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 BTState.Success:                     activeChild = 0;                     return BTState.Success;                 case BTState.Failure:                     activeChild++;                     if (activeChild == children.Count)                     {                         activeChild = 0;                         return BTState.Failure;                     }                     else                         return BTState.Continue;                 case BTState.Continue:                     return BTState.Continue;                 case BTState.Abort:                     activeChild = 0;                     return BTState.Abort;             }             throw new System.Exception("This should never happen, but clearly it has.");         }     }      /// <summary>     /// 行为节点  调用方法,或运行协程。完成实际工作, 例如播放动画,让角色移动位置,感知敌人,更换武器,播放声音,增加生命值等。     /// </summary>     public class Action : BTNode     {         System.Action fn;         System.Func<IEnumerator<BTState>> coroutineFactory;         IEnumerator<BTState> coroutine;         public Action(System.Action fn)         {             this.fn = fn;         }         public Action(System.Func<IEnumerator<BTState>> coroutineFactory)         {             this.coroutineFactory = coroutineFactory;         }         public override BTState Tick()         {             if (fn != null)             {                 fn();                 return BTState.Success;             }             else             {                 if (coroutine == null)                     coroutine = coroutineFactory();                 if (!coroutine.MoveNext())                 {                     coroutine = null;                     return BTState.Success;                 }                 var result = coroutine.Current;                 if (result == BTState.Continue)                     return BTState.Continue;                 else                 {                     coroutine = null;                     return result;                 }             }         }          public override string ToString()         {             return "Action : " + fn.Method.ToString();         }     }      /// <summary>     /// 条件节点   调用方法,如果方法返回true则返回成功,否则返回失败。     /// 用来测试当前是否满足某些性质或条件,例如“玩家是否在20米之内?”“是否能看到玩家?”“生命值是否大于50?”“弹药是否足够?”等     /// </summary>     public class Condition : BTNode     {         public System.Func<bool> fn;          public Condition(System.Func<bool> fn)         {             this.fn = fn;         }         public override BTState Tick()         {             return fn() ? BTState.Success : BTState.Failure;         }          public override string ToString()         {             return "Condition : " + fn.Method.ToString();         }     }      /// <summary>     /// 当方法为True的时候 尝试执行当前  子节点     /// </summary>     public class ConditionalBranch : Block     {         public System.Func<bool> fn;         bool tested = false;         public ConditionalBranch(System.Func<bool> fn)         {             this.fn = fn;         }         public override BTState Tick()         {             if (!tested)             {                 tested = fn();             }             if (tested)             {                 // 当前子节点执行完就进入下一个节点(超上限就返回到第一个)                 var result = base.Tick();                 // 没执行完                 if (result == BTState.Continue)                     return BTState.Continue;                 else                 {                     tested = false;                     // 最后一个子节点执行完,才会为Ture                     return result;                 }             }             else             {                 return BTState.Failure;             }         }          public override string ToString()         {             return "ConditionalBranch : " + fn.Method.ToString();         }     }      /// <summary>     /// While节点   只要方法  返回True 就执行所有子节点, 否则返回 Failure     /// </summary>     public class While : Block     {         public System.Func<bool> fn;          public While(System.Func<bool> fn)         {             this.fn = fn;         }          public override BTState Tick()         {             if (fn())                 base.Tick();             else             {                 //if we exit the loop                 ResetChildren();                 return BTState.Failure;             }              return BTState.Continue;         }          public override string ToString()         {             return "While : " + fn.Method.ToString();         }     }      /// <summary>     /// 阻塞节点  如果当前子节点是Continue 说明没有执行完,阻塞着,执行完之后在继续它后面的兄弟节点 不管成功失败。     /// 如果当前结点是最后一个节点并执行完毕,说明成功!否则就是处于Continue状态。      /// 几个基本上是抽象节点, 像是让所有子节点都执行一遍, 当前子节点执行完就进入下一个节点(超上限就返回到第一个)     /// </summary>     public abstract class Block : Branch     {         public override BTState Tick()         {             switch (children[activeChild].Tick())             {                 case BTState.Continue:                     return BTState.Continue;                 default:                     activeChild++;                     if (activeChild == children.Count)                     {                         activeChild = 0;                         return BTState.Success;                     }                     return BTState.Continue;             }         }     }      public class Root : Block     {         public bool isTerminated = false;          public override BTState Tick()         {             if (isTerminated) return BTState.Abort;             while (true)             {                 switch (children[activeChild].Tick())                 {                     case BTState.Continue:                         return BTState.Continue;                     case BTState.Abort:                         isTerminated = true;                         return BTState.Abort;                     default:                         activeChild++;                         if (activeChild == children.Count)                         {                             activeChild = 0;                             return BTState.Success;                         }                         continue;                 }             }         }     }      /// <summary>     /// 多次运行子节点(一个子节点执行一次就算一次)     /// </summary>     public class Repeat : Block     {         public int count = 1;         int currentCount = 0;         public Repeat(int count)         {             this.count = count;         }         public override BTState Tick()         {             if (count > 0 && currentCount < count)             {                 var result = base.Tick();                 switch (result)                 {                     case BTState.Continue:                         return BTState.Continue;                     default:                         currentCount++;                         if (currentCount == count)                         {                             currentCount = 0;                             return BTState.Success;                         }                         return BTState.Continue;                 }             }             return BTState.Success;         }          public override string ToString()         {             return "Repeat Until : " + currentCount + " / " + count;         }     }       /// <summary>     /// 随机的顺序  执行子节点      /// </summary>     public class RandomSequence : Block     {         int[] m_Weight = null;         int[] m_AddedWeight = null;          /// <summary>         /// 每次再次触发时,将选择一个随机子节点         /// </summary>         /// <param name="weight">保留null,以便所有子节点具有相同的权重。         /// 如果权重低于子节点, 则后续子节点的权重都为1</param>         public RandomSequence(int[] weight = null)         {             activeChild = -1;              m_Weight = weight;         }          public override Branch OpenBranch(params BTNode[] children)         {             m_AddedWeight = new int[children.Length];              for (int i = 0; i < children.Length; ++i)             {                 int weight = 0;                 int previousWeight = 0;                  if (m_Weight == null || m_Weight.Length <= i)                 {//如果没有那个权重, 就将权重 设置为1                     weight = 1;                 }                 else                     weight = m_Weight[i];                  if (i > 0)                     previousWeight = m_AddedWeight[i - 1];                  m_AddedWeight[i] = weight + previousWeight;             }              return base.OpenBranch(children);         }          public override BTState Tick()         {             if (activeChild == -1)                 PickNewChild();              var result = children[activeChild].Tick();              switch (result)             {                 case BTState.Continue:                     return BTState.Continue;                 default:                     PickNewChild();                     return result;             }         }          void PickNewChild()         {             int choice = Random.Range(0, m_AddedWeight[m_AddedWeight.Length - 1]);              for (int i = 0; i < m_AddedWeight.Length; ++i)             {                 if (choice - m_AddedWeight[i] <= 0)                 {                     activeChild = i;                     break;                 }             }         }          public override string ToString()         {             return "Random Sequence : " + activeChild + "/" + children.Count;         }     }       /// <summary>     /// 暂停执行几秒钟。     /// </summary>     public class Wait : BTNode     {         public float seconds = 0;         float future = -1;         public Wait(float seconds)         {             this.seconds = seconds;         }          public override BTState Tick()         {             if (future < 0)                 future = Time.time + seconds;              if (Time.time >= future)             {                 future = -1;                 return BTState.Success;             }             else                 return BTState.Continue;         }          public override string ToString()         {             return "Wait : " + (future - Time.time) + " / " + seconds;         }     }      /// <summary>     /// 设置动画  trigger 参数     /// </summary>     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)         {             this.id = Animator.StringToHash(name);             this.animator = animator;             this.triggerName = name;             this.set = set;         }          public override BTState Tick()         {             if (set)                 animator.SetTrigger(id);             else                 animator.ResetTrigger(id);              return BTState.Success;         }          public override string ToString()         {             return "Trigger : " + triggerName;         }     }      /// <summary>     /// 设置动画 boolean 参数     /// </summary>     public class SetBool : BTNode     {         Animator animator;         int id;         bool value;         string triggerName;          public SetBool(Animator animator, string name, bool value)         {             this.id = Animator.StringToHash(name);             this.animator = animator;             this.value = value;             this.triggerName = name;         }          public override BTState Tick()         {             animator.SetBool(id, value);             return BTState.Success;         }          public override string ToString()         {             return "SetBool : " + triggerName + " = " + value.ToString();         }     }      /// <summary>     /// 等待animator达到一个状态。     /// </summary>     public class WaitForAnimatorState : BTNode     {         Animator animator;         int id;         int layer;         string stateName;          public WaitForAnimatorState(Animator animator, string name, int layer = 0)         {             this.id = Animator.StringToHash(name);             if (!animator.HasState(layer, this.id))             {                 Debug.LogError("The animator does not have state: " + name);             }             this.animator = animator;             this.layer = layer;             this.stateName = name;         }          public override BTState Tick()         {             var state = animator.GetCurrentAnimatorStateInfo(layer);             if (state.fullPathHash == this.id || state.shortNameHash == this.id)                 return BTState.Success;             return BTState.Continue;         }          public override string ToString()         {             return "Wait For State : " + stateName;         }     }      /// <summary>     /// 设置 GameObject 的激活状态     /// </summary>     public class SetActive : BTNode     {          GameObject gameObject;         bool active;          public SetActive(GameObject gameObject, bool active)         {             this.gameObject = gameObject;             this.active = active;         }          public override BTState Tick()         {             gameObject.SetActive(this.active);             return BTState.Success;         }          public override string ToString()         {             return "Set Active : " + gameObject.name + " = " + active;         }     }      /// <summary>     /// 等待animator从SendSignal状态机行为 接收信号。   SendSignal : StateMachineBehaviour     /// </summary>     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)         {             this.name = name;             this.id = Animator.StringToHash(name);             if (!animator.HasState(layer, this.id))             {                 Debug.LogError("The animator does not have state: " + name);             }             else             {                 SendSignal.Register(animator, name, this);             }         }          public override BTState Tick()         {             if (!isSet)                 return BTState.Continue;             else             {                 isSet = false;                 return BTState.Success;             }          }          public override string ToString()         {             return "Wait For Animator Signal : " + name;         }     }      /// <summary>     /// 终止节点  切换到中止 状态     /// </summary>     public class Terminate : BTNode     {          public override BTState Tick()         {             return BTState.Abort;         }      }      /// <summary>     /// Log  输出Log 的节点     /// </summary>     public class Log : BTNode     {         string msg;          public Log(string msg)         {             this.msg = msg;         }          public override BTState Tick()         {             Debug.Log(msg);             return BTState.Success;         }     }  }  #if UNITY_EDITOR namespace BTAI {     public interface IBTDebugable     {         Root GetAIRoot();     } } #endif
using BTAI; using System.Collections.Generic; using UnityEditor; using UnityEngine;  namespace Gamekit2D {      /// <summary>     /// 运行是查看  行为树中所有节点的状态      /// </summary>     public class BTDebug : EditorWindow     {         protected BTAI.Root _currentRoot = null;           [MenuItem("Kit Tools/Behaviour Tree Debug")]         static void OpenWindow()         {             BTDebug btdebug = GetWindow<BTDebug>();             btdebug.Show();         }          private void OnGUI()         {             if (!Application.isPlaying)             {                 EditorGUILayout.HelpBox("Only work during play mode.", MessageType.Info);             }             else             {                 if (_currentRoot == null)                     FindRoot();                 else                 {                     RecursiveTreeParsing(_currentRoot, 0, true);                 }             }         }          void Update()         {             Repaint();         }          void RecursiveTreeParsing(Branch branch, int indent, bool parentIsActive)         {             List<BTNode> nodes = branch.Children();              for (int i = 0; i < nodes.Count; ++i)             {                 EditorGUI.indentLevel = indent;                  bool isActiveChild = branch.ActiveChild() == i;                 GUI.color = (isActiveChild && parentIsActive) ? Color.green : Color.white;                 EditorGUILayout.LabelField(nodes[i].ToString());                  if (nodes[i] is Branch)                     RecursiveTreeParsing(nodes[i] as Branch, indent + 1, isActiveChild);             }         }          void FindRoot()         {             if (Selection.activeGameObject == null)             {                 _currentRoot = null;                 return;             }              IBTDebugable debugable = Selection.activeGameObject.GetComponentInChildren<IBTDebugable>();              if (debugable != null)             {                 _currentRoot = debugable.GetAIRoot();             }         }      } }

就是在菜单“Kit Tools/Behaviour Tree Debug" 可以查看TestBT.cs 对象所在行为树

相关推荐:

使用C/C++编写PHP Extension

【c#教程】C# 数据类型

视频:C# 教程

以上就是C#/基于Unity 行为树的实现步骤【纯代码】的详细内容,更多内容请关注技术你好其它相关文章!

来源链接:
免责声明:
1.资讯内容不构成投资建议,投资者应独立决策并自行承担风险
2.本文版权归属原作所有,仅代表作者本人观点,不代表本站的观点或立场
上一篇:php获取远程图片并下载保存到本地的方法分析 下一篇:设计一个c++ 通用链表:实现多态双向的功能

相关资讯