饭后温柔

汉堡与老干妈同嚼 有可乐味
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

c# 状态机实现

Posted on 2016-06-15 12:19  饭后温柔  阅读(7040)  评论(0编辑  收藏  举报

c#仿boost statechart的状态机。去年转到unity使用c#,statechart原来的风格蛮爽的,缺点是编译忒慢,在c#则编译根本不是问题。

不一样的地方首先是简单!因为没做一些东西如region。其次是每个状态是持久存在的,不像boost statechart当transit时重建。所以entry,exit自己管一下清理。

重入时不包括双方最近的共同outer state,个人喜好。

这不是一个快速的状态机,在它该用的地方保证很爽就是了。不该用的地方比如字符匹配就不要用了。

 

最近想实现一下行为树,但重新回顾这个状态机,我发现就AI来说,2者其实并无什么质变。该状态机如果实现动态的树结构,那其实就是另一个变异的行为树了。

 

using System;
using System.Collections;
using System.Collections.Generic;

namespace StateChart
{
    public enum EResult
    { 
        None,
        Forward,
        Resume,
        Defered,
    }

    public enum EHistory
    {
        Shallow,
        Deep,
    }

public abstract class IEvent { public Type type { get { return GetType(); } } }
public delegate void Reaction<T>(T fsm); public delegate EResult Reaction<U, T>(U fsm, T evt); interface IReaction { EResult Execute<FSM, EVENT>(FSM fsm_, EVENT evt); } public class CReaction<FSM, EVENT> : IReaction { Reaction<FSM, EVENT> reaction; public CReaction(Reaction<FSM, EVENT> reaction_) { reaction = reaction_; } public EResult Execute<F, E>(F fsm_, E evt) { return reaction((FSM)(object)fsm_, (EVENT)(object)evt); } } public abstract class IState<FSM> where FSM : IStateMachine<FSM> { public Type type { get { return GetType(); } } public EHistory History { get; set; } public Reaction<FSM> Entry { get; set; } public Reaction<FSM> Exit { get; set; } //could calc on runtime, but we need more fast spped this time. public int Depth { get; set; } public IState<FSM> OuterState { get; set; } public IState<FSM> ActiveState { get; set; } Dictionary<Type, IReaction> reactions = new Dictionary<Type, IReaction>(); Dictionary<Type, Type> transitions = new Dictionary<Type, Type>(); List<IState<FSM>> subStates = new List<IState<FSM>>(); IState<FSM> initState = null; public IState<FSM> InitState { get { if (initState == null) if (subStates.Count > 0) initState = subStates[0]; return initState; } set { initState = value; } } public IState(IState<FSM> ostate) { History = EHistory.Shallow; OuterState = ostate; if (OuterState != null) OuterState.AddSubState(this); } public IState(IState<FSM> ostate, EHistory history_) { OuterState = ostate; History = history_; if (OuterState != null) OuterState.AddSubState(this); } public void DoEntry(FSM fsm_) { //UnityEngine.Debug.Log("Entry: " + type.ToString()); Console.WriteLine("Entry: " + type.ToString()); if (Entry != null) Entry(fsm_); else OnEntry(fsm_); } public void DoExit(FSM fsm_) { //UnityEngine.Debug.Log("Exit : " + type.ToString()); Console.WriteLine("Exit : " + type.ToString()); if (Exit != null) Exit(fsm_); else OnExit(fsm_); } protected virtual void OnEntry(FSM fsm_) { } protected virtual void OnExit(FSM fsm_) { } public EResult Process<EVENT>(FSM fsm_, EVENT evt) where EVENT : IEvent { IReaction reaction = null; bool hasit = reactions.TryGetValue(evt.type, out reaction); if (!hasit) return EResult.Forward; return reaction.Execute<FSM, EVENT>(fsm_, evt); } public void Bind<EVENT>(Reaction<FSM, EVENT> reaction) where EVENT : IEvent { if (transitions.ContainsKey(typeof(EVENT))) throw new System.InvalidOperationException(); IReaction ireaction = new CReaction<FSM, EVENT>(reaction); reactions.Add(typeof(EVENT), ireaction); } public void Bind<EVENT, TSTATE>() where EVENT : IEvent where TSTATE : IState<FSM> { if (reactions.ContainsKey(typeof(EVENT))) throw new System.InvalidOperationException(); transitions.Add(typeof(EVENT), typeof(TSTATE)); } public void AddSubState(IState<FSM> sstate) { IState<FSM> state = subStates.Find((x) => x.type == sstate.type); if (state != null) return; subStates.Add(sstate); } public IEnumerable<IState<FSM>> IterateSubState() { foreach (IState<FSM> state in subStates) yield return state; } } }

 

using System;
using System.Collections;
using System.Collections.Generic;

namespace StateChart
{

    public abstract class IStateMachine<HOST>  where HOST : IStateMachine<HOST>
    {
        Dictionary<Type, IState<HOST>> typeStates = new Dictionary<Type, IState<HOST>>();
        List<IState<HOST>> activeStates = new List<IState<HOST>>();
        Queue<IEvent> eventQueue = new Queue<IEvent>();
        IState<HOST> outestState = null;
        bool bSuspend = false;

        public IStateMachine() { }

        public void Init(IState<HOST> state) 
        {
            IState<HOST> pstate = state;

            //add outer states
            while (pstate.OuterState != null) {
                pstate.OuterState.ActiveState = pstate;
                activeStates.Add(pstate);
                pstate = pstate.OuterState;
            } 
            activeStates.Add(pstate);
            outestState = pstate;
            
            //build global type-to-state table
            BuildStateTable(outestState, 0);

            //add init sub states
            pstate = state;
            while (pstate.InitState != null) {
                pstate.ActiveState = pstate.InitState;
                pstate = state.InitState;
                if(pstate != null) activeStates.Add(pstate);
            }

            activeStates.Sort((x, y) => x.Depth - y.Depth);
            foreach (IState<HOST> astate in activeStates) {
                astate.DoEntry((HOST)this);
            }
        }

        void BuildStateTable(IState<HOST> state, int depth_) 
        {
            if (state == null) return;
            state.Depth = depth_;
            typeStates.Add(state.type, state);
            foreach (IState<HOST> sstate in state.IterateSubState()) { 
                BuildStateTable(sstate, depth_ + 1); 
            }
        }

        EResult Transit(IState<HOST> state)
        {
            IState<HOST> lstate = null;

            lstate = outestState;
            while (lstate.ActiveState != null) {  // we could save it if state tree is too high.
                lstate = lstate.ActiveState;
            }

            IState<HOST> rstate = state;
            if (state.History == EHistory.Shallow)
                while (rstate.InitState != null)
                    rstate = state.InitState;
            else
                while (rstate.ActiveState != null)
                    rstate = rstate.ActiveState;


            IState<HOST> ltail = lstate;  //save tail of active states
            IState<HOST> rtail = rstate;    //save tail of init states

            int dis = lstate.Depth - rstate.Depth;
            if (dis > 0)
                { IState<HOST> tstate = lstate; lstate = rstate; rstate = tstate; } //rstate will be deepest state

            dis = Math.Abs(dis);
            for (int i = 0; i < dis; i++)  {
                rstate = rstate.OuterState;
            }
            if (rstate == lstate)  //is family
                return EResult.None;
            do
            { //find nearest outer state
                rstate = rstate.OuterState;
                lstate = lstate.OuterState;
            } while (lstate != rstate);

            do  // call exit chain 
            {
                ltail.DoExit((HOST)this);
                ltail = ltail.OuterState;
            } while (ltail != lstate);

            //add tail chain active states
            activeStates.RemoveRange(rstate.Depth + 1, activeStates.Count - rstate.Depth - 1);
            do
            {
                activeStates.Add(rtail);
                lstate = rtail;
                rtail = rtail.OuterState;
                rtail.ActiveState = lstate;
            } while (rtail != rstate);

            // do entry chain
            while (rstate.ActiveState != null)
            {
                rstate = rstate.ActiveState;
                rstate.DoEntry((HOST)this);
            }

            activeStates.Sort((x, y) => x.Depth - y.Depth);
            return EResult.None;
        }

        public EResult Transit(Type stateType)
        {
            IState<HOST> state = null;
            if (!typeStates.TryGetValue(stateType, out state))
                return EResult.None;
            return Transit(state);
        }

        public EResult Transit<TSTATE>()
        { return Transit(typeof(TSTATE)); }

        public void Process<EVENT>(EVENT evt) where EVENT : IEvent
        {
            if (bSuspend) return;

            eventQueue.Enqueue(evt);
            int eventCount = eventQueue.Count;
            while (eventCount > 0){
                eventCount--;            
                IEvent pevent = eventQueue.Dequeue();
                foreach (IState<HOST> state in activeStates)
                    if (bSuspend || state.Process((HOST)this, pevent) == EResult.None)
                        break;
            }
        }

        public void PostEvent<EVENT>(EVENT evt) where EVENT : IEvent
        {
            if (bSuspend) return;
            eventQueue.Enqueue(evt);
        }

        public void Suspend()
        { bSuspend = true; }
        public void Resume()
        { bSuspend = false; }
    }