Unity 状态机实例 学习之用

开始的时候想用lua写的 比较简便 代码也有现成的 
但是考虑到还是以学习为主 就把思想转到cs实现一遍 
这篇文章可以认为是对前几天的解决Unity多线程问题中的状态机的一个简单实现


先来看看将要实现的状态机(本人不想画图 喜欢用文字描述 后期修改再说吧。。。)

共 5 种状态

wait

move

attack_a

attack_b

power


将要实现的过程是:

在wait状态下:

可以转到 move 移动(这里有 3 秒的判断时间) 可以转到 attack_a 攻击 或者 attack_b 攻击

move:

可回到wait 可转到attack_a攻击

attack_a状态:

可转到wait

attack_b状态:

可转到power 可转到wait (这里有 3 秒的判断时间)

power状态:

可转到wait


通过以上的信息 应该能够自行画出状态图 至于其中每个状态的动作 在下面的代码中就可以看到


主要代码:
FSMBase.cs
using UnityEngine;
using System.Collections;




public abstract class FSMBase
{


     public abstract void HandleActor(Actor actor);

}
FSMMain.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//状态
public enum State
{
    WAIT,
    MOVE,
    ATTACK_A,
    ATTACK_B,
    POWER,
}
//动作
public enum Actor
{
    KEY_W,
    MOUSE_LEFT,
    MOUSE_RIGHT,
    WAIT_TIME,
    IMMEDIATELY,
    KEY_SPACE
}




public class FSMMain : MonoBehaviour
{


    public static FSMMain _instance;


    public State currState;
    List<Actor> actorList = new List<Actor>();                              //储存执行的动作
    Dictionary<State, FSMBase> stateDic = new Dictionary<State, FSMBase>(); //储存状态和状态类的映射
    public void StartActor(Actor actor)
    {
        actorList.Add(actor);
    }
    public void ChangeState(State state)
    {
        this.currState = state;
        Debug.Log("currState changed : " + this.currState);




    }
    private void RegistState()
    {
        stateDic.Add(State.WAIT, new FSMWait());
        stateDic.Add(State.MOVE, new FSMMove());
        stateDic.Add(State.ATTACK_A, new FSMAttackA());
        stateDic.Add(State.ATTACK_B, new FSMAttackB());
        stateDic.Add(State.POWER, new FSMPower());

    }


    // Use this for initialization
    void Start()
    {
        
        RegistState();
        _instance = this;
    }

    // Update is called once per frame
    void Update()
    {
       
        if (actorList.Count > 0)
        {
            Actor actor = actorList[0];
            FSMBase fsmBase;
            if (stateDic.TryGetValue(currState, out fsmBase))
            {
                fsmBase.HandleActor(actor);
                actorList.RemoveAt(0);
                
            }
        }

    }
}


TestMain.cs
using UnityEngine;
using System.Collections;


public class TestMain : MonoBehaviour
{
    // Use this for initialization
    void Start()
    {

    }


    // Update is called once per frame
    void Update()
    {

        if (Input.GetKeyDown(KeyCode.W))
        {
            FSMMain._instance.StartActor(Actor.KEY_W);
            Invoke("MoveWait", 3);

        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            FSMMain._instance.StartActor(Actor.KEY_SPACE);
          
        }
        if (Input.GetMouseButtonDown(0))
        {
            FSMMain._instance.StartActor(Actor.MOUSE_LEFT);
        }
        if (Input.GetMouseButtonDown(1))
        {
            FSMMain._instance.StartActor(Actor.MOUSE_RIGHT);
            Invoke("PowerWait",3);

        }

    }
    //这里的代码可以 FSMPower 中实现 个人感觉可以不用判断状态 因为判断状态对状态机有了一定的偏离 所以可以用控制时间的方法比较好
    //虽然代码多了一点 不过对这个功能而言 可以设置个判断是否处于 “大招等待” 的变量isPower
    //如果 3s 中没有任何动作 直接回到 wait 状态 一旦在 attack_b 中有了其他动作 那么变量


设为 false 在 update 就不会执行计算时间 那么就会到其他动作的状态 而不会进入 wait 状态
    void PowerWait()
    {
        if (FSMMain._instance.currState == State.ATTACK_B)
        {
            FSMMain._instance.StartActor(Actor.IMMEDIATELY);
        }
        else
        {
            print("currState is not attack_b So do nothing");
        }
    }



    void MoveWait()
    {
        if (FSMMain._instance.currState == State.MOVE)
        {
            FSMMain._instance.StartActor(Actor.IMMEDIATELY);
        }
        else
        {
            print("currState is not move So do nothing");
        }
    }
}


其他代码:(其他代码略 格式与下相同)
FSMWait.cs
using UnityEngine;
using System.Collections;


public class FSMWait : FSMBase {


    public override void HandleActor(Actor actor)
    {
        if (actor == Actor.KEY_W)
        {

            FSMMain._instance.ChangeState(State.MOVE);
          
        }
        if (actor == Actor.MOUSE_LEFT)
        {




            FSMMain._instance.ChangeState(State.ATTACK_A);
            //这里处理其他的的动作
            //然后可以在这里等待 1 s 来结束该动作 这里不做演示 直接进入wait状态




            //这里由于是完成后立即回到wait状态 所以用ChangeState也可以
            FSMMain._instance.StartActor(Actor.IMMEDIATELY);
           
          
        }
        if (actor == Actor.MOUSE_RIGHT)
        {
             
            FSMMain._instance.ChangeState(State.ATTACK_B);
            Debug.Log("currState changed : " + FSMMain._instance.currState);
         
        }
       
    }
}



开始的时候我认为很容易实现 但是真正做的时候还是不算太容易
在这里说明一下 这是Unity c#工程中的使用 在lua中的实现比这个要容易一些 
毕竟Unity中热更新的逻辑部分大部分都在 lua 脚本中 所以可以试一试用lua脚本实现状态机
其他的cs参考代码我还不知道怎么传呢。。。。。。毕竟刚用没几天。。。(FSMMain.cs 和 TestMain.cs 都随便挂在物体上即可)


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值