zl程序教程

您现在的位置是:首页 >  其他

当前栏目

GameFrameWork框架(Unity3D)使用笔记(七)游戏主流程ProcedureMain——虽然游戏本来没有怪物但是为了使用状态机模块写一个简单怪物AI吧(下)

游戏模块笔记AI框架 一个 简单 没有
2023-09-11 14:20:51 时间

这篇直接接着上一篇开始写啦。


目录

四、开始撸状态机代码!

1、写状态类

IdleState      

AttackingState

ChasingState

ReturningState

 2、Enemy类

五、测试运行


四、开始撸状态机代码!

        使用GF的状态机的话,我们要做的主要是实现一下各个状态类。状态机的创建使用这个模块的Create函数即可,然后就可以用了。

        所以实际上是开始撸状态代码!

1、写状态类

        GF里的状态机需要继承FsmState这个类......嗯不多说了直接开始干就知道了!

        有个注意点这里先提一下,就是状态类里面使用ChangeState<>()方法并不是立即切换状态,下面的代码不执行。并且执行这个方法后就会执行该状态类的OnLeave()。所以要注意如果ChangeState<>()后面的内容不想执行的话要加上return语句。(否则的话比如某个引用在OnLeave()的时候释放了,那在ChangeState<>()后面执行就会报错。)

        注:因为状态机中要调用Enemy实体类的内容,所以写状态的时候实体类里面也做了点工作,写完状态类后会贴出来代码。

  • IdleState      

     创建一个IdleState.cs:

            

        类继承GameFramework.Fsm.FsmState<T> ,生成一下重写:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;
using GameFramework.Fsm;
namespace ShadowU
{
    public class IdleState : FsmState<Enemy>
    {
        protected override void OnDestroy(IFsm<Enemy> fsm)
        {
            base.OnDestroy(fsm);
        }

        protected override void OnEnter(IFsm<Enemy> fsm)
        {
            base.OnEnter(fsm);
        }

        protected override void OnInit(IFsm<Enemy> fsm)
        {
            base.OnInit(fsm);
        }

        protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
        {
            base.OnLeave(fsm, isShutdown);
        }

        protected override void OnUpdate(IFsm<Enemy> fsm, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);
        }
    }

}

 然后就是很简单的根据上一篇的设计填代码就行了:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;
using GameFramework.Fsm;
namespace ShadowU
{
    public class IdleState : FsmState<Enemy>
    {
        private Enemy owner;       //状态机所控制的Enemy实体

        private float moveTimer;   //移动计时器,归零的时候移动一次
        private Vector3 targetPos; //Idle移动的时候的随目标点
        private float speed; //速度
        protected override void OnDestroy(IFsm<Enemy> fsm)
        {
            base.OnDestroy(fsm);
        }

        protected override void OnEnter(IFsm<Enemy> fsm)
        {
            base.OnEnter(fsm);
            owner = fsm.Owner;
            speed = owner.enemyData.Speed;

            SetMoveTimer();
            SetRandomTargetPos();
        }

        protected override void OnInit(IFsm<Enemy> fsm)
        {
            base.OnInit(fsm);
        }

        protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
        {
            base.OnLeave(fsm, isShutdown);
            owner = null;
        }

        protected override void OnUpdate(IFsm<Enemy> fsm, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);

            if (moveTimer > 0)
            {
                if (Vector3.Distance(targetPos, owner.transform.position) > 0.1)
                {
                    //如果还没有到达目标位置
                    //向目标位置移动
                    Vector3 direction = Vector3.Normalize(targetPos - owner.transform.position);
                    owner.transform.Translate(direction * speed * elapseSeconds);
                }
                else
                {
                    //已经到了目标位置
                    //Enemy就停在原地啥也不做
                }
                
            }
            else
            {
                //重新选择一个目标点移动
                SetMoveTimer();
                SetRandomTargetPos();
            }
            moveTimer -= elapseSeconds;

