开始的时候想用lua写的 比较简便 代码也有现成的
但是考虑到还是以学习为主 就把思想转到cs实现一遍
这篇文章可以认为是对前几天的解决Unity多线程问题中的状态机的一个简单实现
先来看看将要实现的状态机(本人不想画图 喜欢用文字描述 后期修改再说吧。。。)
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 都随便挂在物体上即可)
但是考虑到还是以学习为主 就把思想转到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 都随便挂在物体上即可)