unity3d人工智能学习(7)——游戏状态机FSM

这篇博客介绍了如何在Unity3D中实现游戏状态机(FSM)的概念,通过详细的操作过程,展示了敌人(Enemy)如何根据预设的巡逻点(PatrolPoints)巡逻,并在检测到玩家(Player)接近时切换到追逐状态。文章涵盖了从创建场景、设置角色标签到编写FSM相关脚本的全过程。
摘要由CSDN通过智能技术生成

游戏状态机FSM

简述

参考教程:
Unity3D有限状态机(FSM)学习笔记【7】使用实例.
FSM有限状态机学习及Unity3D案例讲解.

在这里插入图片描述
游戏最终结果:
开始游戏的时候,Enemy会沿着设定好的PatrolPoints路径进行巡逻,如果Player与Enemy之间小于4时,Enemy就会追逐Player,当Player距离大于8的时候,Enemy会回去继续进行巡逻。

操作过程

  1. 建立一个简单场景,包括敌人(Enemy),玩家(Player),巡逻点多个(PatrolPoints)。
    在这里插入图片描述

  2. Enemy添加Enemy标签TAG,Player添加Player标签TAG。两个都要添加Character Controller组件。
    在这里插入图片描述
    在这里插入图片描述

  3. Enemy添加Enemy脚本,Player添加Player脚本(用这2个脚本之前还有多个基本脚本)
    在这里插入图片描述
    定义枚举类型

public enum StateID
{
    NullStateID,
    PatrolState,
    ChaseState
}
public enum Transition
{
    NullTransition,
    FindPlayer,
    LosePlayer
}

FSMState基类

public abstract class FSMState
    {
        protected StateID stateID;
        public StateID ID { get { return this.stateID; } }
        protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
        protected FSMSystem fsm;

        public FSMState(FSMSystem fsm)
        {
            this.fsm = fsm;
        }

        /// <summary>
        /// 添加转换条件
        /// </summary>
        /// <param name="trans">转换条件</param>
        /// <param name="stateID">转换的目标状态</param>
        public void AddTransition(Transition trans, StateID stateID)
        {
            if (trans == Transition.NullTransition)
            {
                Debug.LogError(trans + "为空,转换条件不允许为空"); return;
            }
            if (stateID == StateID.NullStateID)
            {
                Debug.LogError(stateID + "为空,状态ID不允许为空"); return;
            }
            if (map.ContainsKey(trans))
            {
                Debug.LogError(trans + "已经存在,请查看该转换条件是否正确");
            }
            else
            {
                map.Add(trans, stateID);
            }
        }

        /// <summary>
        /// 删除转换条件
        /// </summary>
        /// <param name="trans">需删除的转换条件</param>
        public void DeleteTransition(Transition trans)
        {
            if (trans == Transition.NullTransition)
            {
                Debug.LogError(trans + "为空,转换条件不允许为空"); return;
            }
            if (map.ContainsKey(trans) == false)
            {
                Debug.LogError(trans + "不存在,请查看该转换条件是否正确");
            }
            else
            {
                map.Remove(trans);
            }
        }

        /// <summary>
        /// 通过转换条件,得到目标状态
        /// </summary>
        /// <param name="trans">转换条件</param>
        /// <returns>返回目标状态</returns>
        public StateID GetTargetStateID(Transition trans)
        {
            if (map.ContainsKey(trans) == false)
            {
                Debug.LogError(trans + "不存在,请查看该转换条件是否正确");
                return StateID.NullStateID;
            }
            else
            {
                return map[trans];
            }
        }

        public virtual void DoBeforeEntering() { }//进入动作
        public virtual void DoAfterLeaving() { }//离开动作
        public abstract void Act();//输入动作
        public abstract void Reason();//转移动作
    }

FSMSystem管理类

public class FSMSystem
{
    private Dictionary<StateID, FSMState> states = new Dictionary<StateID, FSMState>();
    private FSMState currentState;

    /// <summary>
    /// 更新当前状态行为
    /// </summary>
    public void UpdateFSM()
    {
        currentState.Act();
        currentState.Reason();
    }

    /// <summary>
    /// 添加状态
    /// </summary>
    /// <param name="state">需管理的状态</param>
    public void AddState(FSMState state)
    {
        if (state == null)
        {
            Debug.LogError(state + "为空"); return;
        }
        if (currentState == null)
        {
            currentState = state;
        }
        if (states.ContainsValue(state))
        {
            Debug.LogError(state + "已经存在");
        }
        else
        {
            states.Add(state.ID, state);
        }
    }

    /// <summary>
    /// 删除状态
    /// </summary>
    /// <param name="id">需要删除状态的ID</param>
    /// /// <returns>删除成功返回true,否则返回false</returns>
    public bool DeleteState(StateID id)
    {
        if (id == StateID.NullStateID)
        {
            Debug.LogError(id + "为空");
            return false;
        }
        if (states.ContainsKey(id) == false)
        {
            Debug.LogError(id + "不存在");
            return false;
        }
        else
        {
            states.Remove(id);
            return true;
        }
    }

