2018-04-26 08:56:17 qq_33994566 阅读数 307

    当我们在项目中有需要根据周围状况而改变自身状态的需求时,我们就可以使用状态机来实现。下面是我在WIKI上搜索的状态机示例程序。

    首先是状态机状态以及管理脚本。

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

//这里是切换状态变量
public enum Transition
{
    NullTransition = 0, // Use this transition to represent a non-existing transition in your system
    SawPlayer,
    LostPlayer
}

//这个是每个状态的ID标识
public enum StateID
{
    NullStateID = 0, // Use this ID to represent a non-existing State in your system
    ChasingPlayer,
    FollowingPath
}


public abstract class FSMState
{
    protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
    protected StateID stateID;
    public StateID ID { get { return stateID; } }

    public void AddTransition(Transition trans, StateID id)
    {
        // Check if anyone of the args is invalid
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
            return;
        }

        if (id == StateID.NullStateID)
        {
            Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
            return;
        }

        // Since this is a Deterministic FSM,
        //   check if the current transition was already inside the map
        if (map.ContainsKey(trans))
        {
            Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
                           "Impossible to assign to another state");
            return;
        }

        map.Add(trans, id);
    }

    public void DeleteTransition(Transition trans)
    {
        // Check for NullTransition
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSMState ERROR: NullTransition is not allowed");
            return;
        }

        // Check if the pair is inside the map before deleting
        if (map.ContainsKey(trans))
        {
            map.Remove(trans);
            return;
        }
        Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +
                       " was not on the state's transition list");
    }

    public StateID GetOutputState(Transition trans)
    {
        // Check if the map has this transition
        if (map.ContainsKey(trans))
        {
            return map[trans];
        }
        return StateID.NullStateID;
    }

    /// <summary>
    /// This method is used to set up the State condition before entering it.
    /// It is called automatically by the FSMSystem class before assigning it
    /// to the current state.
    /// </summary>
    public virtual void DoBeforeEntering() { }

    /// <summary>
    /// This method is used to make anything necessary, as reseting variables
    /// before the FSMSystem changes to another one. It is called automatically
    /// by the FSMSystem before changing to a new state.
    /// </summary>
    public virtual void DoBeforeLeaving() { }

    /// <summary>
    /// This method decides if the state should transition to another on its list
    /// NPC is a reference to the object that is controlled by this class
    /// </summary>
    public abstract void Reason(GameObject player, GameObject npc);//一般用于检测状态是否需要改变  详情下面NPC代码
    /// <summary>
    /// This method controls the behavior of the NPC in the game World.
    /// Every action, movement or communication the NPC does should be placed here
    /// NPC is a reference to the object that is controlled by this class
    /// </summary>
    public abstract void Act(GameObject player, GameObject npc);//执行该状态时要做的事情  详情下面NPC代码


} // class FSMState


/// <summary>
/// FSMSystem class represents the Finite State Machine class.
///  It has a List with the States the NPC has and methods to add,
///  delete a state, and to change the current state the Machine is on.
/// </summary>
public class FSMSystem
{
    private List<FSMState> states;

    // The only way one can change the state of the FSM is by performing a transition
    // Don't change the CurrentState directly
    private StateID currentStateID;
    public StateID CurrentStateID { get { return currentStateID; } }
    private FSMState currentState;
    public FSMState CurrentState { get { return currentState; } }


    public FSMSystem()
    {
        states = new List<FSMState>();
    }

