之前我写过一篇有关与有限状态机的文章,我觉得还可以进行优化,同时把框架更加完善具体。也是为GameJam做准备吧。
有限状态机的相关概念可以查看我的上一篇文章:http://t.csdn.cn/Xm3zfhttp://t.csdn.cn/Xm3zf话不多说,上优化后的代码。这次的优化主要是把FSM脱离出游戏物体,单独成为一个类,写起来也更舒服,更符合有限状态机的性质。演示中使用了“持刀”,“持枪”两种状态,具体执行以控制台输出语句来模拟,这里主要讲的是FSM的机制。
状态接口IState:
public interface IState
{
public void OnEnter();
public void OnUpdate();
public void OnExit();
}
状态管理类FSM:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//==================有限状态机框架==================//
public class FSM
{
public Dictionary<string, IState> states;//字典存储各种状态,键值为字符串
public IState currentState;//当前状态记录
//构造函数
public FSM()
{
this.states = new Dictionary<string, IState>();
}
//转换状态
public void SwitchState(string type)
{
if(!states.ContainsKey(type))
{
Debug.Log("不存在:" + type);
return;
}
if(currentState != null)
{
currentState.OnExit();
}
currentState = states[type];
currentState.OnEnter();
}
//添加状态
public void AddState(string type,IState state)
{
if(states.ContainsKey(type))
{
Debug.Log("已存在:"+type);
return;
}
states.Add(type, state);
}
public void DeleteState(string type)
{
}
//执行状态
public void OnUpdate()
{
currentState.OnUpdate();
}
}
两种状态:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IGunMode :IState
{
public TopDownCharacterController manager;
public IGunMode(TopDownCharacterController manager)
{
this.manager = manager;
}
public void OnEnter()
{
Debug.Log("进入持枪模式");
}
public void OnUpdate()
{
if(Input.GetMouseButtonDown(0))
{
Debug.Log("开火!");
}
if(Input.GetKeyUp(KeyCode.F))
{
manager.fsm.SwitchState("KnifeMode");
}
}
public void OnExit()
{
Debug.Log("退出持枪模式");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IKnifeMode : IState
{
public TopDownCharacterController manager;
public IKnifeMode(TopDownCharacterController manager)
{
this.manager = manager;
}
public void OnEnter()
{
Debug.Log("进入持刀模式");
}
public void OnUpdate()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("轻刀!");
}
if(Input.GetMouseButtonUp(1))
{
Debug.Log("重刀!");
}
if(Input.GetKeyDown(KeyCode.J))
{
manager.fsm.SwitchState("GunMode");
}
}
public void OnExit()
{
Debug.Log("退出持刀模式");
}
}
这里我写了一个玩家控制脚本,调用FSM来进行状态切换,这里注意一下,我在设计玩家结构的时候采用的是数据和操作分离,也就是我的数据存储是不写在控制脚本上的,在这里不做讨论,下一篇文章会讲数据的设计。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TopDownCharacterController : MonoBehaviour
{
//public float speed;
private Animator animator;
public FSM fsm;
private CharacterDataStats characterDataStats;
private void Awake()
{
characterDataStats=GetComponent<CharacterDataStats>();
}
private void Start()
{
animator = GetComponent<Animator>();
fsm = new FSM();
fsm.AddState("GunMode", new IGunMode(this));
fsm.AddState("KnifeMode", new IKnifeMode(this));
fsm.SwitchState("KnifeMode");
characterDataStats.CurrentMoveSpeed = 3;
characterDataStats.CurrentHealth = characterDataStats.MaxHealth;
}
private void Update()
{
Moving();
fsm.OnUpdate();
}
public void Moving()
{
Vector2 dir = Vector2.zero;
if (Input.GetKey(KeyCode.A))
{
dir.x = -1;
animator.SetInteger("Direction", 3);
}
else if (Input.GetKey(KeyCode.D))
{
dir.x = 1;
animator.SetInteger("Direction", 2);
}
if (Input.GetKey(KeyCode.W))
{
dir.y = 1;
animator.SetInteger("Direction", 1);
}
else if (Input.GetKey(KeyCode.S))
{
dir.y = -1;
animator.SetInteger("Direction", 0);
}
dir.Normalize();
animator.SetBool("IsMoving", dir.magnitude > 0);
GetComponent<Rigidbody2D>().velocity = characterDataStats.CurrentMoveSpeed * dir;
}
}
演示: