Unity外置ExternalBehavior

为何要外置

Behavior Designer是Unity的一个插件,我用与处理AI逻辑.当AI逻辑很复杂的情况下会使用到ExternalBehavior.

ExternalBehavior相当于是BehaviorTree的嵌套. 但是对于多重嵌套情况 他自带的Task支持的并不好.

比如:

一个角色AI,最外层的逻辑可能为发呆,觅食,巡逻等等.

觅食巡逻是两个ExternalBehavior,里面的实现又会用到另外的两个ExternalBehavior,比如移动,攻击

这时候我们有两个角色,角色A,角色B. AB两个角色其移动,攻击使用不同的ExternalBehavior

A-Move,A-Attack,B-Move,B-Attack

由于嵌套最里层的ExternalBehavior不同,所以导致上层的觅食,巡逻 也需要有两个.

角色A-觅食,角色A-巡逻
角色B-觅食,角色B-巡逻

这样如果当AI非常复杂,嵌套层级又多.维护起来非常麻烦.

改进方法

QQ20171216-171241

将对应的ExternalBehavior提到外层的Variables变量中来,利用ExternalBehavior和主的Behavior如果变量名相同则自动Override的特性.将所有的ExternalBehavior放出来.只要保证编写每个ExternalBehavior时候,使用相同的变量名,则自动替换所有的.

还是上面的例子. 建立Variables

  • LowLevelAI_Move
  • LowLevelAI_Attack
  • HightLevelAI_Eat
  • HightLevelAI_Patrol

这样对于角色A和角色B,Eat和Patrol可以使用同一个ExternalBehavior,只是MoveAttack分别使用不同的.

代码

CEExternalBehaviorTree.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// <summary>
/// 自定义ExternalBehavior
/// 使用SharedExternalTree做参数,该参数可以在BD Root处进行设置.这样就可以在外部配置相应的需要Override的BD
/// </summary>
[TaskIcon("BehaviorTreeReferenceIcon.png")]
[TaskCategory("CopyEngine")]
public class CEExternalBehaviorTree : BehaviorReference
{
public CESharedExternalTree externalTree;

public override BehaviorDesigner.Runtime.ExternalBehavior[] GetExternalBehaviors()
{
if (externalTree.Value != null) return new[] {externalTree.Value};
var dummy = CopyEngineFacade.RES.GetRes<BehaviorDesigner.Runtime.ExternalBehavior>("BD/Dummy");
return new[] {dummy};
}
}

CESharedExternalTree.cs

1
2
3
4
5
6
7
public class CESharedExternalTree : SharedVariable<BehaviorDesigner.Runtime.ExternalBehavior>
{
public static implicit operator CESharedExternalTree(BehaviorDesigner.Runtime.ExternalBehavior value)
{
return new CESharedExternalTree {Value = value};
}
}

CEIsExternalTreeNotNull.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[TaskCategory("CopyEngine")]
public class CEIsExternalTreeNotNull : Conditional
{
public CESharedExternalTree tree;

private BehaviorDesigner.Runtime.ExternalBehavior mDummy;

public override void OnAwake()
{
mDummy = CopyEngineFacade.RES.GetRes<BehaviorDesigner.Runtime.ExternalBehavior>("BD/Dummy");
}

public override TaskStatus OnUpdate()
{
if (tree.Value == null || tree.Value == mDummy)
{
return TaskStatus.Failure;
}
return TaskStatus.Success;
}
}

同时需要在Resources/BD目录下放置一个DummyExternalBehavior

QQ20171216-172214

这么做的操作是为了做判空,因为如果ExternalBehavior默认为空则运行时候就会报错. 而做判空是为了更方便做扩展.

还是刚才的例子,如果角色A和角色B的Move,Attack不同点 一个在于是走,一个是跑,一个是近战攻击,一个是远程攻击.

那就是可以建立一个CommonAI,其变量为:

  • LowLevelAI_Walk
  • LowLevelAI_Run
  • LowLevelAI_CloseAttack
  • LowLevelAI_RangeAttack
  • HightLevelAI_Eat
  • HightLevelAI_Patrol

这样角色A和角色B可以用同一个BehaviorTree,只是实现不同的行为,没有的默认留空. 内部逻辑通过CEIsExternalTreeNotNull进行判断,从而选择不同的实现方式.

坚持原创技术分享,您的支持将鼓励我继续创作!