    /// <summary>
    /// This method places new states inside the FSM,
    /// or prints an ERROR message if the state was already inside the List.
    /// First state added is also the initial state.
    /// </summary>
    public void AddState(FSMState s)
    {
        // Check for Null reference before deleting
        if (s == null)
        {
            Debug.LogError("FSM ERROR: Null reference is not allowed");
        }

        // First State inserted is also the Initial state,
        //   the state the machine is in when the simulation begins
        if (states.Count == 0)
        {
            states.Add(s);
            currentState = s;
            currentStateID = s.ID;
            return;
        }

        // Add the state to the List if it's not inside it
        foreach (FSMState state in states)
        {
            if (state.ID == s.ID)
            {
                Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() +
                               " because state has already been added");
                return;
            }
        }
        states.Add(s);
    }

    /// <summary>
    /// This method delete a state from the FSM List if it exists, 
    ///   or prints an ERROR message if the state was not on the List.
    /// </summary>
    public void DeleteState(StateID id)
    {
        // Check for NullState before deleting
        if (id == StateID.NullStateID)
        {
            Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
            return;
        }

        // Search the List and delete the state if it's inside it
        foreach (FSMState state in states)
        {
            if (state.ID == id)
            {
                states.Remove(state);
                return;
            }
        }
        Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +
                       ". It was not on the list of states");
    }

    /// <summary>
    /// This method tries to change the state the FSM is in based on
    /// the current state and the transition passed. If current state
    ///  doesn't have a target state for the transition passed, 
    /// an ERROR message is printed.
    /// </summary>
    public void PerformTransition(Transition trans)
    {
        // Check for NullTransition before changing the current state
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
            return;
        }

        // Check if the currentState has the transition passed as argument
        StateID id = currentState.GetOutputState(trans);
        if (id == StateID.NullStateID)
        {
            Debug.LogError("FSM ERROR: State " + currentStateID.ToString() + " does not have a target state " +
                           " for transition " + trans.ToString());
            return;
        }

        // Update the currentStateID and currentState		
        currentStateID = id;
        foreach (FSMState state in states)
        {
            if (state.ID == currentStateID)
            {
                // Do the post processing of the state before setting the new one
                currentState.DoBeforeLeaving();

                currentState = state;

                // Reset the state to its desired condition before it can reason or act
                currentState.DoBeforeEntering();
                break;
            }
        }

    } 
} 

然后创建一个NPC代码实现类来实现我们的NPC控制。

using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class NPCControl : MonoBehaviour
{
    public GameObject player;
    public Transform[] path;
    private FSMSystem fsm;

    public void SetTransition(Transition t) { fsm.PerformTransition(t); }

    public void Start()
    {
        MakeFSM();
    }

    public void FixedUpdate()
    {
        fsm.CurrentState.Reason(player, gameObject);
        fsm.CurrentState.Act(player, gameObject);
    }

    // The NPC has two states: FollowPath and ChasePlayer
    // If it's on the first state and SawPlayer transition is fired, it changes to ChasePlayer
    // If it's on ChasePlayerState and LostPlayer transition is fired, it returns to FollowPath
    private void MakeFSM()//创建一个状态机  然后把状态添加到状态机里进行管理
    {
        FollowPathState follow = new FollowPathState(path);
        follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer);

        ChasePlayerState chase = new ChasePlayerState();
        chase.AddTransition(Transition.LostPlayer, StateID.FollowingPath);

        fsm = new FSMSystem();
        fsm.AddState(follow); //默认第一个添加的状态为默认状态  我们也可以更改默认状态
        fsm.AddState(chase);
    }
}

public class FollowPathState : FSMState
{
    private int currentWayPoint;
    private Transform[] waypoints;

    public FollowPathState(Transform[] wp)
    {
        waypoints = wp;
        currentWayPoint = 0;
        stateID = StateID.FollowingPath;
    }

    public override void Reason(GameObject player, GameObject npc)
    {
        // If the Player passes less than 15 meters away in front of the NPC
        RaycastHit hit;
        if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15F))
        {
            if (hit.transform.gameObject.tag == "Player")
                npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);
        }
    }

    public override void Act(GameObject player, GameObject npc)
    {
        // Follow the path of waypoints
        // Find the direction of the current way point 
        Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
        Vector3 moveDir = waypoints[currentWayPoint].position - npc.transform.position;

        if (moveDir.magnitude < 1)
        {
            currentWayPoint++;
            if (currentWayPoint >= waypoints.Length)
            {
                currentWayPoint = 0;
            }
        }
        else
        {
            vel = moveDir.normalized * 10;

            // Rotate towards the waypoint
            npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
                                                      Quaternion.LookRotation(moveDir),
                                                      5 * Time.deltaTime);
            npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);

        }

        // Apply the Velocity
        npc.GetComponent<Rigidbody>().velocity = vel;
    }

} // FollowPathState

public class ChasePlayerState : FSMState
{
    public ChasePlayerState()
    {
        stateID = StateID.ChasingPlayer;
    }

