编辑代码

using System;

namespace AI.FSM
{
    public enum FSMTriggerID
    {
        NoHealth = 1,
        FoundTarget = 2,
        ReachTarget = 3,
        LoseTarget = 4,
        CompletePatrol = 5,
        KilledTarget = 6
        WithoutAttackRange = 7
    }

    public enum FSMStateID
    {
        None = 1,
        Idle = 2,
        Dead = 3,
        Pursuit = 4,
        Attacking = 5,
        Default = 6, 
        Patrolling = 7
    }

    public enum PatrolMode
    {
        Once,
        Loop,
        PingPong
    }

    public abstract class FSMBase: MonoBehvaviour
    {
        public FSMStateID test_currentStateID;  // 调试使用 
        [Tooltip("当前状态机使用的配置文件")]public string configFile = "AI_01.txt";  // 配置文件   

        private List<FSMState> states;
        private FSMState currentState;
        private FSMState defaultState;
        private NavMeshAgent navAgent; //使用NavMeshAgent组件前,记得要先烘焙

        [Tooltip("默认状态编号")]public FSMStateID defaultStateID;
        [HideInInSpector]public Animator anim;
        [HideInInSpector]public CharacterStatus chStatus;
        [HideInInSpector]public Transform targetTF;
        [HideInInSpector]public CharacterSkillSystem skillSystem;
        [HideInInSpector]public bool isPatrolComplete;

        [Tooltip("攻击目标标签")]public string[] targetTags = {"Player"}; 
        [Tooltip("视野距离")]public float sightDistance = 10; 
        [Tooltip("跑动速度")]public float moveSpeed = 2;
        [Tooltip("走动速度")]public float walkSpeed = 1;
        [Tooltip("路点")]public Transform[] wayPoints;
        [Tooltip("巡逻模式")]ublic PatrolMode patrolMode;

        

        #region  //脚本生命周期
        private void Start()
        {
            InitComponent();
            ConfigFSM();
            InitDefaultState();
        }

        // 每帧处理的逻辑
        private void Update()
        {
            test_currentStateID = currentState.StateID;  // 调试使用
            // 判断当前状态条件
            currentState.Reason(this);
            // 执行当前状态逻辑   
            currentState.ActionState(this); 
            // 搜索目标
            SearchTarget();
        }
        #endregion

        #region //状态机自身成员
        // 配置状态机 
        // StreamingAssets 下放一个文件 AI_01.txt 配置文件,放状态条件转换映射表
        private void ConfigFSM()
        {
            //通过配置文件 
            states = new List<FSMState>();
            // 每个文件创建一个读取器对象
            var map = AIConfigurationFactory.GetMap(fileName);

            foreach(var state in map)
            {
                Type type = Type.GetType("AI.FSM." + state.Key + "State");
                FSMState stateObj = Activator.CreateInstance(type) as FSMState
                states.Add(stateObj);
                foreach(var dic in state.Value)
                {
                    // dic.Key,dic.Value 的值都是字符串,但下面需要枚举值,需要转换
                    FSMTriggerID triggerID = (FSMTriggerID)Enum.Parse(typeof(FSMTriggerID), dic.Key);//字符串 -》 枚举
                    FSMStateID stateID = (FSMStateID)Enum.Parse(typeof(FSMStateID), dic.Value);
                    stateObj.AddMap(triggerID, stateID);
                }
                
            }

        }

        // 切换状态
        public void ChangeActiveState(FSMStateID stateID)
        {
            // 设置当前状态
            // 如果需要切换的状态编号是 default,则直接返回默认状态;
            // 否则从状态列表中查找
            //if(stateID == FSMStateID.Default)
            //    currentState = defaultState;
            //else    
            //    currentState = states.Find(s => s.StateID == stateID);
            currentState.ExitState(this); // 离开上一个状态
            // 切换状态
            currentState = stateID == FSMStateID.Default? defaultState: states.Find(s => s.StateID == stateID);
            currentState.EnterState(this);  // 进入下一个状态
        }
        #endregion

        #region //状态机自身成员
        private void InitDefaultState()
        {
            defaultState = states.Find(s => s.StateID == defaultStateID); // 查找默认状态
            currentState = defaultState; // 为当前状态赋值
            currentState.EnterState(this);  // 进入状态
        }

        public void InitComponent()
        {
            anim = GetComponentInChilderen<Animator>();
            chStatus = GetComponent<CharacterStatus>();
            navAgent = GetComponent<NavMeshAgent>();
            skillSystem = GetComponent<CharacterSkillSystem>();
        }

        // 查找目标,找所有的有指定标签的目标,判断释放在圆形一定范围内,活的目标,单个目标
        private void SearchTarget()
        {
            SkillData data = new SkillData()
            {
                attackTargets = targetTags,
                attackDistance = sightDistance;
                attackAngle = 360,
                attackType = SkillAttackType.Single
            };
            // 调技能系统查找,以后需要把改功能单独抽出来做一个类
            Transform[] targetArr = new SectorAttackSelector().SelectTarget(data, transform);
            targetTF = targetArr.Length == 0 ? null : targetArr[0];
        }