            //如果玩家进入了发现范围,则进入追逐状态
            if (owner.IsPlayerFound())
            {
                ChangeState<ChasingState>(fsm);//状态机的状态切换
            }
        }
        /// <summary>
        /// 随机设置一个0.5-3秒的Timer 
        /// </summary>
        private void SetMoveTimer()
        {
            moveTimer = Random.Range(0.5f,3.0f);
        }
        /// <summary>
        /// 随机设置一个IdleRange内的目标点
        /// </summary>
        private void SetRandomTargetPos()
        {
            float idleRange = -owner.enemyData.IdleRange;
            targetPos = new Vector3(Random.Range(-idleRange,idleRange),owner.transform.position.y,owner.transform.position.z);
        }
    }

}

  • AttackingState

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
using UnityGameFramework.Runtime;
namespace ShadowU
{
    public class AttackingState : FsmState<Enemy>
    {
        private Enemy owner;
        private Transform targetTran;
        private float speed = 0.7f; //速度  因为即便进入了攻击范围还要靠近走走
        private Color preCol;       //之前Enemy的颜色
        protected override void OnDestroy(IFsm<Enemy> fsm)
        {
            base.OnDestroy(fsm);
        }

        protected override void OnEnter(IFsm<Enemy> fsm)
        {
            base.OnEnter(fsm);
            owner = fsm.Owner;
            targetTran = owner.targetTran;

            //进入攻击状态  Enemy变为红色
            preCol = owner.GetComponent<SpriteRenderer>().material.color;
            owner.GetComponent<SpriteRenderer>().material.color = Color.red;
            Log.Info("变红色!");
        }

        protected override void OnInit(IFsm<Enemy> fsm)
        {
            base.OnInit(fsm);
        }

        protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
        {
            base.OnLeave(fsm, isShutdown);

            //退出攻击状态 颜色变回去
            owner.GetComponent<SpriteRenderer>().material.color = preCol;

            owner = null;
            targetTran = null;
        }

        protected override void OnUpdate(IFsm<Enemy> fsm, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);

            //慢慢靠近目标
            Vector3 direction = Vector3.Normalize(targetTran.position - owner.transform.position);
            owner.transform.Translate(direction * speed * elapseSeconds);

            //如果超出了攻击范围 则进入追逐状态
            if (!owner.IsInAttackRange())
            {
                ChangeState<ChasingState>(fsm);
            }
        }
    }
}

  • ChasingState

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
namespace ShadowU
{
    public class ChasingState : FsmState<Enemy>
    {
        private Enemy owner;
        private Transform targetTran;
        private float speed; //速度
        protected override void OnDestroy(IFsm<Enemy> fsm)
        {
            base.OnDestroy(fsm);
        }

        protected override void OnEnter(IFsm<Enemy> fsm)
        {
            base.OnEnter(fsm);
            owner = fsm.Owner;
            targetTran = owner.targetTran;
            speed = owner.enemyData.Speed;
        }

        protected override void OnInit(IFsm<Enemy> fsm)
        {
            base.OnInit(fsm);
        }

        protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
        {
            base.OnLeave(fsm, isShutdown);
            owner = null;
            targetTran = null;
        }

        protected override void OnUpdate(IFsm<Enemy> fsm, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);

            //追逐玩家
            Vector3 direction = Vector3.Normalize(targetTran.position - owner.transform.position);
            owner.transform.Translate(direction * speed * elapseSeconds);
            
            //如果超出了追逐的活动范围则进入返回状态
            if (owner.IsOutOfChasingRange())
            {
                ChangeState<ReturningState>(fsm);
                return;
            }
            //如果进入了攻击范围则进入攻击状态
            if (owner.IsInAttackRange())
            {
                ChangeState<AttackingState>(fsm);
            }
        }
    }
}
  • ReturningState

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
namespace ShadowU
{
    public class ReturningState : FsmState<Enemy>
    {
        private Enemy owner;
        private float speed;
        private float timer; //敌人返回两秒后才会检测玩家是否进入发现范围,否则如果玩家一直在发现范围内Enemy会卡在追逐范围边缘。
        protected override void OnDestroy(IFsm<Enemy> fsm)
        {
            base.OnDestroy(fsm);
        }