    public override void Reason(GameObject player, GameObject npc)
    {
        // If the player has gone 30 meters away from the NPC, fire LostPlayer transition
        if (Vector3.Distance(npc.transform.position, player.transform.position) >= 30)
            npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);
    }

    //转到追逐状态  npc开始追寻Player
    public override void Act(GameObject player, GameObject npc)
    {
        // Follow the path of waypoints
        // Find the direction of the player 		
        Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
        Vector3 moveDir = player.transform.position - npc.transform.position;

        // Rotate towards the waypoint
        npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
                                                  Quaternion.LookRotation(moveDir),
                                                  5 * Time.deltaTime);
        npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);

        vel = moveDir.normalized * 10;

        // Apply the new Velocity
        npc.GetComponent<Rigidbody>().velocity = vel;
    }

} 
   
2016-08-31 11:51:16 ios_song 阅读数 3810

Unity高级——状态机——Switch状态机

我们将要学习的内容是Switch状态机,前一节我们通过if写了一个简单的功能,就是开关灯的功能,但是开关灯的功能,并不能称之为完整的状态机,虽然说它包含了状态机中所必须的元素或者概念。比如里面也呈现了一些状态的过程……但是它并不是一个完整的状态机

 

最最简单的一个的状态机,一般我们都会使用Switch语句去实现,因为我们使用Switch的时候,会和枚举一起使用,用枚举来表示我们的状态机中不同的状态。然后用Switch来判断,当前处于什么状态再来解决应该选择哪些逻辑代码进行执行。Switch本身就是一个分支语句。


using UnityEngine;
using System.Collections;

public class LightController : MonoBehaviour {

    private Light _light;

    void Awake(){
        _light = GetComponent<Light> ();
    }

    void  Update(){
        //当前的脚步就是状态机
        //用户按键时间就是状态切换的条件——事件
        if(Input.GetKeyDown(KeyCode.O)){

            //灯的光强从0变为1时,就是状态的过渡
            //打开灯
            //灯开着的状态
            _light.intensity=1f;
    
        }

        if(Input.GetKeyDown(KeyCode.C)){

            //灯的光强从1变为0时,就是状态的过渡
            //关闭灯
            //灯处于关着的状态
            _light.intensity=0f;
        
        }
    }
}

接下来我们要模拟灯光忽明忽暗的效果!

而我们当前的代码是无法实现,我们当前的代码不能准备的去判断,我们当前的这个灯到底是处于开着的,还是关这的,接下来我们要改一下一下代码!

Update()里面的内容删除,在Update()中,使用switch来实现!



using UnityEngine;
using System.Collections;

//枚举用来表示灯光中的状态
public enum LightStates{
    Open,//打开状态
    Close,//关闭状态
    
}

public class LightController : MonoBehaviour {

    private Light _light;//当前游戏对象上的灯光组件
    private LightStates _currentStatre;//当前状态


    void Awake(){
        //初始化成员变量
        _light = GetComponent<Light> ();
    }

    void  Update(){
        //if语句实现状态机效果
        //当前的脚步就是状态机
        //用户按键时间就是状态切换的条件——事件
//        if(Input.GetKeyDown(KeyCode.O)){


            //灯的光强从0变为1时,就是状态的过渡
            //打开灯
            //灯开着的状态
//            _light.intensity=1f;

    
//        }

//        if(Input.GetKeyDown(KeyCode.C)){


            //灯的光强从1变为0时,就是状态的过渡
            //关闭灯
            //灯处于关着的状态
//            _light.intensity=0f;



        //使用Switch进行状态检测
        switch (_currentStatre) {
        case LightStates.Open:
            //当处于Open状态时要执行的代码
            print ("Open....");
            OnOpenState ();
            break;
        case LightStates.Close:
            //当处于Close状态时要执行的代码
            print ("Close...");
            OnCloseState ();
            break;
        }
        }
        //当处于Open状态时执行的代码
        private void OnOpenState(){
        //检测按键时间——按下C键是关灯
        if(Input.GetKeyDown(KeyCode.C)){
            _light.intensity = 0f;
            //更新当前状态
            _currentStatre = LightStates.Close;
            
        }
        
        }
        //当处于Close状态时要执行的代码
        private void OnCloseState(){
        //检测按键时间——按下O键是关灯
        if(Input.GetKeyDown(KeyCode.O)){
            _light.intensity = 1f;
            //更新当前状态
            _currentStatre = LightStates.Open;

        }

    }
}

 

 

 