        // 移动
        public void MoveToTarget(Vector3 position, float stopDistance, float moveSpeed)
        {
            //通过寻路组件实现
            navAgent.SetDestination(position);
            navAgent.stoppingDistance = stopDistance;
            navAgent.speed = moveSpeed;
        }

        public void StopMove()
        {
            navAgent.SetDestination(transform.position);
            // navAgent.enabled = false;
            // navAgent.enabled = true;
        }
        #endregion

        


    }

    
    public abstract class FSMState
    {

        private Dictionary<FSMTriggerID, FSMStateID> map; //映射表
        
        private List<FSMTrigger> Triggers;  // 条件列表

        public FSMStateID StateID{get; set;}

        public FSMState()
        {
            map = new Dictionary<FSMState, FSMStateID>();
            Triggers = new List<FSMTrigger>();
            Init();
        }

        public abstract void Init(); // 要求实现类必须初始化状态类,为编号赋值

        // 检测当前状态的条件是否满足
        public void Reason(FSMBase fsm)
        {
            for(int i=0; i<Triggers.Count; i++)
            {
                // 发现条件满足
                if(Trigger[i].HandleTrigger(fsm))
                {
                    // 从映射表中获取输出状态
                    FSMStateID stateID = map[Triggers[i].TriggerID]
                    // 切换状态
                    fsm.ChangeActiveState(stateID);
                    return;
                }
            }
        }

        // 由状态机FSMBase调用(为映射表和条件列表赋值)
        public void AddMap(FSMTriggerID triggerID, FSMStateID stateID)
        {
            map.Add(triggerID, stateID); // 添加映射
            CreateTrigger(triggerID);  // 创建条件对象
        }

        private void CreateTrigger(FSMTriggerID triggerID)
        {
            // 反射创建条件对象
            // 命名规范: AI.FSM + 条件枚举 + Trigger
            Type type = Type.GetType("AI.FSM." + triggerID + "Trigger");
            FSMTrigger trigger = Activator.CreateInstance(type) as FSMTrigger;
            Triggers.Add(trigger);
        }

        // 为具体状态栏提供可选实现
        public virtual void EnterState(FSMBase fsm){}
        public virtual void ActionState(FSMBase fsm){}
        public virtual void ExitState(FSMBase fsm){}

    }


    public abstract class FSMTrigger
    {
        public FSMTriggerID TriggerID{get; set;}

        public FSMTrigger()
        {
            Init();
        };

        public abstract void Init();  // 要求子类必须初始化条件,为编号赋值

        public abstract bool HandleTrigger(FSMBase fsm); //逻辑处理
    }
}


namespace AI.FSM
{
    public class DeadState: FSMState
    {
        public override void Init()
        {
            StateID = FSMStateID.Dead;
        }

        public override void EnterState(FSMBase fsm)
        {
            base.EnterState(fsm);
            fsm.enabled = false;  // 禁用状态机
        }

    }

    public class PursuitState: FSMState
    {
        public override void Init()
        {
            StateID = FSMStateID.Pursuit;
        }

        public override void EnterState(fsm)
        {
            base.EnterState(fsm);
            fsm.anim.SetBool(fsm.chStatus.chParams.run, true);

        }

        public override void ActionState(FSMBase fsm)
        {
            base.ActionState(fsm);
            fsm.MoveToTarget(fsm.targetTF.position, fsm.chStatus.attackDistance, fsm.moveSpeed)
        }

        public override void ExitState(FSMBase fsm)
        {
            base.ExitState(fsm);
            // 停止移动
            fsm.StopMove();
            fsm.anim.SetBool(fsm.chStatus.chParams.run, false);
        }

    }

    public class AttackingState: FSMState
    {
        private float atkTime;

        public override void Init()
        {
            StateID = FSMStateID.Attacking;
        }

        public override void ActionState(FSMBase fsm)
        {

            base.ActionState(fsm);
            if(atkTime <= Time.time)
            {
                fsm.skillSystem.UseRandomSkill();
                atkTime = Time.time + fsm.chStatus.attackInterval;
            }

        }
    }

    public class PatrollingState: FSMState    
    {
        private int index;
        public override void Init()
        {
            StateID = FSMStateID.Patrolling;
        }

        public override void EnterState(FSMBase fsm)
        {
            base.EnterState(fsm);
            fsm.isPatrolComplete = false;
            fsm.anime.SetBool(fsm.chStatus.chParams.walk, true);
        }

        public override void ActionState(FSMBase fsm)
        {
            base.ActionState(fsm);
            // 根据巡逻模式 单次?循环?往返?
            switch(fsm.patrolMode)
            {
                case PatrolMode.Once:
                    OncePatrolling(fsm);
                    break;
                case PatrolMode.Loop:
                    LoopPatrolling(fsm);
                    break;
                case PatrolMode.PingPong:
                    PingPongPatrolling(fsm);
                    break;
            }
        }

        public override void ExitState(FSMBase fsm)
        {
            base.ExitState(fsm);
            fsm.anim.SetBool(fsm.chStatus.chParams.walk, false);
        }

