【Unity】RPG2D龙城纷争(十六)敌方AI

更新日期:2024年7月24日。
项目源码:第五章发布(正式开始游戏逻辑的章节)

简介

本章我们将进入让整个游戏真正跑起来的核心环节,那便是敌方阵营的AI,能够调遣敌方所有角色进行移动追击围堵玩家角色,才是真正体现游戏性的标志。

一、敌方AI调遣策略

首先,我们来设计敌方AI针对敌方所有角色的调遣策略:

设敌方角色A(会为所有敌方角色执行此调遣策略):
1.【收集】:收集所有玩家角色的评估数据;
2.【评估】:评估所有玩家角色数据相对于自己的价值;
3.【行动】:向价值最高目标发起行动(攻击、追击等)。

简言之,此策略可归纳为如下三步:

收集 => 评估 => 行动

通过自定义收集算法、评估算法,可以使得敌方所有角色始终保持向高价值目标行动的趋势。

二、敌方AI基类

那么首先,我们依然是抽象一个敌方AI的基类AI

    /// <summary>
    /// 敌人AI
    /// </summary>
    public abstract class AI
    {
        /// <summary>
        /// 调遣所有敌人行动
        /// </summary>
        /// <param name="level">关卡</param>
        /// <param name="enemyRoles">敌人列表</param>
        /// <param name="playerRoles">玩家列表</param>
        /// <param name="endCallback">调遣结束回调</param>
        public void DispatchAllEnemy(Level level, List<Role> enemyRoles, List<Role> playerRoles, HTFAction endCallback)
        {
            
        }
    }

我们只需要使自己编写的AI类型继承至AI类,其便作为一个AI控制器独立存在,通过定义不同的AI类型还可以实现各种风格或难度的AI,比如擅长围堵的,擅长风筝的,擅长逃跑的…

三、为关卡指定AI类型

然后回到我们的关卡(Level)类,在其中添加AI相关字段:

        /// <summary>
        /// AI类型
        /// </summary>
        [Label("敌人AI类型"), ClassType(typeof(AI))] public string AIType = "<None>";

        /// <summary>
        /// 敌人AI实例
        /// </summary>
        public AI EnemyAI { get; private set; }

ClassType使得其能够检索AI类的所有子类,<None>则代表无AI类型,这将是不被允许的。

然后在关卡(Level)的检视器面板便可以直接选择所有的AI类型:

在这里插入图片描述

四、创建AI控制器

接着我们在关卡(Level)初始化(Initialize)方法中创建AI控制器:

        public virtual void Initialize()
        {
            if (!string.IsNullOrEmpty(AIType) && AIType != "<None>")
            {
                Type type = ReflectionToolkit.GetTypeInRunTimeAssemblies(AIType);
                EnemyAI = Activator.CreateInstance(type) as AI;
            }
            else
            {
                Log.Error($"关卡【{Name}】的敌人AI类型为空,这是不被允许的!");
            }
        }

五、调遣所有敌人行动

我们在之前的章节中已经对EnemyAI有所涉及了,也即是在玩家结束当前回合时,将控制器交由AI时,AI便会开始调遣所有敌人行动

        /// <summary>
        /// 玩家结束当前回合
        /// </summary>
        public void PlayerEndRound()
        {
            //......

			//调遣所有敌人行动
            EnemyAI.DispatchAllEnemy(this, EnemyRoles, PlayerRoles, () =>
            {
            	//行动结束后,进入下一回合
                StartCoroutine(EnterRound(Round + 1, false));
            });
        }

到此,AI的生命周期差不多了,接下来我们进入正题,开始编写AI调遣策略的三个阶段(收集、评估、行动)。

六、AI调遣策略第一阶段:收集

要知道,在玩家操作角色行动之前,也会对整个战场的数据进行收集以便对接下来的行动做出评估,所以为了模拟真人的操作,AI在这里也将进行这个数据收集的过程。

我们定义一个收集方法:

    public abstract class AI
    {
        /// <summary>
        /// 收集评估数据(具体如何收集,交由具体的AI子类来实现)
        /// </summary>
        /// <param name="enemy">敌人</param>
        /// <param name="player">玩家</param>
        protected virtual EvaluationData GenerateEvaluationData(Role enemy, Role player)
        {
            return null;
        }
    }

AI收集评估数据必然是一对一的(一个敌方角色对应一个玩家角色),只不过这个敌方角色会对所有玩家角色都收集一份评估数据

