最近有空时在学做怪物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年的视频,虽然很老了但同样很适用。