1. 程式人生 > >Unity中狀態機模式的簡單封裝

Unity中狀態機模式的簡單封裝

遊戲中的邏輯少不了用狀態機。但是面對複雜的狀態(比如戰鬥角色控制,複雜的UI狀態控制等),用狀態機模式是最合適不過的了。結合C#的特點,我對狀態機模式進行了簡化。

完善的fsm:

public abstract class FSM<T> : MonoBehaviour {
    static System.Action FuncVoid = () => {};
    protected static System.Action<T, object> DefaultTrigger = (t, p) => { };

    public State state { get; private set; }
    public string Name;

    public void SetDefaultTrigger(System.Action<T, object> _SetTrigger) {
        DefaultTrigger = _SetTrigger;
    }

    public class State {
        public System.Action OnEnter = FuncVoid;
        public System.Action OnTick = FuncVoid;
        public System.Action OnExit = FuncVoid;
        public System.Action<T, object> SetTrigger = DefaultTrigger;
        public string Name { get; private set; }

        public State(string name) {
            Name = name;
        }
    }

    public void setState(State _state) {
        state.OnExit();
        state = _state;
        Name = state.Name;
        state.OnEnter();
    }

    protected void doStart() {
        if (state == null) {
            state = initState();
            state.OnEnter();
            StartCoroutine(OnTick());
        }
    }

    IEnumerator OnTick() {
        while (true) {
            state.OnTick();
            yield return null;
        }
    }

    protected abstract State initState();
}

下面是個例子:

public abstract class FSMManager<T> {
    public State current { get; protected set; }

    public abstract void Start();

    public class State {
        private string name;
        public System.Action OnEnter = FuncVoid;
        public System.Action OnTick = FuncVoid;
        public System.Action OnExit = FuncVoid;
        public System.Action<T, object[]> SetTrigger = FuncVoid2;
    }

    public static void FuncVoid() { }
    public static void FuncVoid2(T type, object[] ps) { }


    public void SetTrigger(T type, Object[] ps = null) {
        current.SetTrigger(type, ps);
    }

    public void SetState(State state) {
        current.OnExit();
        current = state;
        current.OnEnter();
    }

    public void OnTick() {
        current.OnTick();
    }
}

使用:

public class MyFSM : FSMManager<MyFSM.Type> {
    public enum Type {
        Type1,
        Type2,
        Typ3
    }
    public int count = 0;
    public State state1, state2;

    public MyFSM() {
        current = state1;
    }

    public override void Start() {
        current = state1;
        state1.OnEnter();
    }
}
public string test1= "111", test2 = "222";
public MyFSM fsm;
[Test]
public void NewEditModeTestSimplePasses() {
    // Use the Assert class to test conditions.

    fsm = new MyFSM() {
        state1 = new FSMManager<MyFSM.Type>.State() {
            OnEnter = delegate {
                Debug.Log("===>> " + fsm.count);
                if (fsm.count++ < 2) {
                    fsm.SetState(fsm.state1);
                } else {
                    fsm.SetState(fsm.state2);
                }
            },
        },
        state2 = new FSMManager<MyFSM.Type>.State() {
            OnEnter = delegate {
                Debug.Log("===>>>" + test2);
            }
        },
    };
    fsm.Start();
}

================ 另外的例子 ==================

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public abstract class FSM<T> : MonoBehaviour {
    static void FuncVoid() { }
    static void FuncVoid2(T type, object[] ps) { }

    protected State state;

    public class State {
        public System.Action OnEnter = FuncVoid;
        public System.Action OnTick = FuncVoid;
        public System.Action OnExit = FuncVoid;
        public System.Action<T, object[]> SetTrigger = FuncVoid2;
        public int param;
    }

    public void setState(State _state) {
        state.OnExit();
        state = _state;
        state.OnEnter();
    }

    protected void doStart(State ss) {
        state = initState();
        StartCoroutine(OnTick());
    }

    IEnumerator OnTick() {
        while (true) {
            state.OnTick();
            yield return null;
        }
    }

    protected abstract State initState();
}

public class UILoading : FSM<UILoading.MyEvent> {
    public enum MyEvent {
        Trig1,
        Trig2,
        Trig3
    }
    State state1, state2;

    public Animator anim;

    public Button btnLocal;
    public Button btnOnline;
    public Button btnOptions;


    void printOK(string who) {
        Debug.LogError("=====OKOKOK!! " + who);
    }

    private void Awake() {
        doStart(state1);

        btnLocal.onClick.AddListener(delegate {
            state.SetTrigger(MyEvent.Trig1, null);
        });

        btnOnline.onClick.AddListener(delegate {
            state.SetTrigger(MyEvent.Trig2, null);
        });
        anim.StopPlayback();
        btnOptions.onClick.AddListener(delegate {
            anim.StartPlayback();
            Debug.LogError("======");
        });

    }

    protected override State initState() {
        state1 = new State() {
            param = 7,
            OnEnter = delegate {
                state1.param = 5;
                printOK("state1");
            },
            OnTick = delegate {
                state1.param--;
                Debug.LogError("State1 tick : " + state1.param);

                if (state1.param <= 0) {
                    setState(state2);
                }
            },
            OnExit = delegate {
                Debug.LogError("<<< State1 exit");
            }
        };
        state2 = new State() {
            OnEnter = delegate {
                printOK("state2");
            },
            OnTick = delegate {
                Debug.LogError("state2 Tick");
            },
            OnExit = delegate {
                Debug.LogError("<<< State2 exit");
            },
            SetTrigger = (e, ps) => {
                switch (e) {
                    case MyEvent.Trig1:
                        setState(state1);
                        break;
                    case MyEvent.Trig2:
                        printOK("!!!!!");
                        break;
                }
            }
        };
        return state1;
    }
}