孙广东 2018.7.25
行为树的概念, 各种 Unity 插件 都没时间介绍。 代码是看了 Unity的 2D Game Kit , 提炼出来,测试! 对于学习和理解行为树会很有帮助!!! 纯代码, 没有Unity插件的节点编辑导出配置等内容。
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); }