然后,我们定义评估数据类:

    /// <summary>
    /// AI评估数据
    /// </summary>
    public class EvaluationData : IReference
    {
        /// <summary>
        /// 评估的目标玩家
        /// </summary>
        public Role Target;
        /// <summary>
        /// 玩家的上方是否可抵达
        /// </summary>
        public bool IsUpCanMove;
        /// <summary>
        /// 玩家的下方是否可抵达
        /// </summary>
        public bool IsDownCanMove;
        /// <summary>
        /// 玩家的左方是否可抵达
        /// </summary>
        public bool IsLeftCanMove;
        /// <summary>
        /// 玩家的右方是否可抵达
        /// </summary>
        public bool IsRightCanMove;
        /// <summary>
        /// 可用来攻击玩家的要诀
        /// </summary>
        public Ability UseAbility;
        /// <summary>
        /// 针对玩家的行动模式(0:无法攻击,1:能直接攻击,2:移动后可攻击)
        /// </summary>
        public int ActMode;
        /// <summary>
        /// 玩家是否能反击
        /// </summary>
        public bool IsCanBackAttack;
        /// <summary>
        /// 玩家当前的路径距离(为0则无法抵达玩家位置)
        /// </summary>
        public int Distance;

        /// <summary>
        /// 重置数据
        /// </summary>
        public virtual void Reset()
        {
            Target = null;
            UseAbility = null;
        }
    }

当然,针对不同的AI类型,这里的评估数据是可以做出调整的,毕竟越强大的AI必然依赖越多的战场数据。

然后,我们在进入调遣所有敌人行动(DispatchAllEnemy)入口方法后,为所有敌方角色收集评估数据:

            for (int i = 0; i < enemyRoles.Count; i++)
            {
                Role enemy = enemyRoles[i];
                //敌方角色处于活动中,且未死亡
                if (enemy.State == RoleState.Actived && !enemy.IsDead)
                {
                    //回收评估数据缓存列表
                    for (int j = 0; j < _evaluationDatas.Count; j++)
                    {
                        Main.m_ReferencePool.Despawn(_evaluationDatas[j]);
                    }
                    _evaluationDatas.Clear();
                    //此敌方角色,针对所有玩家角色,开始收集评估数据
                    for (int j = 0; j < playerRoles.Count; j++)
                    {
                        Role player = playerRoles[j];
                        if (player.State == RoleState.Actived && !player.IsDead)
                        {
                        	//将评估数据加入缓存列表
                            _evaluationDatas.Add(GenerateEvaluationData(enemy, player));
                        }
                    }
                }
            }

七、AI调遣策略第二阶段:评估

上一阶段已经收集到了敌方角色针对所有玩家角色的评估数据,那么接下来就是对所有数据做出评估,同时量化为可横向比较的价值

我们依然定义一个评估方法:

    public abstract class AI
    {
        /// <summary>
        /// 评估一个玩家对于当前敌人的价值(具体如何评估,交由具体的AI子类来实现)
        /// </summary>
        /// <param name="enemy">敌人</param>
        /// <param name="data">评估数据</param>
        /// <returns>总价值,价值越高,敌人越优先向其发起行动</returns>
        protected virtual int Evaluation(Role enemy, EvaluationData data)
        {
            return 0;
        }
    }

对上一阶段收集到的所有评估数据进行评估:

            for (int i = 0; i < enemyRoles.Count; i++)
            {
                Role enemy = enemyRoles[i];
                
                //收集评估数据......

                //开始评估价值最高的目标
                EvaluationData data = null;
                int cost = -1;
                for (int j = 0; j < _evaluationDatas.Count; j++)
                {
                    int newCost = Evaluation(enemy, _evaluationDatas[j]);
                    if (newCost > cost)
                    {
                        data = _evaluationDatas[j];
                        cost = newCost;
                    }
                }
            }

到这一阶段,最高的价值cost对应的目标数据data便已经被筛选出来了,接下来只需向其指向的目标角色发起行动即可。

八、AI调遣策略第三阶段:行动

为此,我们再定义一个行动方法:

    public abstract class AI
    {
        /// <summary>
        /// 敌人向玩家发起行动(具体如何行动,交由具体的AI子类来实现)
        /// </summary>
        /// <param name="enemy">敌人</param>
        /// <param name="data">AI评估数据</param>
        protected virtual IEnumerator EnemyActing(Role enemy, EvaluationData data)
        {
        	//行动完毕后,enemy 被标记为已行动,并等待1秒后,结束此角色的调遣
            enemy.IsActed = true;
            yield return YieldInstructioner.GetWaitForSeconds(1);
        } 
    }

