只使用FSM及其变种是很难应用在复杂的AI中的,因为其复杂度的扩展性很差,就比如,如果怪物有20种状态,你就需要考虑20*20种可能性的连接。
所以这时我们必须建立分层次的状态机设计,试想一下,把AI的所有行为再分个类,每一类都认为是一种策略(事实上我们人的大脑也很类似这样,比如我们去攻击一个人,我们选择什么方式攻击、什么方式假动作是大脑思考好的,但之下的出拳这些动作确是自然而然做出来,虽然攻击策略不同,但是下面的行为确实一样的——这就是把策略看作了行为的组合),这样首先第一个好处就是,用很少的行为就可以表示很复杂的策略,因为同是出拳,在不同的策略中有不同的用法。这样,原本是增加状态节点->复杂度线性增加->难度指数增加,现在是增加状态节点和策略节点->复杂度平方增加->难度平方增加。
然后再来看看在框架中我们要如何做,很明显,一个策略要和一个状态机绑定。这里有一个问题出现了,策略还用FSM来做么?显然是不好的,FSM强调状态的转化,但是对于策略来说,策略的转化没有什么衔接和前后关系,所以我们使用了FuSM。FuSM的整体思想也很简单,他为每个策略确定了一个激活水平,选取最高激活水平的策略进入。
来看看代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityECS;
using FreedomAI;
namespace FreedomAI
{
public delegate float StrategyActioner(AIEntity pEntity);
public delegate float StrategyFeedbacker(AIEntity pEntity);
public delegate void StrategyExit(AIEntity pEntity);
public delegate void StrategyEnter(AIEntity pEntity);
public class ObstacleComponent:UComponent
{
public GameObject hitObject;
public float hitDistance;
public Vector3 target;
}
public class ObstacleAvoidance
{
public static float ActionFunc(AIEntity pEntity)
{
GameObject tAIObject = pEntity.GetComponent<BaseAIComponent> ().mAIRT;
Vector3 tDir = pEntity.GetComponent<AIMove> ().mDirection;
float maxDis = 2.0f;
RaycastHit hit = new RaycastHit();
int layoutmask = 1 << LayerMask.NameToLayer ("Collision");
if (Physics.Raycast (tAIObject.transform.position, tDir,out hit,maxDis,layoutmask))
{
Vector3 hitPos = hit.transform.position;
float tDis = Vector3.Distance (hitPos,tAIObject.transform.position);
pEntity.GetComponent<ObstacleComponent> ().hitObject = hit.transform.gameObject;
pEntity.GetComponent<ObstacleComponent> ().hitDistance = tDis;
if (tDis < 1.0f)
{
return 1.0f;
}
return 2.0f-tDis;
}
else
{
return 0.0f;
}
}
public static void Strategy_Enter(AIEntity pEntity)
{
pEntity.GetComponent<ObstacleComponent> ().target = Vector3.zero;
}
public static void FSM_Avoid(AIEntity pEntity)
{
if (pEntity.GetComponent<ObstacleComponent> ().target == Vector3.zero)
{
Vector3 v1 = pEntity.GetComponent<ObstacleComponent> ().hitObject.transform.position - pEntity.AIPos;
v1.y = 0.0f;
Vector3 v2 = new Vector3 (1.0f,0.0f,-v1.x/v1.z);
v2.Normalize ();
Vector3 v3 = -v2;
for (int i = 0; i <=10; i++)
{
float tempRate = (float)i / 10.0f;
Vector3 vdir1 = Vector3.Lerp (v1, v2, tempRate);
vdir1.Normalize ();
Vector3 vdir2 = Vector3.Lerp (v1,v3,tempRate);
vdir2.Normalize ();
float maxDis = 2