FSM是用的非常成熟的技术,不过我们在算法框架上加入了自己的理解。
一是加上概率机制。
将FSM进化成概率FSM,是自由度的第一步体现。不在是固定会在某个节点变到另一个节点,而是在某一个区间都有可能发生变化。
二是加上反馈机制。
由于是基于概率的,每一条节点连线都可以附上权值,比如权值是3,就相当于这个转化的概率大3倍,这样我们就可以加入反馈一样的学习机制,首先我们认为节点的转化是有一个目标的,比如Idle->RunToPlayer,这个转化的目标就是要接近Player,我们的目标函数就是检测是否有更接近这个Player,于是在发生转化的10帧内,我们都进行目标函数的检测,统计最终的总收益,如果转化的目标有更加达成的趋势,节点联系增强,反之减弱。
三是加上情绪机制。
在节点的转化过程中,我们又考虑了,AI的情绪,首先AI的情绪有这FSM类似一套的转化机制,然后转化节点的具体概率计算,可以随时引入AI情绪的变量,(也可以不引),看需求,比如一个AI被设定成愤怒是更容易攻击玩家,这个情绪机制就很方便了。
四是引入了委托使得工程更加便利。
以前写FSM,很大程度上要写很多的switch-case,不仅代码很长,而且很不好改,我们引入了委托,把节点函数做为了一个数据,向图中的节点一样,新写了一个就动态的忘里插入就可以了。
代码:
public delegate void StateEnter(AIEntity pEntity);
public delegate void StateExit(AIEntity pEntity);
public delegate void StateExecuter(AIEntity pEntity);
public delegate float StateTranfer(AIEntity pEntity);
public delegate float StateFeedbacker(AIEntity pEntity);
public delegate void StateRecorder(AIEntity pEntity);
public delegate void AnimationPlay(Animator pAnimator);
public delegate float EmotionExecuter(AIEntity pEntity);
public struct TranferNode
{
public StateTranfer mTranfer;
public int id;
}
public struct FeedbackerNode
{
public StateFeedbacker mFeedbacker;
public int id;
}
public struct PowerNode
{
public float power;
public int id;
}
public class EmptyTranfer
{
public static float Run(AIEntity pEntity)
{
return 0.0f;
}
}
public class EmptyExitAndEnter
{
public static void EmptyExit(AIEntity pEntity)
{
}
public static void EmptyEnter(AIEntity pEntity)
{
}
}
public class AIStateRecord
{
public void Run(AIEntity pEntity)
{
pEntity.GetComponent<AIState> ().LastEntityData.GetComponent<AIMove> ().mDirection = pEntity.GetComponent<AIMove> ().mDirection;
pEntity.GetComponent<AIState> ().LastEntityData.GetComponent<AIMove> ().mVelocity = pEntity.GetComponent<AIMove> ().mVelocity;
pEntity.GetComponent<AIState> ().LastEntityData.GetComponent<AIMove> ().mMoveFunc = pEntity.GetComponent<AIMove> ().mMoveFunc;
pEntity.GetComponent<AIState> ().LastEntityData.AIPos = pEntity.AIPos;
pEntity.GetComponent<AIState> ().LastEntityData.PlayerPos = pEntity.PlayerPos;
pEntity.GetComponent<AIState> ().LastEntityData.GetComponent<AIAnimation> ().tempAnim = pEntity.GetComponent<AIAnimation> ().tempAnim;
}
}
public class AIState:UComponent
{
public StateExecuter[] mExecuter;
public StateExit[] mExiter;
public StateEnter[] mEnterer;
public List<TranferNode>[] mTranfer;
public List<FeedbackerNode>[] mFeedbacker;
public List<PowerNode>[] mPowerEdge;
public UEntity LastPlayerEntity;
public AIEntity LastEntityData;
public Dictionary<int,string> mStateAnimation;
//public List<UComponent> LastComponent;
public int mMaxCount = 25;
public int mtempCount = 0;
public int tempID = 0;
public int mCaptureFrame = 0;
public bool mFeedbackerState = false;
public float[] mframebuffer = new float[10];
public StateFeedbacker mTempFeedbacker;
public int mid_fir, mid_sec;
public uint IAnyway;
public StateRecorder mStateRecorder;
public override void Init ()
{
mStateAnimation = new Dictionary<int, string> ();
mExiter = new StateExit[25];
mEnterer = new StateEnter[25];
mExecuter = new StateExecuter[25];
mTranfer = new List<TranferNode>[25];
for (int i = 0; i < 25; i++)
{
mTranfer [i] = new List<TranferNode> ();
}
mFeedbacker = new List<FeedbackerNode>[25];
for (int i = 0; i < 25; i++)
{
mFeedbacker [i] = new List<FeedbackerNode> ();
}
mPowerEdge = new List<PowerNode>[25];
for (int i = 0; i < 25; i++)
{
mPowerEdge [i] = new List<PowerNode> ();
}
LastPlayerEntity = new UEntity ();
LastEntityData = new AIEntity ();
LastEntityData.PlayerEntity = LastPlayerEntity;
LastEntityData.mWorld = this.mUEntity.mWorld;
IAnyway = (uint)mtempCount;
mtempCount++;
}
public bool AddAnimation(StateExecuter pExecuter,string pName)
{
int index = -1;
for (int i = 0; i < mtempCount; i++)
{
if (mExecuter [i] == pExecuter)
{
index = i;
break;
}
}
if (index != -1)
{
mStateAnimation.Add (index,pName);
return true;
}
else
{
return false;
}
}
public int AddExecuter(StateExecuter pExecuter,StateExit pExit,StateEnter pEnter)
{
mExecuter [mtempCount] = pExecuter;
mExiter [mtempCount] = pExit;
mEnterer [mtempCount] = pEnter;
mtempCount++;
return mtempCount-1;
}
public void AddEdge(StateTranfer pTranfer,StateFeedbacker pFeedbacker,int id1,int id2)
{
TranferNode tn = new TranferNode ();
tn.id = id2;
tn.mTranfer = pTranfer;
FeedbackerNode fn = new FeedbackerNode ();
fn.id = id2;
fn.mFeedbacker = pFeedbacker;
PowerNode pn = new PowerNode ();
pn.id = id2;
pn.power = 1.0f;
mTranfer [id1].Add (tn);
mFeedbacker [id1].Add (fn);
mPowerEdge [id1].Add (pn);
}
public void AddEdge(StateTranfer pTranfer,StateFeedbacker pFeedbacker,StateExecuter exe1,StateExecuter exe2)
{
int id1 = -1;
int id2 = -1;
for (int i = 0; i < 25; i++)
{
if (mExecuter [i] == exe1)
{
id1 = i;
}
if (mExecuter [i] == exe2)
{
id2 = i;
}
if (id1 != -1 && id2 != -1)
{
AddEdge (pTranfer,pFeedbacker,id1,id2);
break;
}
}
}
public void AddAnywayTranfer(StateTranfer pStateTranfer,StateFeedbacker pFeedbacker,int id)
{
AddEdge (pStateTranfer,pFeedbacker,0,id);
}
}
public class StateControlSystem:USystem
{
public override void Init ()
{
this.AddRequestComponent (typeof(AIState));
this.AddRequestComponent (typeof(AIAnimation));
}
public override void Update (UEntity uEntity)
{
// Debug.Log (uEntity.GetComponent<AIState>().mStateAnimation[uEntity.GetComponent<AIState>().tempID]);
//Debug.Log(uEntity.GetComponent<AIState>().mStateAnimation[uEntity.GetComponent<AIState>().tempID]+" "+uEntity.GetComponent<AIEmotion>().GetTempEmotion());
uEntity.GetComponent<AIState> ().mExecuter [uEntity.GetComponent<AIState> ().tempID]((AIEntity)uEntity);
((AIEntity)uEntity).AIPos = ((AIEntity)uEntity).GetComponent<BaseAIComponent> ().mAIRT.transform.position;
((AIEntity)uEntity).PlayerPos = ((AIEntity)uEntity).mPlayer.transform.position;
string tName = uEntity.GetComponent<AIState> ().mStateAnimation [uEntity.GetComponent<AIState> ().tempID];
uEntity.GetComponent<AIAnimation> ().tempAnim = tName;
for (int i = 0; i < uEntity.GetComponent<AIState> ().mTranfer[0].Count; i++)
{
if (uEntity.GetComponent<AIState> ().mTranfer [0] [i].id == uEntity.GetComponent<AIState> ().tempID)
{
continue;
}
float tRate = uEntity.GetComponent<AIState> ().mTranfer [0] [i].mTranfer(((AIEntity)uEntity));
float tPower = uEntity.GetComponent<AIState> ().mPowerEdge [0] [i].power;
bool returning = false;
if (tRate * tPower > 0.04f*Time.deltaTime)
{
if (Random.Range (0.0f, 1.0f) <= tRate * tPower)
{
// Debug.Log (tRate+" "+tPower+"any");
uEntity.GetComponent<AIState> ().mTempFeedbacker = uEntity.GetComponent<AIState> ().mFeedbacker [0] [i].mFeedbacker;
if (uEntity.GetComponent<AIState> ().mCaptureFrame > 0)
{
float sum = 0.0f;
for (int j = 0; j < 10-uEntity.GetComponent<AIState> ().mCaptureFrame; j++)
{
sum += uEntity.GetComponent<AIState> ().mframebuffer [j];
}
sum /= 10.0f-uEntity.GetComponent<AIState> ().mCaptureFrame;
for (int j = 0; j < uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir].Count; j++)
{
if (uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [j].id == uEntity.GetComponent<AIState> ().mid_sec)
{
PowerNode tpn = new PowerNode ();
tpn.id = uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [j].id;
tpn.power = uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [j].power + sum;
if (tpn.power > 3.0f)
tpn.power = 3.0f;
if (tpn.power < 0.3f)
tpn.power = 0.3f;
uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [j] = tpn;
break;
}
}
}
uEntity.GetComponent<AIState> ().mCaptureFrame = 10;
uEntity.GetComponent<AIState> ().mid_fir = 0;
uEntity.GetComponent<AIState> ().mid_sec = uEntity.GetComponent<AIState> ().mTranfer [0] [i].id;
uEntity.GetComponent<AIState> ().mExiter [uEntity.GetComponent<AIState> ().tempID] ((AIEntity)uEntity);
uEntity.GetComponent<AIState> ().tempID = uEntity.GetComponent<AIState> ().mTranfer [0] [i].id;
uEntity.GetComponent<AIState> ().mEnterer [uEntity.GetComponent<AIState> ().tempID] ((AIEntity)uEntity);
returning = true;
}
}
if (returning)
{
return;
}
}
for (int i = 0; i < uEntity.GetComponent<AIState> ().mTranfer [uEntity.GetComponent<AIState> ().tempID].Count; i++)
{
float tRate = uEntity.GetComponent<AIState> ().mTranfer [uEntity.GetComponent<AIState> ().tempID] [i].mTranfer(((AIEntity)uEntity));
float tPower = uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().tempID] [i].power;
bool breaking = false;
if (tRate * tPower > 0.04f*Time.deltaTime)
{
if (Random.Range (0.0f, 1.0f) <= tRate * tPower)
{
uEntity.GetComponent<AIState> ().mTempFeedbacker = uEntity.GetComponent<AIState> ().mFeedbacker [uEntity.GetComponent<AIState> ().tempID] [i].mFeedbacker;
if (uEntity.GetComponent<AIState> ().mCaptureFrame > 0)
{
float sum = 0.0f;
for (int j = 0; j < 10-uEntity.GetComponent<AIState> ().mCaptureFrame; j++)
{
sum += uEntity.GetComponent<AIState> ().mframebuffer [j];
}
sum /= 10.0f-uEntity.GetComponent<AIState> ().mCaptureFrame;
for (int j = 0; j < uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir].Count; j++)
{
if (uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [j].id == uEntity.GetComponent<AIState> ().mid_sec)
{
PowerNode tpn = new PowerNode ();
tpn.id = uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [j].id;
tpn.power = uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [j].power + sum;
if (tpn.power > 3.0f)
tpn.power = 3.0f;
if (tpn.power < 0.3f)
tpn.power = 0.3f;
uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [j] = tpn;
break;
}
}
}
uEntity.GetComponent<AIState> ().mCaptureFrame = 10;
uEntity.GetComponent<AIState> ().mid_fir = uEntity.GetComponent<AIState> ().tempID;
uEntity.GetComponent<AIState> ().mid_sec = uEntity.GetComponent<AIState> ().mTranfer [uEntity.GetComponent<AIState> ().tempID] [i].id;
uEntity.GetComponent<AIState> ().mExiter [uEntity.GetComponent<AIState> ().tempID] ((AIEntity)uEntity);
uEntity.GetComponent<AIState> ().tempID = uEntity.GetComponent<AIState> ().mTranfer [uEntity.GetComponent<AIState> ().tempID] [i].id;
uEntity.GetComponent<AIState> ().mEnterer [uEntity.GetComponent<AIState> ().tempID] ((AIEntity)uEntity);
breaking = true;
}
}
if (breaking)
{
break;
}
}
}
}
public class FrameCaptureSystem:USystem
{
public override void Init ()
{
this.AddRequestComponent (typeof(AIState));
}
public override void Update (UEntity uEntity)
{
if (uEntity.GetComponent<AIState> ().mCaptureFrame != 0)
{
int tIndex = uEntity.GetComponent<AIState> ().mCaptureFrame-1;
float tLast = uEntity.GetComponent<AIState> ().mTempFeedbacker ((AIEntity)uEntity.GetComponent<AIState>().LastEntityData);
float tNow = uEntity.GetComponent<AIState> ().mTempFeedbacker ((AIEntity)uEntity);
uEntity.GetComponent<AIState> ().mframebuffer [tIndex] = Mathf.Abs(tLast)-Mathf.Abs(tNow);
uEntity.GetComponent<AIState> ().mCaptureFrame--;
if (uEntity.GetComponent<AIState> ().mCaptureFrame == 0)
{
uEntity.GetComponent<AIState> ().mFeedbackerState = true;
}
}
}
}
public class FrameStatistics:USystem
{
public override void Init ()
{
this.AddRequestComponent (typeof(AIState));
}
public override void Update (UEntity uEntity)
{
if (uEntity.GetComponent<AIState> ().mFeedbackerState)
{
float sum = 0.0f;
for (int i = 0; i < 10; i++)
{
sum += uEntity.GetComponent<AIState> ().mframebuffer [i];
}
sum /= 10.0f;
for (int i = 0; i < uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir].Count; i++)
{
if (uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [i].id == uEntity.GetComponent<AIState> ().mid_sec)
{
PowerNode tpn = new PowerNode ();
tpn.id = uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [i].id;
tpn.power = uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [i].power + sum;
if (tpn.power > 3.0f)
tpn.power = 3.0f;
if (tpn.power < 0.3f)
tpn.power = 0.3f;
uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [i] = tpn;
//Debug.Log (uEntity.GetComponent<AIState> ().mid_fir+" "+uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [i].power+" "+uEntity.GetComponent<AIState> ().mPowerEdge [uEntity.GetComponent<AIState> ().mid_fir] [i].id);
break;
}
}
uEntity.GetComponent<AIState> ().mFeedbackerState = false;
}
}
}
public class StateRecordSystem:USystem
{
public override void Init ()
{
this.AddRequestComponent (typeof(AIState));
power = 2000;
}
public override void Update (UEntity uEntity)
{
uEntity.GetComponent<AIState> ().mStateRecorder ((AIEntity)uEntity);
}
}