    /// <summary>
    /// 执行转换
    /// </summary>
    /// <param name="trans">转换条件</param>
    public void PerformTransition(Transition trans)
    {
        if (trans == Transition.NullTransition)
        {
            Debug.LogError(trans + "为空"); return;
        }
        StateID targetID = currentState.GetTargetStateID(trans);
        if (states.ContainsKey(targetID) == false)
        {
            Debug.LogError(targetID + "不存在"); return;
        }
        FSMState targetState = states[targetID];
        currentState.DoAfterLeaving();
        targetState.DoBeforeEntering();
        currentState = targetState;
    }
}

玩家代码

public class PlayerFSM : MonoBehaviour
{
    private CharacterController characherCon;
    public float speed = 5;
    public float smooth = 5;

    private void Start()
    {
        characherCon = gameObject.GetComponent<CharacterController>();
    }

    private void Update()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        transform.Rotate(Vector3.up, h * smooth);
        characherCon.SimpleMove(transform.forward * v * speed);
        if (Input.GetKeyDown(KeyCode.Space))
        {
            speed = 10;
        }
        if (Input.GetKeyUp(KeyCode.Space))
        {
            speed = 5;
        }
    }
}

敌人代码

public class EnemyFSM : MonoBehaviour
{
    private FSMSystem fsm;

    private void Start()
    {
        fsm = new FSMSystem();
        FSMState patrolState = new PatrolState(fsm);
        FSMState chaseState = new ChaseState(fsm);
        fsm.AddState(patrolState);
        fsm.AddState(chaseState);
        patrolState.AddTransition(Transition.FindPlayer, StateID.ChaseState);
        chaseState.AddTransition(Transition.LosePlayer, StateID.PatrolState);
    }

    private void Update()
    {
        fsm.UpdateFSM();
    }
}

巡逻状态:

public class PatrolState : FSMState
{
    private Transform enemy;
    private Transform player;
    private List<Transform> pathsList = new List<Transform>();
    private int index = 0;

    public float smooth = 3;

    public PatrolState(FSMSystem fsm) : base(fsm)
    {
        stateID = StateID.PatrolState;
        enemy = GameObject.FindWithTag("Enemy").transform;
        player = GameObject.FindWithTag("Player").transform;
        Transform pathRoot = GameObject.Find("PatrolPoints").transform;
        foreach (Transform pathPoint in pathRoot)
        {
            pathsList.Add(pathPoint);
        }
    }

    public override void DoBeforeEntering()
    {
        Debug.Log("敌人开始巡逻了!");
    }

    public override void DoAfterLeaving()
    {
        Debug.Log("敌人发现玩家,结束巡逻!");
    }

    public override void Act()
    {
        Vector3 forward = pathsList[index].position - enemy.position;
        forward = new Vector3(forward.x, 0, forward.z);
        Quaternion targetQuaternion = Quaternion.LookRotation(forward, Vector3.up);
        enemy.rotation = Quaternion.Slerp(enemy.rotation, targetQuaternion, Time.deltaTime * smooth);
        enemy.Translate(Vector3.forward * Time.deltaTime * smooth);
        if (Vector3.Distance(enemy.position, pathsList[index].position) < 2f)
        {
            index++;
            index %= pathsList.Count;
        }
    }

    public override void Reason()
    {
        if (Vector3.Distance(enemy.position, player.position) < 4)
        {
            fsm.PerformTransition(Transition.FindPlayer);
        }
    }
}

追逐状态:

public class ChaseState : FSMState
{
    private Transform enemy;
    private Transform player;
    public float smooth = 3;
    public float chaseSpeed = 5;

    public ChaseState(FSMSystem fsm):base(fsm)
    {
        stateID = StateID.ChaseState;
        enemy = GameObject.FindWithTag("Enemy").transform;
        player = GameObject.FindWithTag("Player").transform;
    }

    public override void DoBeforeEntering()
    {
        Debug.Log("敌人开始追逐玩家了!");
    }

    public override void DoAfterLeaving()
    {
        Debug.Log("敌人跟丢玩家,继续巡逻了!");
    }

    public override void Act()
    {
        Vector3 forward = player.position - enemy.position;
        forward = new Vector3(forward.x, 0, forward.z);
        Quaternion targetQuaternion = Quaternion.LookRotation(forward, Vector3.up);
        enemy.rotation = Quaternion.Slerp(enemy.rotation, targetQuaternion, Time.deltaTime * smooth);
        enemy.Translate(Vector3.forward * Time.deltaTime * chaseSpeed);  
    }

    public override void Reason()
    {
        if (Vector3.Distance(enemy.position, player.position) > 8)
        {
            fsm.PerformTransition(Transition.LosePlayer);
        }
    }
}

结果(红色敌人,蓝色玩家,绿色巡逻点)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值