【Unity游戏开发日志】有限状态机FSM优化与应用

        之前我写过一篇有关与有限状态机的文章,我觉得还可以进行优化,同时把框架更加完善具体。也是为GameJam做准备吧。

        有限状态机的相关概念可以查看我的上一篇文章:http://t.csdn.cn/Xm3zficon-default.png?t=N4P3http://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;
    }
}

演示:

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shotar0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值