2016-08-26 13:15:56 ios_song 阅读数 10845
一、状态机

状态机能够分离逻辑代码,提高代码的可维护性和重用性


本节课我们来学习,在Unity中使用状态机,来实现一些逻辑的划分,首先我们需要了解一下,为什么开发的时候使用状态机?
之前有过开发经验的朋友,可能会知道在应用开发中,最常用的一种框架就是MVC框架。
也就是视图模型控制器这个框架,但是我们在游戏开发中,会发现很难去应用MVC框架。
是因为我们的游戏开发,并不像网站或者其他应用开发那样,模型,视图和控制器在游戏开发的过程中,它们的界限很模糊,划分的不是很清晰。这是因为我们游戏类型的不同,游戏本身底层架构就会相差很远!而且在游戏中,不同的游戏对象往往存在大量的交互的这种现象。
所以,这种垂直的MVC框架就不太适用于游戏开发。但是我依然可以在游戏开发UI部分使用MVC框架。
除了UI,我们也需要某种程度上去分离游戏中的一些程序代码。比如说我们游戏中需要些一个脚本来控制角色的一些逻辑。角色的移动,攻击等属性时,我们不可能把代码放在一个脚本中完成,我们肯定要通过一定的方法,把它们划分成多个脚本或者说多个部分,依次来实现。因为这样把复杂的代码划分成一个一个小部分去实现,可以提高我们代码的可维护性,以及重用性,这也就是为什么我们要使用状态机的原因!

状态机就可以帮助我们分离逻辑代码。


二、有限状态机
有限状态机(FSM)简称状态机
FSM的状态是有限个数的
有限状态机就是为了表示有限个状态,以及这些状态直接的过渡行为的一个模型。在游戏的开发过程中,有限状态机能够将一个复杂的功能或者说复杂的逻辑简化为若干个稳定的状态!之后,在这些状态中进行事件的判断。

举一个例子:以人物角色为例

人物角色我们使用状态机来实现人物的复杂的逻辑的话。我们首先会根据我们的游戏玩法或者功能,来把游戏角色划分为不同个状态。如游戏角色处于闲置的状态,也有可能处于攻击的状态,可能处于行走的状态,可能处于副本的状态,交接任务的状态等等……

我们把这些逻辑代码都按照状态来划分开来之后,我们在单独在某个状态中,实现特定的代码,如果把这些游戏角色划分出有一个是战斗状态,只要战斗相关的代码我们都会放在战斗状态中去实现。肯定不能放到行走的状态中去实现的。

这个就是使用状态机来帮助我们逻辑代码进行分离。

举例:灯泡的状态图


灯泡分为两个状态:开和关。在任意时刻,灯泡要么处于开的状态,要么处于关的状态。如果我们当前灯泡是开着的,如果我们想把灯泡关闭,我们只需要关闭开关就可以了。反过来,如果我们当前灯泡是关着的,我们想要打开灯泡,只需要打开开关即可。

在这个灯泡的状态图中我们可以分类出两个状态,这里面的状态(State)就是为了存储灯泡的一些信息的或者执行某些动作。

如果我们从开的状态切换大关的状态这中间就叫状态过渡(Transition)

状态过渡就是用来描述:状态直接的转换的。
我们关闭开关和打开开关之间就是状态过渡的条件,它们的作用就是用来触发一个状态过渡的,我们称为过渡事件(Event)!

我们在开或者关的状态中,还会有一个动作,这个动作可能是持续执行的或者一瞬间执行的,称为动作(Action)!

图片中所有元素组合到一起,就是一个状态机!
三、回到Unity中进行演示——把灯泡这个功能Unity中实现一下
首先为了项目准备了一些材质球,然后我们就开始

在场景中创建一个Plane——然后我们把Plane染成绿色,然后在创建中,创建一个Cube,给Cube染一个紫色,然后我们调整摄像机,对准Cube……未完待续……




2017-10-31 15:33:46 qq_33747722 阅读数 4635

