在Unity中实现状态机设计模式

首先定义一个状态基类,里面有三个抽象方法,分别表示进入状态,退出状态,更新状态时需要执行的方法。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class PlayerState
{
    public abstract void Enter();
    public abstract void Exit();
    public abstract void Update();
}

新建状态管理类,负责所有状态的切换和添加状态,当切换状态的时候判断有没有正在执行的状态,如果有的话就先退出正在执行的状态并切换成目标状态,这样就算后面有成百上千个状态都不会出现状态混乱的情况,玩家同一时刻只会执行一种状态。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 状态管理器
public class PlayerStateManager : MonoBehaviour
{
    private Dictionary< System.Type, PlayerState> states;
    public  PlayerState currentState;
    public  PlayerStateManager Instance { get; private set; }

    public Player  currentPlayer;//获取角色引用

    void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
            InitializeStates();
        }
        else
        {
            Destroy(gameObject);
        }
    }
    //添加状态
    void InitializeStates()
    {
        states = new Dictionary<System.Type, PlayerState>
        {
            { typeof(IdleState), new IdleState(currentPlayer) },
            { typeof(PatrolState), new PatrolState(currentPlayer) },
            { typeof(FromworkState), new FromworkState(currentPlayer) }
         };
    }
    //切换状态
    public void ChangeState<T>() where T : PlayerState
    {
        var newStateType = typeof(T);

        if (states.ContainsKey(newStateType))
        {
            currentState?.Exit();
            currentState = states[newStateType];
            currentState.Enter();
        }
    }
    public T GetState<T>() where T : PlayerState
    {
        var stateType = typeof(T);
        if (states.ContainsKey(stateType))
            return (T)states[stateType];
        return null;
    }
}

定义玩家类,玩家身上定义了所有玩家的状态,待机,巡逻,下班。但是玩家不负责直接切换他们,而是通知状态机来切换状态。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
    public GameObject Point1;
    public GameObject Point2;

    Vector3 _targetPos;
    bool _hasTarget = false;

    [SerializeField]private PlayerStateManager _playerStateManager;
    void Update()
    {
        //如果状态不为空刷新状态
        if (_playerStateManager.currentState != null)
        {
            _playerStateManager.currentState.Update();
        }
        //控制状态切换
        if (Input.GetKeyDown(KeyCode.A))
        {
            _playerStateManager.ChangeState<IdleState>();
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            _playerStateManager.ChangeState<PatrolState>();
        }
        if (Input.GetKeyDown(KeyCode.D))
        {
            _playerStateManager.ChangeState<FromworkState>();
        }
    }
    //走到点位1
    public IEnumerator MoveToPoint1(float speed)
    {
        // 避免空引用
        if (Point1 == null)
            yield break;

        Vector3 targetPos = Point1.transform.position;

        while (Vector3.Distance(transform.position, targetPos) > 0.01f)
        {
            transform.position = Vector3.MoveTowards(
                transform.position,
                targetPos,
                speed * Time.deltaTime
            );
            yield return null; // 等下一帧
        }

        // 确保精确落点
        transform.position = targetPos;
    }
    //待机
    public void Idle()
    {
        // 绕Point1转圈
        transform.RotateAround(Point1.transform.position, Vector3.forward, 30 * Time.deltaTime);
    }
    //巡逻
    public void Patrol()
    {
        //随机点目标缓存
        if (!_hasTarget)
        {
            _targetPos = new Vector3(
                Random.Range(-5f, 5f),
                Random.Range(-5f, 5f),
                transform.position.z
            );
            _hasTarget = true;
        }

        transform.position = Vector3.MoveTowards(
            transform.position,
           _targetPos,
           3f * Time.deltaTime
       );

        //走到了就刷新目标
        if (Vector3.Distance(transform.position, _targetPos) < 0.2f)
            _hasTarget = false;
    }
    //下班
    public void Fromwork()
    {
        transform.position = Vector3.MoveTowards(
            transform.position,
            Point2.transform.position,
           3f * Time.deltaTime
       );
    }
}

