本文参考自:html
在阅读完本文后,请查看上述连接内容。git
本文使用的示例工程:FINCTIVE/lost-in-the-wilderness-nightmare(荒野迷踪:噩梦)欢迎Star!github
因为水平有限,文章内容可能有误,欢迎探讨、交流、或直接批评。
┗|`O′|┛设计模式
本文做者: FINCTIVE
联系邮箱: finctive@qq.comide
转载请标明原文连接以及做者名字,谢谢。this
原文发布于语雀:www.yuque.com/finctive/ga…spa
在荒野迷踪:噩梦中,敌人的行为以下图(在线运行demo):
设计
看起来能够用大大的 if{...}else if{...}else{...} 语句实现,但实际上手开发以后我发现……3d
切入正题:一个状态指AI的一系列行为,例如本项目中的静止、追逐、自爆状态,能够使用一个类描述。对于一个状态,应该把处理代码写在一个类中。好比,“追逐玩家”状态相关的代码,尽可能不要写到其余状态的类里面。code
敌人AI游戏对象截图
public abstract class BaseState : MonoBehaviour
{
// 执行本状态的相关操做,返回值是下一次游戏循环的状态
public abstract BaseState Tick();
// 与本状态有关的初始化代码
public virtual void OnStateStart(){}
// 与本状态有关的退出代码
public virtual void OnStateExit(){}
}
复制代码
状态机
public class StateMachine : MonoBehaviour
{
public BaseState defaultState;
[HideInInspector]public BaseState currentState;
private void Awake() {
currentState = defaultState;
}
void FixedUpdate() {
BaseState nextStateType = currentState.Tick();
if (nextStateType != currentState)
{
nextStateType.OnStateStart();
currentState.OnStateExit();
}
currentState = nextStateType;
}
}
复制代码
我把与全部状态相关的控制脚本写在了EnemyController组件中,暴露出公共方法让状态机脚本调用。这样能够复用代码,而且让状态机的逻辑代码只负责更高一层的控制,而无论细节如何。
如下是追逐状态的代码,其余状态同理。
public class EnemyChasingState : BaseState
{
public EnemyAttackingState enemyAttackingState;
public EnemyIdlingState enemyIdlingState;
private EnemyController _enemyController;
private void Awake() {
_enemyController = GetComponent<EnemyController>();
}
private static readonly int AnimMove = Animator.StringToHash("move");
public override BaseState Tick() {
Vector3 targetPos = PlayerController.playerTransform.position;
_enemyController.MoveToTarget(targetPos);
float distanceSqrMag = (targetPos - transform.position).sqrMagnitude;
// 距离足够近,开始攻击(自爆)
if(distanceSqrMag < _enemyController.enemyInfo.startAttackingDistance*_enemyController.enemyInfo.startAttackingDistance)
{
return enemyAttackingState;
}
// 距离太远,放弃追逐
if(distanceSqrMag > _enemyController.enemyInfo.stopChasingDistance*_enemyController.enemyInfo.stopChasingDistance)
{
return enemyIdlingState;
}
return this;
}
public override void OnStateExit() {
_enemyController.modelAnimator.SetFloat(AnimMove, 0f);
}
}
复制代码
做者:FINCTIVE(finctive@qq.com)欢迎转载,请附上原文连接以及做者名字,谢谢!