码迷,mamicode.com
首页 > 编程语言 > 详细

简单实用的 Unity 状态机设计

时间:2014-12-04 00:49:36      阅读:457      评论:0      收藏:0      [点我收藏+]

标签:style   blog   io   ar   color   使用   sp   on   div   

状态机是非常常用的游戏编程模式,状态机的设计也有简单或复杂的区别。

 

我脑海里的状态机

状态机是什么样的?这是一个非常典型的状态机设计(随手写的):

// 状态类
    class State
    {
        // 保存的状态机引用
        StateMachine _machine;

        // 构造状态,保存状态机引用
        public State(StateMachine machine) { _machine = machine; }

        // 进入状态
        public virtual void OnEnter() { }

        // 离开状态
        public virtual void OnLeave() { }

        // 更新状态
        public virtual void OnUpdate() { }
    }

    // 状态机类
    class StateMachine
    {
        // 当前状态
        State _state;

        // 改变状态
        public void ChangeState(State newState)
        {
            _state.OnLeave();
            _state = newState;
            _state.OnEnter();
        }

        // 更新当前状态
        void Update()
        {
            _state.OnUpdate();
        }
    }

这段代码主要表达了这样的思想:

1.状态含有进入、更新、离开 3 个可重写的方法;

2.状态机和状态的关系是 1 对多;

3.一个状态机内同一时间最多只有一个状态是“活动”的。

为什么没有看到 Transition 的存在?因为我认为添加这个类失去的比获得的更多。一方面状态转移应该存在于设计图上而不是代码里,另一方面转移的本质就是执行一个状态切换(可能还要做一些其他事情),这样的“一个过程”没有对象化的充足理由。这只是我的个人观点,如果你有想说的可以给我留言。

 

这个设计存在的问题

当你使用这个设计时,你需要继承状态类来实现自己的状态,重写 OnEnter,OnLeave 或 OnUpdate。想象一下为了执行一个简单的操作而定义一个新的类,然后使用这个类来创建一个状态实例,并且很可能这个类只需要用来创建一个状态实例。时间久了你可能会怀疑这一切的意义是什么。如果这还不算什么:你可能注意到了,状态保存了所属状态机的引用。在这些重写方法里,你不可避免的需要访问所属状态机的属性或方法,所以先保存状态机的引用是必要的。而且你可能还需要重新定义一个 get 属性,来将基本的 StateMachine 转换为自定义的状态机类型。想象一下每一次读取角色的生命值都要加一个“character.”的前缀。怎么样才能让事情变的更简单?

 

最后的解决办法

    // 状态
    public class State
    {
        // 进入
        public Action onEnter;

        // 离开
        public Action onLeave;

        // 更新
        public Action<float> onUpdate;
    }


    // 状态机
    public class StateMachine : MonoBehaviour
    {
        // 当前状态
        private State _state;
        public State state
        {
            // 获取当前状态
            get { return _state; }

            // 切换状态
            set
            {
                if (_state != null && _state.onLeave != null) _state.onLeave();
                _stateTime = 0;
                _state = value;
                if (_state != null && _state.onEnter != null) _state.onEnter();
            }
        }


        // 状态时间
        private float _stateTime;
        public float stateTime { get { return _stateTime; } }


        // 更新状态(在 FixedUpdate,Update 或 LateUpdate 中调用)
        protected void UpdateState(float deltaTime)
        {
            _stateTime += deltaTime;
            if (_state != null && _state.onUpdate != null) _state.onUpdate(deltaTime);
        }
    }

状态没有虚拟或抽象方法了,被替换为委托,并且不再保存状态机的引用。在实际使用中,大多数情况下都不需要继承 State 类了,转而为各个状态的委托初始化,用来初始化的所有方法可以直接写在状态机类里,因此这些方法可以直接访问状态机的所有成员。为了更方便在 Unity 中使用,状态机继承了 Monobehaviour,你可以根据实际需求选择不同的更新模式。另外为了方便编程添加了状态计时。

如果状态类还是需要多层次的继承呢?委托的好处之一就是自由。在继承的类里,你可以获得、替换或增加委托的方法,还有什么是不可实现的?

 

简单实用的 Unity 状态机设计

标签:style   blog   io   ar   color   使用   sp   on   div   

原文地址:http://www.cnblogs.com/whitecat/p/4141839.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!