原文地址:blog.liujunliang.com.cn

在之前有过介绍一个可视化有限状态机编辑器插件PlayerMaker

在这里也可以在我们的代码中实现一个状态机

本文源码地址:点击打开链接

首先创建一个脚本,来管理我们的各个状态

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

/// <summary>
/// 状态ID
/// </summary>
public enum FSMStateID
{
    NullFSMStateID,
    PatrolFSMStateID,//巡逻状态
    ChaseFSMStateID,//追逐状态
}


/// <summary>
/// 状态转化条件
/// </summary>
public enum FSMTransition
{
    SeePlayer,//看到主角(目标)
    LeavePlayer,//远离敌人(目标)
}

public class FSMSystem
{
    private FSMStateID mCurrentStateID;
    private FSMBaseState mCurrentState;

    private Dictionary<FSMStateID, FSMBaseState> mFSMStateDic = new Dictionary<FSMStateID, FSMBaseState>();


    public void AddFSMSate(FSMBaseState state)
    {
        if (state == null)
        {
            Debug.Log("角色状态为空,无法添加");
            return;
        }
        if (mCurrentState == null)
        {
            //第一个添加的状态被作为系统首个运行的状态
            mCurrentStateID = state.mStateID;
            mCurrentState = state;
            mCurrentState.StateStart();
        }
        if (mFSMStateDic.ContainsValue(state))
        {
            Debug.Log("容器内存在该状态");
            return;
        }
        mFSMStateDic.Add(state.mStateID, state);
    }

    public void DeleteFSMSate(FSMBaseState state)
    {
        if (state == null)
        {
            Debug.Log("角色状态为空,无法添加");
            return;
        }
        if (!mFSMStateDic.ContainsValue(state))
        {
            Debug.Log("容器内不存在该状态");
            return;
        }
        mFSMStateDic.Remove(state.mStateID);
    }

    //更新(执行)系统
    public void UpdateSystem()
    {
        if (mCurrentState != null)
        {
            mCurrentState.StateUpdate();
            mCurrentState.TransitionReason();
        }
    }

    //转换状态
    public void TransitionFSMState(FSMTransition transition)
    {
        FSMStateID stateID = mCurrentState.GetStateIdByTransition(transition);
        if (stateID != FSMStateID.NullFSMStateID)
        {
            mCurrentStateID = stateID;
            mCurrentState.StateEnd();
            //换状态
            mCurrentState = mFSMStateDic.FirstOrDefault(q => q.Key == stateID).Value;
            mCurrentState.StateStart();
        }
    }
}



各个状态(巡逻状态、追逐状态)抽象理解为一个对象

创建一个状态基类,各个状态子类中可以继承重写这个基类方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public abstract class FSMBaseState
{
    public FSMStateID mStateID { get; set; }    //状态ID
    public FSMSystem mFSMSystem { get; set; }   //该对象属于在哪个状态机

    public Dictionary<FSMTransition, FSMStateID> mFSMStateIdDic = new Dictionary<FSMTransition, FSMStateID>();

    public FSMBaseState(FSMSystem fsmSystem, FSMStateID stateID)
    {
        this.mFSMSystem = fsmSystem;
        this.mStateID = stateID;
    }

    public void AddTransition(FSMTransition transition, FSMStateID stateID)
    {
        if (mFSMStateIdDic.ContainsKey(transition))
        {
            Debug.Log("本状态已经包含了该转换条件");
            return;
        }
        mFSMStateIdDic.Add(transition, stateID);
    }

    public void DeleteTransition(FSMTransition transition)
    {
        if (!mFSMStateIdDic.ContainsKey(transition))
        {
            Debug.Log("容器中没有该转换条件");
            return;
        }
        mFSMStateIdDic.Remove(transition);
    }

    public FSMStateID GetStateIdByTransition(FSMTransition transition)
    {
        if (!mFSMStateIdDic.ContainsKey(transition))
        {
            Debug.Log("容器内没有该转换条件,无法获取状态");
            return FSMStateID.NullFSMStateID;
        }

        return mFSMStateIdDic.FirstOrDefault(q => q.Key == transition).Value;
    }

    public abstract void StateStart();
    public abstract void StateUpdate();
    public abstract void StateEnd();
    //转化状态条件
    public abstract void TransitionReason();
}


以下是巡逻状态

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

public class FSMPatrolState : FSMBaseState
{
    //路径点
    private List<Transform> mStargetPointTransform = new List<Transform>();
    //路径点索引
    private int mPointIndex = 0;
    //士兵
    private GameObject mSliderObj { get; set; }
    //主角
    private GameObject mPlayerObj { get; set; }
    //士兵移动速度
    private float mMoveSpeed = 4f;

    public FSMPatrolState(FSMSystem fsmSystem) : base(fsmSystem, FSMStateID.PatrolFSMStateID) { }

    public override void StateStart()
    {
        //获取路径点
        Transform[] transforms = GameObject.Find("Points").GetComponentsInChildren<Transform>();
        foreach (var m_transform in transforms)
        {
            if (m_transform != GameObject.Find("Points").transform)
            {
                mStargetPointTransform.Add(m_transform);
                Debug.Log(m_transform.position);
            }
        }

        //获取士兵对象
        mSliderObj = GameObject.Find("Slider");
        //获取主角对象
        mPlayerObj = GameObject.Find("Player");
    }

    public override void StateUpdate()
    {
        //确实目标点并移动  
        mSliderObj.transform.LookAt(this.mStargetPointTransform[this.mPointIndex].position);
        mSliderObj.transform.Translate(Vector3.forward * Time.deltaTime * mMoveSpeed);

        if (Vector3.Distance(mSliderObj.transform.position, this.mStargetPointTransform[this.mPointIndex].position) < 0.5f)
        {
            //切换目标点
            this.mPointIndex++;
            if (this.mPointIndex >= this.mStargetPointTransform.Count)
            {
                this.mPointIndex = 0;
            }    
        }
    }

    public override void StateEnd()
    {
        
    }

    public override void TransitionReason()
    {
        if (Vector3.Distance(mSliderObj.transform.position, mPlayerObj.transform.position) <= 2.0f)
        {
            //转化状态
            if (this.mFSMSystem == null)
            {
                Debug.Log("目标状态机为空");
                return;
            }
            mFSMSystem.TransitionFSMState(FSMTransition.SeePlayer);
        }
    }
}


以下是追逐状态

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

public class FSMChaseState : FSMBaseState
{
    private GameObject mPlayerObj { get; set; }
    private GameObject mSliderObj { get; set; }
    private float mSliderMoveSpeed = 6.0f;

    public FSMChaseState(FSMSystem fsmSystem) : base(fsmSystem, FSMStateID.ChaseFSMStateID) { }

    public override void StateStart()
    {
        mPlayerObj = GameObject.Find("Player");
        mSliderObj = GameObject.Find("Slider");
    }

    public override void StateUpdate()
    {
        if (Vector3.Distance(mPlayerObj.transform.position, mSliderObj.transform.position) <= 10.0f)
        {
            //开始面向主角
            mSliderObj.transform.LookAt(mPlayerObj.transform.position);
            //开始追逐
            mSliderObj.transform.Translate(Vector3.forward * Time.deltaTime * mSliderMoveSpeed);
        }
    }

    public override void StateEnd()
    {
        
    }

    public override void TransitionReason()
    {
        //当主角远离敌人
        if (Vector3.Distance(mPlayerObj.transform.position, mSliderObj.transform.position) > 10.0f)
        {
            //转化状态
            if (this.mFSMSystem == null)
            {
                Debug.Log("目标状态机为空");
                return;
            }
            mFSMSystem.TransitionFSMState(FSMTransition.LeavePlayer);
        }
    }
}

该状态机的优点在于当有不同类型的状态时候,可以直接添加到状态系统内,而不要需要状态系统内部的运行逻辑

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

public class Slider : MonoBehaviour
{
    private FSMSystem fsmSystem { get; set; }

