XYGame-AI设计2-FSM
XYGame-AI设计2-FSM
梦想游戏人 发表于10个月前
XYGame-AI设计2-FSM
  • 发表于 10个月前
  • 阅读 26
  • 收藏 0
  • 点赞 0
  • 评论 0

新睿云服务器60天免费使用,快来体验!>>>   

把具体的行为代码(移动,攻击)写在Enemy 里面,然后决策(仇恨)用FSM 状态机来做

AI状态 流程图

 

把流程图拆解为 几个状态后,编写以下代码

AIEnemy.cs

/*
* Email:   me@dreamyouxi.com
using FSM to peocess lite AI
 */
using UnityEngine;
using System.Collections;

namespace AIEnemy
{
    public class FSMBase : GAObject
    {
        public Enemy host = null;
        public FSMMachine machine = null;
        public override void UpdateMS()
        {

        }
        public override void OnEnter()
        {

        }
        public override void OnExit()
        {

        }
        public override bool Init()
        {
            base.Init();
            return true;
        }
        //  ------ helper function
        public void ChangeTo<T>() where T : FSMBase, new()
        {
            machine.ChangeTo<T>();
        }
    }
    /// <summary>
    /// 锁定塔状态 
    /// 该状态下只会攻击塔
    /// </summary>
    public class LockTower : FSMBase
    {
        public override void UpdateMS()
        {
            if (host.target == null)
            {
                this.ChangeTo<Free>();
            }
        }
        public override void OnEnter()
        {
            host.bullet_atk1_info.AddHitTarget(host.target);//锁定只攻击  Tower
        }
        public override void OnExit()
        {

        }
    }

    /// <summary>
    /// 自由状态 
    /// 目标死亡 或者 刚出生 才会在该状态下
    /// </summary>
    public class Free : FSMBase
    {
        public override void UpdateMS()
        {
            if (BuildingMgr.ins.GetBuildingsCount() > 0)
            { //存在塔
                ArrayList list = HeroMgr.ins.GetHeros();
                bool has_hero = false;
                if (list.Count > 0)
                {
                    foreach (Hero h in list)
                    {
                        if (host.target_distance > h.ClaculateDistance(host))
                        {//范围内 有玩家
                            has_hero = true;
                            break;
                        }
                    }
                }
                if (has_hero) // 仇恨范围内有玩家
                {//锁定玩家攻击
                    if (host.AI_SearchNearestTarget(list, true)) ;
                    {
                        this.ChangeTo<LockHero>();
                    }
                }
                else // 仇恨范围内没有玩家,直接进入半锁定塔状态
                {
                    this.ChangeTo<LockHalfTowerBefore>();
                }
            }
            else
            {//不存在塔,直接锁定最近玩家攻击
                if (host.AI_SearchNearestTarget(HeroMgr.ins.GetHeros(), true))
                {
                    this.ChangeTo<LockHero>();
                }
                else
                {//没有任何攻击目标 随机走动
                    if (Utils.random_frameMS.Next(0, 300) == 5 || dir == -1)
                    {
                        Terrain terrain = AppMgr.GetCurrentApp<BattleApp>().GetCurrentWorldMap().GetTerrain();
                        Vector2 to = new Vector2(Utils.random_frameMS.Next((int)(terrain.limit_x_left * 1000), (int)(terrain.limit_x_right * 1000)) / 1000f, Utils.random_frameMS.Next((int)(terrain.limit_z_down * 1000), (int)(terrain.limit_z_up * 1000)) / 1000f);
                        dir = (int)Utils.GetAngle(host.pos, to);
                    }
                    host.dir = dir;// Utils.random_frameMS.Next(0, 361);
                }
            }
        }
        public override void OnEnter()
        {
            if (host != null)
            {//目标没死
                host.bullet_atk1_info.hit_targets.Clear();//清空目标
            }
        }
        public override void OnExit()
        {

        }

        private int dir = -1;

    }