定义具体的状态,待机,巡逻,下班。(待机的时候就在点1转圈,巡逻的时候就在场景中随机移动,下班的时候走到点2)(进入每个状态前都要先回到点1)。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class IdleState : PlayerState
{
    Player player;
    public IdleState(Player targetplayer)
    {
        player = targetplayer;
    }
    private Coroutine moveRoutine;
    public override void Enter()
    {
        Debug.Log("进入待机状态");
        moveRoutine = player.StartCoroutine(IdleProcess());
    }
    public override void Exit()
    {
        Debug.Log("退出待机状态");
        if (moveRoutine!= null)
        {
            player.StopCoroutine(moveRoutine);
        }
    }
    public override void Update()
    {
         
    }
    private IEnumerator IdleProcess()
    {
        // 1. 先移动到 Point1
        yield return player.MoveToPoint1(3f);

        // 2. 到达后开始持续执行 Idle 动作
        while (true)
        {
            player.Idle();
            yield return null;
        }
    }
}
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PatrolState : PlayerState
{
    Player player;
    private Coroutine patrolRoutine;

    public PatrolState(Player targetplayer)
    {
        player = targetplayer;
    }

    public override void Enter()
    {
        Debug.Log("进入巡逻状态");
        patrolRoutine = player.StartCoroutine(PatrolProcess());
    }

    public override void Exit()
    {
        Debug.Log("退出巡逻状态");
        if (patrolRoutine != null)
        {
            player.StopCoroutine(patrolRoutine);
        }
    }

    public override void Update()
    {
         
    }

    private IEnumerator PatrolProcess()
    {
        // 1. 先移动到 Point1
        yield return player.MoveToPoint1(3f);

        // 2. 循环执行巡逻动作
        while (true)
        {
            player.Patrol();
            yield return null;
        }
    }
}
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FromworkState : PlayerState
{
    Player player;
    private Coroutine fromworkRoutine;

    public FromworkState(Player targetplayer)
    {
        player = targetplayer;
    }

    public override void Enter()
    {
        Debug.Log("进入下班状态");
        fromworkRoutine = player.StartCoroutine(FromworkProcess());
    }

    public override void Exit()
    {
        Debug.Log("退出下班状态");
        if (fromworkRoutine != null)
        {
            player.StopCoroutine(fromworkRoutine);
        }
    }

    public override void Update()
    {
         
    }

    private IEnumerator FromworkProcess()
    {
        // 1. 先移动到 Point1
        yield return player.MoveToPoint1(3f);

        // 2. 到达后执行下班动作循环
        while (true)
        {
            player.Fromwork();
            yield return null;
        }
    }
}

场景引用,创建一个方块当做玩家,然后创建两个圆红色表示上班的位置,绿色表示下班的位置。将玩家脚本和状态机控制脚本添加到玩家身上,将对应的脚本和物体进行拖拽到对应位置。

巡行项目然后按下A就会发现玩家移动到了红色的点并开始旋转。(按下S进入巡逻,按下D进入下班转态)。

相关推荐
玄同76511 分钟前
Python 数据类型:LLM 语料与 API 参数的底层处理逻辑
开发语言·人工智能·python·自然语言处理·llm·nlp·知识图谱
Slow菜鸟16 分钟前
Java基础 | 布隆过滤器
java·开发语言
比奇堡派星星23 分钟前
Linux Hotplug 机制详解
linux·开发语言·驱动开发
沉默金鱼1 小时前
Unity实用技能-UI进度条
ui·unity·游戏引擎
molaifeng1 小时前
像搭积木一样理解 Golang AST
开发语言·后端·golang
SystickInt1 小时前
C语言 UTC时间转化为北京时间
c语言·开发语言
黎雁·泠崖1 小时前
C 语言动态内存管理进阶:常见错误排查 + 经典笔试题深度解析
c语言·开发语言
成为大佬先秃头1 小时前
渐进式JavaScript框架:Vue 过渡 & 动画 & 可复用性 & 组合
开发语言·javascript·vue.js
嘻嘻嘻开心1 小时前
Java IO流
java·开发语言
JIngJaneIL2 小时前
基于java+ vue家庭理财管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot