首页 > 其他 > 详细

C# FSM (仿Unity 中的Mecanim动画系统的状态机)

时间:2014-03-11 17:42:46      阅读:878      评论:0      收藏:0      [点我收藏+]

刚自学Unity,发现Unity 之所以很火,因为很多系统,编辑器,都设计得如此的强大;

大家用起来都比较容易维护。


自学中,发现,一个挺有趣的系统:Animator,这个在Unity 中,有一个独立的子面板,其中,大家如下面:

bubuko.com,布布扣


以上,就是单个动画的状态机可视化管理界面;

any state就是每次都会处理的状态;(任意状态)

idle(闲置状态)

run(跑动状态)

dead(死亡状态)


大家应该也发现了,idle、run、dead这些状态之间都有一些带有方向的线段连接起来;

这些带方向的线段就是:Transition(等会我的代码,会有对应的类),意思就是设置了一些过渡参数,来从,原来的状态,过渡到:指定的状态;

如图:

bubuko.com,布布扣


而每个Transition其实是可以有多个:过渡管道;(Pipeline,有对应的类),就是管理,状态之间可以过渡的参数管理

bubuko.com,布布扣

以上是单个Pipeline,一个Transition中,含多个Pipeline的话,左边的视图中,带方向的的Transition外观会变成:带有三个方向的箭头:

如图:

bubuko.com,布布扣

以上,一个Transition有,两个Pipeline,第一个是当speed>10时(红色线部份),会从:idle过渡到:run状态,

而第二个Pipeline是,hp>50(看绿色线的部份),也会从:idle过渡到:run状态;

多个Pipeline之间的逻辑是:or,运算符:||的关系,意思是:

if (speed > 10 || hp > 50) 

    // transilated to run


其中,上图中,大家都应该看到了:Condition,speed,Greater,或是hp,Greater这些设置过渡的参数;

对应FSMParam类,这些参数,是存在于单个FSM的,如图:

bubuko.com,布布扣


FSM相关的声明:(注释挺少的,不过代码600多行,我就不贴上来了,有提供百度网盘下载就可以了,往下看)

    /// <summary>
    /// 状态机中,当前状态发生改变的事件委托声明
    /// </summary>
	public delegate void CurStateChangedEventHandler(FSM sender);

	/// <summary>
    /// 状态机中,当有参数发生变化时的事件委托声明
    /// </summary>
    public delegate void ParamsChangedEventHandler(FSM sender, ParamsChangedEvent args);

    /// <summary>
    /// 过渡参数为函数委托的声明
    /// </summary>
	public delegate bool FSMFuncParamHandler(params object[] objs);

    /// <summary>
    /// 状态机中,当有参数发生变化时的参数类声明
    /// </summary>
    public class ParamsChangedEvent : EventArgs;

    /// <summary>
    /// 有限状态机
    /// (
    ///     设计思路,参考:Unity 4.3.2f 版本的Mecanim动画系统中的状态机,
    ///     根据使用上的功能,来猜想实现思路,当然可能我的实现方式不是最好,
    ///     如果大家还有比较好的一些见解,那大家一起交流交流吧。
    ///     该博文就不多说其它的,我的文采也不好,直接上代码吧。
    /// )
    /// @author :   Jave.Lin(afeng)
    /// @time   :   2014-03-11
    /// @version:   1.0
    /// </summary>
	public class FSM;

	/// <summary>
    /// 状态
    /// </summary>
	public abstract class State;

	/// <summary>
    /// 状态之间的过度处理(可包含多个过度管道)
    /// </summary>
	public class Transition;

	/// <summary>
    /// 状态之间的过度管道(可包含多个过度条件)
    /// </summary>
	public class ChangStatePipeline;

	/// <summary>
    /// 过度参数的成立条件类型
    /// </summary>
	[Flags]
	public enum ConditionType;

	/// <summary>
    /// 过度参数类型
    /// </summary>
	public enum FSMParamType;

	/// <summary>
    /// 过度参数是函数类型
    /// </summary>
	public class FSMParamFunc : FSMParam;

	/// <summary>
    /// 过度参数是值类型
    /// </summary>
	public class FSMParamValue : FSMParam;

	/// <summary>
    /// 过度的参数类
    /// </summary>
	public abstract class FSMParam;