    /// <summary>
    /// 半锁定塔 状态后
    /// 在该状态下  只有被玩家攻击才会 攻击玩家 否则 一直攻击塔
    /// </summary>
    public class LockHalfTowerAfter : FSMBase
    {
        public override void UpdateMS()
        {
            if (host.target == null || host == null)
            {//如果塔 失效
                ChangeTo<Free>();
                return;
            }
        }
        public override void OnEnter()
        {

        }
        public override void OnDispose()
        {
            EventDispatcher.ins.RemoveEventListener(this, Events.ID_BATTLE_ENTITY_BEFORE_TAKEATTACKED);
        }
        public override void OnExit()
        {

        }
        public override void OnEvent(int type, object userData)
        {
            if (this.IsInValid()) return;
            if (type == Events.ID_BATTLE_ENTITY_BEFORE_TAKEATTACKED)
            {
                AttackInfo info = userData as AttackInfo;
                if (info.target as Enemy != host) return;

                {//已经命中 塔 后 被攻击 会切换,在搜索阶段 被命中 也会进入锁定hero
                    //自己被命中
                    if (info.ownner.IsMaxTarget() == false)
                    {
                        host.target = info.ownner;
                        machine.ChangeTo<LockHero>();
                    }
                }
            }
        }
        public override bool Init()
        {
            base.Init();

            EventDispatcher.ins.AddEventListener(this, Events.ID_BATTLE_ENTITY_BEFORE_TAKEATTACKED);
            return true;
        }

    }
    /// <summary>
    /// 半锁定塔 命中前,
    /// 该状态 下 仇恨指向 塔  但未命中塔 在此期间 如果范围内 有玩家 会更改为锁定玩家攻击
    /// </summary>
    public class LockHalfTowerBefore : FSMBase
    {
        public override void UpdateMS()
        {
            if (has_find)//已找到塔 
            {
                if (has_shoot == false)
                { //发起攻击 塔
                    if (host.target == null || host == null)
                    {
                        ChangeTo<Free>();
                        return;
                    }
                    this.Shoot();
                }
            }
            else
            {
                if (host.AI_SearchNearestTarget(BuildingMgr.ins.GetBuildings(), false))//寻找最近的建筑作为目标
                {//找到 目标  
                    has_find = true;
                }
                else
                {//搜索失败 ,进入Free
                    machine.ChangeTo<Free>();
                    return;
                }
            }

            //在此期间如果仇恨范围内有玩家,那么切换仇恨为玩家
            ArrayList list = HeroMgr.ins.GetHeros();
            bool has_hero = false;
            if (list.Count > 0)
            {
                foreach (Hero h in list)
                {
                    if (host.target_distance > h.ClaculateDistance(host))
                    {//范围内 有玩家
                        has_hero = true;
                        break;
                    }
                }
            }
            if (has_hero) // 仇恨范围内有玩家
            {//锁定玩家攻击
                if (host.AI_SearchNearestTarget(list, true)) ;
                {
                    this.ChangeTo<LockHero>();
                    return;
                }
            }
        }
        public override void OnEnter()
        {
            has_shoot = false;
            has_find = false;
        }
        public override void OnExit()
        {
            if (info_backup != null)
            {
                host.bullet_atk1_info = info_backup;
                info_backup = null;
            }
        }
        private void Shoot()
        {
            info_backup = host.bullet_atk1_info;
            //      host.atk = true;

            BulletConfigInfo info = BulletConfigInfo.Create();
            info.AddHitTarget(host.target);
            info.plistAnimation = "";
            info.distance = 0.2f;
            info.distance_atk = 1f;
            info.number = 1;
            info.collider_size = new Vector3(2f, 2f, 2f);
            info.AddHitTarget(host.target);
            info._OnTakeAttack = (Bullet bullet, object userData) =>
            {
                //命中后  
                ChangeTo<LockHalfTowerAfter>();
            };
            has_shoot = true;
            host.bullet_atk1_info = info;
        }

        public override bool Init()
        {
            base.Init();
            return true;
        }
        private BulletConfigInfo info_backup = null;
        private bool has_shoot = false;
        private bool has_find = false;

    }

    /// <summary>
    /// 锁定玩家状态
    /// 该状态只会攻击玩家 直到 玩家死亡
    /// </summary>
    public class LockHero : FSMBase
    {
        public override void UpdateMS()
        {
            if (host.target == null)
            {
                this.ChangeTo<Free>();
            }
        }
        public override void OnEnter()
        {
            host.bullet_atk1_info.AddHitTarget(host.target);//锁定只攻击 Hero
        }
        public override void OnExit()
        {

        }
    }

    /// <summary>
    /// FSM  状态机
    /// </summary>
    public class FSMMachine : GAObject
    {
        public override void UpdateMS()
        {
            if (host.ai_type != AIEnemyType.FSM) return;
            if (pause) return;
            if (current_fsm != null)
            {
                current_fsm.UpdateMS();
                //    Debug.Log("FSM: " + current_fsm.GetType().ToString());
            }
        }
        public override void OnEnter()
        {

        }
        public override void OnExit()
        {

        }
        public override bool Init()
        {
            base.Init();
            current_fsm = Utils.Create<Free>();
            current_fsm.machine = this;
            current_fsm.host = host;
            current_fsm.Init();
            return true;
        }
        public void ChangeTo(FSMBase fsm)
        {
            if (current_fsm != null)
            {
                current_fsm.OnExit();
                current_fsm.LazyDispose();
            }
            fsm.host = host;
            fsm.machine = this;
            fsm.OnEnter();
            current_fsm = fsm;
        }
        public void ChangeTo<T>() where T : FSMBase, new()
        {
            FSMBase fsm = Utils.Create<T>();
            fsm.Init();
            this.ChangeTo(fsm);
        }
        public void Pause()
        {
            pause = true;
        }
        public void Resume()
        {
            pause = false;
        }
        public bool IsPause()
        {
            return pause;
        }
        private bool pause = false;
        FSMBase current_fsm = null;
        public Enemy host = null;
    }
}

下篇,为行为树

源代码:https://git.oschina.net/dreamyouxi/XYGame

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 33
博文 387
码字总数 110724
×
梦想游戏人
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: