Unity 下简易状态机的实现
来源:互联网 发布:网络繁忙请稍后再试 编辑:程序博客网 时间:2024/05/18 22:44
Unity 下简易状态机的实现
国庆到处都是人,我的建议是要么出国旅游,要么就宅在家把时间留给自己或家人。朋友约我去星巴克坐着重构代码,想想也是程序猿之间才有的默契。
关于状态机,我想都知道它很常用,就像单例一样,缺了它们,我们在游戏编程世界里呼吸都不畅快。网上也有很多大神写的状态机,比如PlayMaker这样重量级的插件,功能很强大,依赖也不少。程序猿的胃口有时候就是很挑剔,吃惯了大鱼大肉,也想整点清粥小菜。应该有很多朋友跟我一样,希望有一套简易的状态机实现,每行代码自己都了如指掌,实现我们一些简单的状态控制的需求。In hand,伸手就来的方便!
下面是我的实现,有错误或者low的地方,欢迎大家留言,一起进步吧!
讲到状态机,首当其冲的肯定是状态,我们把状态细分为不同的阶段,每个阶段处理各自的事情。状态里最基本要有OnEnter,OnExit,OnUpdate三个阶段的处理。在 Unity下,我把OnUpdate分得更细,原因在于有些操作需要放在特定的帧下面执行,比如相机的跟随,角色的移动等。
/// <summary> /// 状态基类. /// </summary> public class FiniteState { public string Name { get { return GetType().Name; } } public virtual void OnEnter(object context){} public virtual void OnExit(object context){} public virtual void OnUpdate(object context){} public virtual void OnFixedUpdate(object context){} public virtual void OnLateUpdate(object context){} }}
Transition是有限状态机里面不可或缺的三要素之一,它主要约束状态之间的过渡,A和B两个状态之间是否存在过渡关系,以及过渡的条件是什么,就由FiniteStateTransition来决定。
using UnityEngine;using System.Collections;namespace PLY.StateMachine{ /// <summary> /// 状态机过渡约束基类. /// </summary> public class FiniteStateTransition { public string EventName; public string LastStateName; public string NextStateName; public FiniteStateTransition(string eventName, string lastStateName, string nextStateName){ EventName = eventName; LastStateName = lastStateName; NextStateName = nextStateName; } }
最后是管理所有状态及其之间 Transition关系的状态机,它提供映射 Transition的方法,接收触发事件并过渡到对应的状态。
using UnityEngine;using System.Collections;using System.Collections.Generic;namespace PLY.StateMachine { /// <summary> /// 有限状态机基类. /// </summary> [System.Serializable] public class FiniteStateMachine : MonoBehaviour { private object Context_; public string CurrentStateName = "EmptyState"; private FiniteState CurrentState_; protected FiniteState CurrentState{ get{ return CurrentState_; } set{ CurrentState_ = value; CurrentStateName = CurrentState_.Name; } } protected FiniteState defaultState; private Dictionary<string, FiniteState> allStates_ = new Dictionary<string, FiniteState>(); private List<FiniteStateTransition> allTransitions_ = new List<FiniteStateTransition>(); protected virtual void Init(){} protected void AddState(FiniteState state){ allStates_.Add(state.Name, state); } protected void RemoveState(FiniteState state){ allStates_.Remove(state.Name); } /// <summary> /// 添加过渡. /// </summary> /// <param name="eventName">Event name.</param> /// <typeparam name="T1">源状态类型.</typeparam> /// <typeparam name="T2">目标状态类型.</typeparam> protected void AddTransition<T1,T2>(string eventName)where T1 : FiniteState where T2 : FiniteState{ string lastStateName = typeof(T1).Name; string nextStateName = typeof(T2).Name; FiniteStateTransition transition = new FiniteStateTransition(eventName, lastStateName, nextStateName ); allTransitions_.Add(transition); } protected void AddTransition(FiniteStateTransition transition){ allTransitions_.Add(transition); } protected void RemoveTransation(FiniteStateTransition transition){ allTransitions_.Remove(transition); } public void SetContext(object context){ Context_ = context; } /// <summary> /// 发送事件,触发下一状态的切换 /// </summary> /// <param name="eventName">事件名.</param> public void SendEvent(string eventName){ for (int i = 0; i < allTransitions_.Count; i++) { FiniteState nextState; if (TryGetNextStateByEventName(allTransitions_[i], eventName, out nextState)){ ChangeState(nextState); break; } } } private bool TryGetNextStateByEventName(FiniteStateTransition transation, string eventName, out FiniteState state){ state = null; if (CurrentState == null || !CurrentState.Name.Equals(transation.LastStateName)) return false; if (!eventName.Equals(transation.EventName)) return false; return (allStates_.TryGetValue(transation.NextStateName, out state)); } /// <summary> /// 切换状态. /// </summary> /// <param name="newState">New state.</param> private void ChangeState(FiniteState newState){ if (CurrentState != null){ CurrentState.OnExit(Context_); } else{ Debug.LogError("当前状态为空,退出状态失败!", gameObject); } CurrentState = newState; if(CurrentState != null){ CurrentState.OnEnter(Context_); } else{ Debug.LogError("当前状态为空,进入状态失败!", gameObject); } } private void SetDefaultToCurrent(){ CurrentState = defaultState; } private void Awake(){ Init(); SetDefaultToCurrent(); } private void Start () { if (CurrentState != null){ CurrentState.OnEnter(Context_); } } private void Update () { if (CurrentState != null){ CurrentState.OnUpdate(Context_); } } private void FixedUpdate(){ if (CurrentState != null){ CurrentState.OnFixedUpdate(Context_); } } private void LateUpdate(){ if (CurrentState != null){ CurrentState.OnLateUpdate(Context_); } } }}
下面是我的测试用例了,简单写了一个关于锁的状态机,两种状态:锁住,未锁住。
using UnityEngine;using System.Collections;using PLY.StateMachine;public class PlayerStateMachine : FiniteStateMachine{ protected override void Init (){ Locked state1 = new Locked(); AddState(state1); Unlocked state2 = new Unlocked(); AddState(state2); defaultState = state1; AddTransition<Locked, Unlocked>("UNLOCK"); AddTransition<Unlocked, Locked>("LOCK"); }}public class Locked : FiniteState{ public override void OnEnter (object target){ Player owner = target as Player; owner.Show(true); } public override void OnExit (object target){} public override void OnUpdate (object target){}}public class Unlocked : FiniteState{ public override void OnEnter (object target){ Player owner = target as Player; owner.Show(false); } public override void OnExit (object target){} public override void OnUpdate (object target){}}
using UnityEngine;using System.Collections;using PLY.StateMachine;public class Player : MonoBehaviour{ public FiniteStateMachine StateMachine; private bool isShowingLockedView_; private Texture LockTex_; private Texture UnlockTex_; private void Awake(){ StateMachine.SetContext(this); LockTex_ = Resources.Load("tex_locked") as Texture; UnlockTex_ = Resources.Load("tex_unlocked") as Texture; } private void OnGUI(){ if(GUI.Button(new Rect(0,0,120,30), "UNLOCK")){ StateMachine.SendEvent("UNLOCK"); } if(GUI.Button(new Rect(0,40,120,30), "LOCK")){ StateMachine.SendEvent("LOCK"); } if(isShowingLockedView_){ GUI.Label(new Rect(Screen.width/2,50,128,128), new GUIContent(LockTex_)); } else{ GUI.Label(new Rect(Screen.width/2,50,128,128), new GUIContent(UnlockTex_)); } } public void Show(bool isLocked){ isShowingLockedView_ = isLocked; }}
0 0
- Unity 下简易状态机的实现
- unity怪物的状态机
- Unity中实现一个状态机
- TCP拥塞状态机的实现(下)
- Unity实现简易扫雷
- Unity中状态机的使用
- Unity动画,状态机的使用
- 基于状态机的简易RISC CPU设计
- unity状态机
- qt状态机的实现
- 标准状态机的实现
- 状态机的实现
- 状态机的实现
- lua实现的状态机
- 简单的状态机实现
- 状态机的实现
- 状态机的实现
- 单状态机的实现
- 2015国庆第一场——四川省赛练习
- ng 机器学习第二讲
- 三色旗
- IOS开发笔记-01按钮操作-05.IBAction&IBOutlet 06.加法计算器小结 07.关闭键盘
- 1 java程序设计概述
- Unity 下简易状态机的实现
- LeetCode题解:Flatten Binary Tree to Linked List
- codeforces 466D Increase Sequence DP
- LeetCode题解:Populating Next Right Pointers in Each Node
- equal和==的区别(原理讲解)
- Mysql命令大全
- 下载微软虚拟学院视频字幕,解决本地播放没有字幕
- TCP/IP详解卷1 读书笔记:第七章 Ping程序
- LeetCode题解:Triangle