调用部份:(部份不算很多,我就全部拿上来了,这里比较清楚的可以看到,我们平时在Unity 操作Mecanim的一些设置中,大概内部是如何调用的一个过程)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace FSMTestingProject
{
    /// <summary>
    /// 测试:有限状态机
    /// @author :   Jave.Lin(afeng)
    /// @time   :   2014-03-11
    /// @version:   1.0
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            Console.ForegroundColor = ConsoleColor.Green;

            var fsm = new FSM("Jave‘s FSM");

            Console.WriteLine(string.Format("Start testing :{0}", fsm));

            // add event show cur state changed info
            fsm.CurStateChangedEvent += (CurStateChangedEventHandler)((sender) =>
            {
                Console.WriteLine(string.Format("fsm : {0}, had changed cur state, last state : {1}, cur state : {2}", fsm, fsm.LastState, fsm.CurState));
            });
            // add event show params changed info
            fsm.ParamsChangedEvent += (ParamsChangedEventHandler)((sender, paramChangedArgs) =>
            {
                Console.WriteLine(string.Format(
                    "param:{1}, src:{2}, to: {3}",
                    fsm, paramChangedArgs.CurValue.Name, paramChangedArgs.LastValue.Value, paramChangedArgs.CurValue.Value));
            });

            // add some states
            fsm.AddState(new MyState(fsm, "idle"));
            fsm.AddState(new MyState(fsm, "run"));
            fsm.AddState(new MyState(fsm, "dead"));
            fsm.AnyState = new MyState(fsm, "any state");
            // set cur state
            fsm.SetCurState(fsm.GetState("idle"));
            // add fsm some params
            fsm.AddParam(new FSMParamValue("speed") { Value = 0 }); // init speed = 0;
            fsm.AddParam(new FSMParamValue("hp") { Value = 100 }); // init hp = 100

            // idle state add some transition to run
            var idleState = fsm.GetState("idle");
            var runState = fsm.GetState("run");
            if (idleState != null && runState != null)
            {
                // create transition
                var toRunTransition = new Transition(idleState, runState);
                
                // create transition pipelines
                var pipeline = new ChangStatePipeline(idleState, runState);
                // create pipelines conditions , and add condition
                var condition = new PipelineCondition(pipeline, "speed", 10, ConditionType.GreaterEquals);
                pipeline.AddCondition(condition);

                // transition add pipeline
                toRunTransition.AddPipeline(pipeline);

                // at last, state add transition
                idleState.AddTransition(toRunTransition);
            }
            // run back to idle
            if (idleState != null && runState != null)
            {
                // create transition
                var toRunTransition = new Transition(runState, idleState);

                // create transition pipelines
                var pipeline = new ChangStatePipeline(runState, idleState);
                // create pipelines conditions , and add condition
                var condition = new PipelineCondition(pipeline, "speed", 10, ConditionType.Less);
                pipeline.AddCondition(condition);

                // transition add pipeline
                toRunTransition.AddPipeline(pipeline);

                // at last, state add transition
                runState.AddTransition(toRunTransition);
            }
            // idle or run state add some transition to dead state‘s transitions
            var deadState = fsm.GetState("dead");
            if (idleState != null && deadState != null && runState != null)
            {
                // idle to dead
                var trans1 = ToDeadStateTransition(idleState, deadState);
                idleState.AddTransition(trans1);
                // run to dead
                var trans2 = ToDeadStateTransition(runState, deadState);
                runState.AddTransition(trans2);
            }

            //const int updatePerMs = (int)(1f / 60f * 1000);
            const int updatePerMs = (int)(1000);

            bool speedAdding = true;

            // dummy fsm update task
            var task = Task.Factory.StartNew((Action)(() => 
            {
                while (true)
                {
                    fsm.Update();

                    var speedParam = fsm.GetParam<FSMParamValue>("speed");

                    if (Convert.ToDouble(speedParam.Value) <= 0)
                        speedAdding = true;
                    else if (Convert.ToDouble(speedParam.Value) > 10)
                        speedAdding = false;
                    fsm.SetParamValue("speed", Convert.ToDouble(speedParam.Value) + (speedAdding ? 1 : -1)); // to run when speed greater and equals 10, back to idle when speed less than 10

                    var hpParam = fsm.GetParam<FSMParamValue>("hp");
                    if (Convert.ToDouble(hpParam.Value) > 0)
                    {
                        fsm.SetParamValue("hp", Convert.ToDouble(hpParam.Value) - 5); // minus 10 hp per frame, to dead when hp less and equals 0
                    }
                    Thread.Sleep(updatePerMs);
                }
            }));

            Console.ReadLine();
        }

        static Transition ToDeadStateTransition(State srcState, State targetState)
        {
            var result = new Transition(srcState, targetState);

            var pipeline = new ChangStatePipeline(srcState, targetState);
            var condition = new PipelineCondition(pipeline, "hp", 0, ConditionType.LessEquals);
            pipeline.AddCondition(condition);

            result.AddPipeline(pipeline);
            return result;
        }
    }

    class MyState : State
    {
        public MyState(FSM fsm, string name)
            : base(fsm, name)
        {

        }

        public override void EnterState()
        {
            Console.WriteLine(string.Format("->Enter State : {0}", Name));
        }

        public override void LeaveState()
        {
            Console.WriteLine(string.Format("<-LeaveState : {0}", Name));
        }

        public override void Excute()
        {
            Console.WriteLine(string.Format("$$Excute State : {0}", Name));
        }
    }
}

项目运行后,可以看到:

先有:idle 的speed一步一步的+1,到speed >= 10时,状态就变成了:run;

然后再有:run,从speed = 11; 变到speed < 10时,状态变成了:idle;

如图:

bubuko.com,布布扣


最后,大家比较关注的:源项目

转载的话,注声明:

转载于:http://blog.csdn.net/linjf520/article/details/21010637

C# FSM (仿Unity 中的Mecanim动画系统的状态机),布布扣,bubuko.com

C# FSM (仿Unity 中的Mecanim动画系统的状态机)

原文:http://blog.csdn.net/linjf520/article/details/21010637

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!