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); } }