然后,对评估阶段筛选出来的最佳目标发起行动:

            for (int i = 0; i < enemyRoles.Count; i++)
            {
                Role enemy = enemyRoles[i];
                
                //收集评估数据......

                //评估价值最高目标......

	 		    //向价值最高目标发起行动,并等待行动结束
                 if (data != null)
                 {
                     yield return EnemyActing(enemy, data);
                 }
            }

到此,AI调遣策略的三个阶段便简介明了的展现出来了,它的代码量极其精简,但可扩展的空间却相当的大,核心的算法都将由具体的AI类型在收集评估行动这三个阶段做出具体的实现。

那么,下一篇我们将基于此探讨一个默认AI类型(AI_Normal)的实现。

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Unity2D RPG手游源码是一种可用于制作角色扮演类手游的代码基础。Unity是一款游戏引擎,而2D RPG则代表游戏类型为二维角色扮演。通过使用Unity2D RPG手游源码,开发者可以快速搭建一个具备角色选择、剧情交互、战斗系统、升级系统等基本要素的手游。 该源码通常包含了游戏的各种元素和功能的实现代码,如角色控制、任务系统、道具系统、技能系统等。使用源码可以节省开发时间,同时也便于开发者进行二次开发和定制。 对于想要制作二维角色扮演类手游的开发者来说,Unity2D RPG手游源码是一个很好的起点。通过学习和修改源码,开发者可以根据自己的需求来制作出具备独特特色的游戏。 值得一提的是,源码只是游戏开发的一部分,还需要配合各种素材和资源进行开发。开发者需要设计游戏世界的地图、角色、怪物和场景,同时编写数值平衡和游戏逻辑等。所以除了掌握源码,开发者还需要具备一定的美术和设计能力。 总之,Unity2D RPG手游源码是制作二维角色扮演类手游的基础代码。通过学习和修改源码,开发者可以制作出符合自己需求的游戏,并需要配合素材和资源进行完整的开发工作。 ### 回答2: Unity2D RPG 手游源码是用于开发二维角色扮演游戏的解决方案。该源码提供了一系列游戏开发所需的基本功能和特性,以便开发者能够快速搭建和定制自己的RPG游戏。 在Unity2D RPG 手游源码中,通常会包括以下主要功能: 1. 角色控制:该源码提供了用于角色移动、跳跃、攻击和技能释放等动作控制的功能。开发者可以根据游戏需求进行自定义调整。 2. 物品系统:该源码通常包含物品的管理系统,可以实现物品的获取、使用和存储。开发者可以轻松添加各种装备、道具和消耗品。 3. NPC 交互:该源码提供了与非玩家角色(NPC)进行对话、任务接取和完成等交互功能。开发者可以添加自己的任务和对话系统。 4. 角色属性:该源码通常包括角色的属性系统,例如生命值、魔法值、攻击力和防御力等。开发者可以调整属性数值来平衡游戏玩法。 5. 场景管理:该源码提供了场景之间的切换和加载功能,使得开发者可以构建出一个连贯的游戏世界。 6. 性能优化:该源码通常会包含一些优化技巧,以确保游戏在不同设备上有平稳的运行体验。 总体来说,Unity2D RPG 手游源码是一个强大且灵活的解决方案,可以帮助开发者更轻松地构建自己的角色扮演游戏。通过使用该源码,开发者可以加速开发进程,同时还可以根据自身需求对游戏系统进行自定义和扩展。 ### 回答3: Unity2D RPG 手游源码是一套用于制作角色扮演类手游的代码资源。Unity是一款流行的游戏开发引擎,2D表示游戏是以平面的方式进行展示,RPG是角色扮演游戏的缩写。 这套源码提供了一些基础的游戏功能,比如角色的移动、攻击、升级、装备等。通过使用这些源码,开发者可以快速搭建起一个简单的RPG手游框架,并在此基础上添加更多自定义的功能和内容。 源码中通常包含了角色的属性和能力系统,游戏地图的编辑器,角色控制脚本,敌人AI脚本等。开发者可以在此基础上进行扩展和修改,以适应自己的游戏需求。 使用Unity2D RPG 手游源码可以大大节省游戏开发的时间和精力,因为它提供了一个已经实现了一些常见功能的蓝图。同时,它还可以作为学习游戏开发的工具,通过研究源码中的实现方式和逻辑,开发者能够更好地理解和掌握游戏开发的技术。 然而,使用源码也需要具备一定的编程知识和经验。开发者需要熟悉Unity引擎以及C#编程语言,才能使用源码进行修改和定制。 总之,Unity2D RPG 手游源码是一种快速开发角色扮演类手游的解决方案,它提供了基础功能和框架,可以帮助开发者节省时间和精力。但在使用之前需要具备一定的编程知识和经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神码编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值