	void Start ()
    {
        fsmSystem = new FSMSystem();

        //巡逻状态,在构造参数传一个系统参数,确定该状态是在哪个状态系统中管理的,状态转换的时候调用
        FSMBaseState patrolState = new FSMPatrolState(fsmSystem);
        patrolState.AddTransition(FSMTransition.SeePlayer, FSMStateID.ChaseFSMStateID);//巡逻状态转化条件

        //追逐状态
        FSMBaseState chaseState = new FSMChaseState(fsmSystem);
        chaseState.AddTransition(FSMTransition.LeavePlayer, FSMStateID.PatrolFSMStateID);
        
        fsmSystem.AddFSMSate(patrolState);
        fsmSystem.AddFSMSate(chaseState);
	}
	
	void Update ()
    {
        fsmSystem.UpdateSystem();	
	}
}

原文地址:blog.liujunliang.com.cn



2018-04-25 17:50:55 nemo_free 阅读数 369

脚本自动化生成状态机

给角色调试动作,经常会用到状态机,那么一般来说如果就几个角色,那么用Unity的可视化状态机编辑就挺方便了,但是如果有几十个甚至上百个角色的模型,要生成状态机,一个一个调试,不知道要花费多少时间精力,幸好Unity提供了Editor的状态机API,供我们来处理批量的状态机。

首先得有一个模版的AnimatorController

用于处理大部分相同的状态,包括状态机以及状态之间的切换等等

然后拷贝一份到你的角色目录

if (!File.Exists(animatorControllerPath + fileName))
{
   FileUtil.CopyFileOrDirectory(templetControlPath + fileName, animatorControllerPath + "/" + generateName);
   Debug.Log("copy animator control success");
}
//注意刷新,否则导致animatorController为null
   AssetDatabase.Refresh();

注意刷新下,不然生成的为空

获取角色目录下面所有的动画clip

string[] assets = AssetDatabase.FindAssets("t:GameObject", animPath.Split());
AnimationClip[] animClip = new AnimationClip[assets.Length];
//获得目录下所有的AnimationClip对象
 for (int i = 0; i < assets.Length; ++i)
{
   string path = AssetDatabase.GUIDToAssetPath(assets[i]);
   animClip[i] = AssetDatabase.LoadAssetAtPath(path, typeof(AnimationClip)) as AnimationClip;
}
//如果一个fbx中有多个AnimationClip对象咋办呢?
var aniArray = AssetDatabase.LoadAllAssetsAtPath(path);
var filterArray = aniArray.Where(t => t.GetType() == typeof(AnimationClip) && !t.name.Contains("Take")).ToArray();
for (int j = 0; j < filterArray.Length; j++)
{
   animClip[j] = filterArray[j] as AnimationClip;
}

多个AnimationClip,我的做法是获取所有的,然后过滤出来就OK了,可能会有更好的方法,如果有请留言哦!!

拿到AnimatorController里面的状态,然后按照你的规则,往状态里面添加Motion,也就是AnimationClip,就可以啦

 for (int i = 0; i < sm.states.Length; ++i)
 {
         for (int j = 0; j < animClip.Length; ++j)
         {
           if (animClip[j].name.ToLower().Contains(sm.states[i].state.name.ToLower()))
           {
              sm.states[i].state.motion = animClip[j];
           }
           if (matchMap.ContainsKey(sm.states[i].state.name))
           {
              if (animClip[j].name.Contains(matchMap[sm.states[i].state.name]))
               sm.states[i].state.motion = animClip[j];
           }
         }
}

可能有的状态是BleedTree

BlendTree runIdleBlendTree = new BlendTree();
runIdleBlendTree.name = "BlendTree";
runIdleBlendTree.AddChild(idle);
runIdleBlendTree.AddChild(run);
runIdleBlendTree.blendParameter = "Speed";
sm.states[i].state.motion = runIdleBlendTree;

需要生成状态,然后再添加AnimationClip

 for (int i = 0; i < actionClipList.Count; i++)
{
     var action = sm.AddState(actionClipList[i].name, new Vector3(600, 300 + 60 * i));
     var tran = dashState.AddTransition(action);
     tran.exitTime = 1;
     tran.duration = 0;
     tran.offset = 0;
     tran.AddCondition(AnimatorConditionMode.Equals, 5 + i, "CMD");
     action.AddTransition(jumpbackState);
}

补充一下 如果修改Project目录下面的资源可以直接调用

  AssetDatabase.RenameAsset(path, newName);

Unity 有限状态机

阅读数 198

没有更多推荐了,返回首页