        protected override void OnEnter(IFsm<Enemy> fsm)
        {
            base.OnEnter(fsm);
            owner = fsm.Owner;
            speed = owner.enemyData.Speed;
            timer = 2.0f;   
        }

        protected override void OnInit(IFsm<Enemy> fsm)
        {
            base.OnInit(fsm);
        }

        protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
        {
            base.OnLeave(fsm, isShutdown);
            owner = null;
        }

        protected override void OnUpdate(IFsm<Enemy> fsm, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);

            //向Enemy的原点移动
            Vector3 direction = Vector3.Normalize(owner.enemyData.Origin- owner.transform.position);
            owner.transform.Translate(direction * speed * elapseSeconds);

            if(timer > 0)
            {
                timer -= elapseSeconds;
            }
            else
            {
                //玩家进入发现范围 转换到  追逐状态
                if (owner.IsPlayerFound())
                {
                    ChangeState<ChasingState>(fsm);
                    return;
                }
            }
            //Enemy进入了巡逻范围 则转到巡视状态
            if (owner.IsInIdleRange())
            {
                ChangeState<IdleState>(fsm);
            }
        }
    }
}

 2、Enemy类

       Enemy类也进行了一点修改,主要就是加入了四个检测:IsPlayerFound(),IsInAttackRange(),IsOutOfChasingRange()和IsInIdleRange();以及创建、启动和销毁状态机的内容。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
using UnityGameFramework.Runtime;
namespace ShadowU
{
    public class Enemy : EntityLogic
    {
        public EnemyData enemyData;//敌人实体数据类
        public Transform targetTran; //玩家Transform  即Enemy追逐的目标
        private IFsm<Enemy> fsm;     //状态机
        protected override void OnInit(object userData)
        {
            base.OnInit(userData);
            CachedTransform.position = ((EnemyData)userData).Position;
        }

        protected override void OnShow(object userData)
        {
            base.OnShow(userData);
            enemyData = (EnemyData)userData;
            /*
            Log.Debug("敌人加载成功");
            Log.Info(enemyData.Origin);
            Log.Info(enemyData.IdleRange);
            Log.Info(enemyData.ChasingRange);
            Log.Info(enemyData.AlarmRange);
            Log.Info(enemyData.AttackRange);
            Log.Info(enemyData.Speed);
            */
            targetTran = GameObject.FindGameObjectWithTag("Player").transform;
            //状态类的列表
            List<FsmState<Enemy>> states = new List<FsmState<Enemy>>(){ new IdleState() ,new ChasingState(),new ReturningState(),new AttackingState()};
            //创建状态机
            fsm = GameEntry.Fsm.CreateFsm<Enemy>("EnemyFsm",this,states);
            //启动状态机
            fsm.Start<IdleState>();
        }

        protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(elapseSeconds, realElapseSeconds);

        }
        /// <summary>
        /// 是否已经发现了玩家(检测玩家是否在Alarm范围内)
        /// </summary>
        /// <returns></returns>
        public bool IsPlayerFound()
        {
            return Vector3.Distance(transform.position,targetTran.position)<enemyData.AlarmRange;
        }
        /// <summary>
        /// 玩家是否处于Enemy的攻击范围
        /// </summary>
        /// <returns></returns>
        public bool IsInAttackRange()
        {
            return Vector3.Distance(transform.position, targetTran.position) < enemyData.AttackRange;
        }
        /// <summary>
        /// Enemy是否超出了追逐范围
        /// </summary>
        /// <returns></returns>
        public bool IsOutOfChasingRange()
        {
            return Vector3.Distance(transform.position, enemyData.Origin) > enemyData.ChasingRange;
        }
        public bool IsInIdleRange()
        {
            return Vector3.Distance(transform.position, enemyData.Origin) < enemyData.IdleRange;
        }

        protected override void OnHide(bool isShutdown, object userData)
        {
            base.OnHide(isShutdown, userData);

            GameEntry.Fsm.DestroyFsm(fsm);
            fsm = null;
        }
    }
}



五、测试运行

        看看效果!

        

 搞定!