        private void OncePatrolling(FSMBase fsm)
        {
            if(Vector3.Distance(fsm.transform.position, fsm.wayPoints[index].position) < 0.5f)
            {
                if(index == fsm.wayPoints.Length-1)
                {
                    fsm.isPatrolComplete = true;
                    return;
                }
                index++;
            }
            fsm.MoveToTarget(fsm.wayPoints[index].position, 0, fsm.walkSpeed);
        }

        private void LoopPatrolling(FSMBase fsm)
        {
            if(Vector3.Distance(fsm.transform.position, fsm.wayPoints[index].position) < 0.5f)
            {
                index = (index + 1) % fsm.wayPoints.Length;
            }
            fsm.MoveToTarget(fsm.wayPoints[index].position, 0, fsm.walkSpeed);
        }

        private void PingPongPatrolling(FSMBase fsm)
        {
            if(Vector3.Distance(fsm.transform.position, fsm.wayPoints[index].position) < 0.5f)
            {
                if(index == fsm.wayPoints.Length-1)
                {
                    //A B C C B A A B C  index++ A B C B A B C
                    //0 1 2 0 1 2 0 1 2  index++ 0 1 2 1 2 1 2
                    Array.Reverse(fsm.wayPoints);
                    //index++;  // 加不加都行
                }
                index = (index + 1) % fsm.wayPoints.Length;
            }
            fsm.MoveToTarget(fsm.wayPoints[index].position, 0, fsm.walkSpeed);
        }
    }

    public class TargetFoundTrigger: FSMTrigger
    {
        public override bool HandleTrigger(FSMBase fsm)
        {
            return fsm.targetTF != null;
        }

        public override void Init()
        {
            TriggerID = FSMTriggerID.FoundTarget;
        }
    }

    public class ReachTargetTrigger: FSMTrigger
    {
        public override bool HandleTrigger(FSMBase fsm)
        {
            if(fsm.targetTF == null) return false;
            return Vector3.Distance(fsm.transform.position, fsm.targetTF.position) <= fsm.chStatus.attackDistance;
        }

        public override void Init()
        {
            TriggerID = FSMTriggerID.ReachTarget;
        }
    }

    public class LoseTargetTrigger: FSMTrigger
    {
        public override bool HandleTrigger(FSMBase fsm)
        {
            return fsm.targetTF == null;
        }

        public override void Init()
        {
            TriggerID = FSMTriggerID.LoseTarget;
        }
    }

    public class WithoutAttackRangeTrigger: FSMTrigger
    {
        public override bool HandleTrigger(FSMBase fsm)
        {
            return Vector3.Distance(fsm.transform.position, fsm.targetTF.position) > fsm.chStatus.attackDistance;
        }

        public override void Init()
        {
            TriggerID = FSMTriggerID.WithoutAttackRange;
        }
    }

    public class KilledTargetTrigger: FSMTrigger
    {
        public override bool HandleTrigger(FSMBase fsm)
        {
            return fsm.TargetTF.GetComponent<CharacterStatus>().HP <= 0;
        }

        public override void Init()
        {
            TriggerID = FSMTriggerID.KilledTarget;
        }
    }

    public class CompletePatrolTrigger: FSMTrigger
    {
        public override bool HandleTrigger(FSMBase fsm)
        {
            return fsm.isPatrolComplete;
        }

        public override void Init()
        {
            TriggerID = FSMTriggerID.CompletePatrol;
        }
    }

    

}



namespace AI.FSM{}
public class AIConfigurationReader
{
    // 数据结构
    // 大字典:key状态, value 映射
    // 小字典:key条件编号, value 状态编号
    public Dictionary<string, Dictionary<string, string>> _map{get; private set;};
    private string mainKey;

    public AIConfigurationReader(string fileName)
    {
        _map = new Dictionary<string, Dictionary<string, string>>();
        // 读取配置文件
        string content = ConfigurationReader.GetConfigFile(fileName);
        // 解析配置文件
        ConfigurationReader.Reader(content, BuildMap)
    }


    private void BuildMap(string line)
    {
        line = line.Trim(); //去除空白,如果空行,则为空字符串
        if(string.IsNullOrEmpty(line)) return;
        if(line.StartsWith("[")) // 状态 [当前状态]
        {
            mainKey = line.Substring(1,line.Length-2);
            _map.Add(mainKey, new Dictionary<string, string());
        }
        else  //映射  条件>状态
        {
            string[] keyValue = line.Split('>');
            _map[mainKey].Add(keyValue[0], keyValue[1]);
        }
    }

}

// 作用:缓存配置文件读取器对象
public class AIConfigurationFactory
{
    private static Dictionary<string, AIConfigurationReader> cache;

    static AIConfigurationFactory()
    {
        cache = new Dictionary<string, AIConfigurationReader>();
    }

    public static Dictionary<string, Dictionary<string, string>> GetMap(string fileName)
    {
        if(!cache.ContainsKey(fileName))
        {
            cache.Add(fileName, new AIConfigurationReader(fileName));
        }
        return cache[fileName]._map;
    }
}