【怪物AI设计】敌人不规则移动

最近有空时在学做怪物AI设计,于是记录一下不规则移动。

怪物AI设计常见思路

目前Unity制作怪物AI主要有两种方案,第一类是有限状态机,之前写过文章介绍过;第二类是行为树,Unity的一个插件。综合对比,状态机纯靠代码实现、是免费的,缺点是状态复杂、比较难以理解,状态多起来的时候会变得比较难以维护;行为树是靠图形化界面+代码实现的,缺点是付费插件、略微耗性能。其实我么手写的有限状态机就是行为树逻辑的体现。有限状态机实现AI移动,我之前已经实现过了(可以参考我前面写的文章);这里我用的行为树,插件可以在论坛aurorayh.asia获取。(仅学习使用)

行为树实现

这里我们自己写一个节点,继承行为树为我们封装好的Action类,EnemyMovementController和EnemyCombatController类是我自己写的,前者提供了SetAnimation方法,用来设置敌人移动速度;后者则是写了个GetAttackCommand方法来接收攻击指令,还有一个SetAttackCommand方法提供给外部、用来分发攻击指令。ExpandClass类是我自己手写的一些方法。我们通过FreeMoveIndex+switch...case语句,使敌人不规则移动,通过actionTimer改变动作时间,防止敌人一直处于一个状态不变。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
using UnityEngine;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
using System.Collections.Generic;
using System.Collections;

public class AIFreeMoveACtion : Action
{
private EnemyMovementController moveController;
private EnemyCombatController combatController;
private int FreeMoveIndex;
private int lastIndex;
private float actionTimer = 2f;
public override void OnAwake()
{
moveController = this.GetComponent<EnemyMovementController>();
combatController = this.GetComponent<EnemyCombatController>();
FreeMoveIndex = Random.Range(0, 3);
}

public override TaskStatus OnUpdate()
{
//接收到攻击指令就结束了
if (combatController.GetAttackCommand())
return TaskStatus.Success;
else
{
//时间管理器,让10秒后自动设置为攻击
if(ExpandClass.GetTransformDistance(this.transform,EnemyManager.Instance().GetPlayer()) > 8f)
{
moveController.SetAnimation(0f, 1f);
}
if(ExpandClass.GetTransformDistance(this.transform, EnemyManager.Instance().GetPlayer()) < 8f && ExpandClass.GetTransformDistance(this.transform, EnemyManager.Instance().GetPlayer()) > 3f)
{
FreeMove();
UpdateIndex();
}
if (ExpandClass.GetTransformDistance(this.transform, EnemyManager.Instance().GetPlayer()) < 3f && ExpandClass.GetTransformDistance(this.transform, EnemyManager.Instance().GetPlayer()) > 0f)
{
moveController.SetAnimation(-1f, -1f);
}

return TaskStatus.Running;
}
}

private void FreeMove()
{
switch (FreeMoveIndex)
{
case 0:
moveController.SetAnimation(-1f, 0f);
break;
case 1:
moveController.SetAnimation(0f, 0f);
break;
case 2:
moveController.SetAnimation(1f,0f);
break;

}

}

private void UpdateIndex()
{
actionTimer -= Time.deltaTime;
if (actionTimer < 0f)
{
UpdateTwo();
}
}

private void UpdateTwo()
{
lastIndex = FreeMoveIndex;
FreeMoveIndex = Random.Range(0, 3);
if (FreeMoveIndex == lastIndex)
{
FreeMoveIndex = Random.Range(0, 3);
}
actionTimer = 2f;
}
}

至于界面部分就更简单了,任何学过行为树的人都知道,如果你没有学过行为树的话,可以去b站搜视频看,有一个2019年的视频,虽然很老了但同样很适用。