【Unity3D开发小游戏】《战棋小游戏》Unity开发教程

1、前言

此次想要作的一个小游戏,或者说一个小Demo,实际上是一个简单且传统的战棋战斗场景。初步的设计是:在2D世界里建立一张由六边形地块组成的战斗地图,敌我双方依据体力在地图上轮流行动并向对方发动攻击,先消灭掉全部敌人的一方将得到胜利。前端

这一辑将比上一辑的内容更简单,但完成后会是一个功能较完整且能够玩耍的Demo。git

我使用的Unity版本是2018.2.7,可是其实并无用到2018的任何新功能。程序员

2、Github地址

Github:github.com/elsong823/H… *能够本身下载源代码github

3、正文


预计将分为如下几篇:算法

一、建立战场 根据预约尺寸生成战场地图,并随机一些障碍物。后端

二、添加对战双方 向战场中添加做战单位,做战单位可被点选,并进行移动。数组

三、战场逻辑 做战双方按照顺序依次行动,可进行移动、攻击。缓存

四、添加战场UI 添加能够随时显示战况的Hud、为做战单位添加血条等。数据结构

五、扩展做战单位 丰富做战单位的类型,添加职业,并加入若干不一样类型的技能。ide

六、扩展战场地图 丰富战场地图,加入地形及道具等元素。

七、规范战斗配置 能够经过规范化的数据结构配置战场、职业、技能、道具等。


1、建立战场

| 目标 生成一个规定尺寸的战场,战场上的格子均为六边形,而且暂定两种格子类型:普通格子和障碍格子。

| 在开始以前我作了如下事情 1.建立了新的工程。 2.新建场景并保存在场景文件夹下。 3.删除默认建立的平行光,调整场景光照设置,弃用了天空盒等。 4.将相机调整为正交相机。 由于是从零点五开始系列,认为读者有必定的Unity引擎使用基础,就不对这些操做进行介绍了。

| 建立战场地图 我没有采用把数据和显示捆绑在一块儿的作法,而是将数据和显示分离。 个人思路是用一个战斗建立器来建立战斗,一场完整的战斗信息至少应包含地图及对战双方的信息。而Unity搭建的战斗场景则能够理解为一场战斗的显示器。

| 数据部分(主要) 战斗数据:地图尺寸,包含全部格子的二维数组。 格子数据:格子类型,所在行列,所在空间坐标。

在这里插入图片描述
| 显示部分(主要) 战场:一个用于组织全部格子对象的节点对象。 格子对象:一个能显示六边形瓦片的2D渲染器,并根据格子类型改变颜色。

在这里插入图片描述
当须要将战斗表现出来时,须要将以上数据装入一个用于显示的“战场”,这个战场就是在Unity世界里建立的。
在这里插入图片描述
| 在Unity中建立战场 目前战场的结构还很是简单,只须要一个组织格子单位的节点。
在这里插入图片描述
| 建立格子 建立一个格子脚本,并挂在一个新建立的空对象上。
在这里插入图片描述
为这个空对象添加一个子对象,称之为瓦片,用来显示一个六边形。

由于这里我只想作一个2D的Demo,所以咱们只须要一个SpriteRenderer组件便可。

在这里插入图片描述
使用预先准备的一张六边形图片做为地块显示,一个格子对象就作好了。
在这里插入图片描述
为了区分普通和障碍,暂定普通格子为白色,障碍格子为灰色。
在这里插入图片描述
| 部分代码
在这里插入图片描述
铺设普通格子
在这里插入图片描述
随机放一些障碍格子

在这里插入图片描述
战场根据格子信息,显示格子
在这里插入图片描述
格子根据不一样类型显示不一样颜色
在这里插入图片描述
初始随机多张地图,并切换查看。

2、添加战场地图功能

| 目标 实现一些后面所需的操做地图的基础功能,如: 一、点击战场后高亮显示被点中的格子; 二、点击两个位置实现从A到B的导航并在地图上显示路径; 三、设定一个移动半径,点中格子后显示出可移动范围。

实现后的效果以下图:

在这里插入图片描述
点击格子变红
在这里插入图片描述
红色向蓝色导航(黄色为路径,青色为探索过但没有采用的格子)
在这里插入图片描述
半径为2个单位的可移动范围

| 点击格子变红 我采用的方法是: 一、获取屏幕点击位置的世界坐标,并将其转换到格子的Root节点下; 二、用这个坐标推断出点击格子所在的行、列范围; 三、遍历这些推测格子的中心,找到距离最近的格子,即为点中的格子。

在这里插入图片描述
点击屏幕后推断的九个格子

| 调整地图瓦片渲染器的位置 为了方便计算格子的位置,这里调整了瓦片渲染器的位置,保证它在地块对象的中心,这样在设置、获取、计算坐标时,少一步转换的操做,也更容易被理解。

在这里插入图片描述
| 导航 这里采用的导航是A星算法,网上的介绍不少,就再也不赘述了。在这只对六边形地图上两格之间最短移动距离的计算作个简单说明。

六边形格子的路程计算

与四边形地图差异不大,六边形地图也可理解为先作行移动,再作列移动。但它的差别是:在作行移动的同时,其列也能够在必定范围发生变化,由于它能够斜着走。

在这里插入图片描述
单元格坐标在仅计算行移动量的同时,列可移动范围是一个三角形。

在这里插入图片描述
起始点所在奇、偶行的差异,对覆盖三角形区域的计算是有影响的。 注:黑字表示格子的列,红字表示当前格子与出发格子的列差值。

综上,六边形地图下两格子之间的最近路程能够理解为:从起始位置先纵向移动,若是移动到与目标同行,可是目标格子又不在可到达格子范围内的话,再横向移动若干单位便可,如图:

在这里插入图片描述
须要注意的是,奇数、偶数行与首行是否“缩进”了半个格子有关。

| 调整数据结构 导航功能交给一个新的工具类:地图导航器来完成,可是原来地图中格子信息是直接保存在战斗数据对象中,如今我将地图数据从战斗数据中拆分出来,便于导航器集中处理数据。

在这里插入图片描述
旧 战斗数据的结构

在这里插入图片描述
新 战斗数据结构
在这里插入图片描述

| 根据半径显示可移动范围 预设半径显示可移动范围,也可转换为在进行行偏移的同时,求列覆盖的最小和最大值。

在这里插入图片描述
如在计算移动半径为2的覆盖区域时,假设从中心点开始,推算下方的区域,本质上是: 一、行移动量为0时,覆盖纵坐标偏移[-2~2]的区域。 二、行移动量为1时,覆盖纵坐标偏移[-1,0] + [-1, 1]的区域,其中[-1, 2]为行移动量为1的格子覆盖区间,[-1, 1]为再横向移动一单位时的偏移增量。 三、行移动量为2时,覆盖纵坐标偏移[-1, 1]的区域。

在计算偏移区域时,使用了上述提到的计算两格子之间距离的方法。

3、添加对战双方

| 目标 向战场中添加战斗单位,完成简单的战斗循环,看起来的样子是: 一、战场中的对战双方轮流行动,可进行移动、攻击; 二、攻击将对敌人形成伤害; 三、没有生命值的战斗单位会被从战场中移除; 四、当一方被所有消灭时,战斗结束。

实现后的效果以下图:

在这里插入图片描述
| 准备工做 在开始以前,咱们先作一些准备工做。

显示格子坐标 为格子添加Text Mesh Pro组件以显示格子坐标,方便调试。

在这里插入图片描述
增长地图功能:放置出生点 我不想看到战斗单位在刚进入战场的时候是随机摆放位置的,所以我须要为它们提供一些出生点。这样当战斗单位初入战场时,会向战斗地图请求一个本方可用的出生点,若是请求成功则加入战场,并设定在那个位置上;若是请求失败则不会进入战场,避免出现乱占位置的状况。
在这里插入图片描述
想象中出生点的位置,最上、最下排奇数位置放置出生点
在这里插入图片描述
实际生成的状况(绿色为出生点)

增长地图功能:寻找最近可用格子 指定一个起点和一个终点,返回一个环绕终点的距离起点最近、且可用的格子。这主要是为了战斗单位在肯定攻击目标后,须要选择一个它身边的格子做为移动的目标格子(目前假定全部战斗单位的攻击半径都为1)。

这里选择了一种比较偷懒的方法,就是将导航位置直接设定在目标单位的身上,若是导航成功,则将到达终点的前一个格子做为目标格子,这样不只肯定了目标格子,同时还将导航路径一并算出。

在这里插入图片描述
点击起点和终点进行测试(红:起点,蓝:终点,灰:障碍,青色:目标格子)

准备工做到此为止,下面开始加入战斗单位。

| 添加战斗单位 由于是六边形瓦片地图组成的战棋游戏,所以我将战斗单位也表示成六边形,目前来看二者的Prefab并无什么差异,经过设置Order值来确保战斗单位显示在地图格子的上方。

在这里插入图片描述
为了更好的区分战场双方战斗单位的差别,咱们给它们设置不一样的颜色。
在这里插入图片描述
虽然从显示方式来看,战斗单位与地图格子并无什么差异,但是若是从数据角度出发,二者的差异可就大了。为了更好的介绍战斗单位,让咱们从上至下来梳理一下整个战场与战斗系统吧。

| 战场与战斗信息 一个战场就是一场完整的战斗。每个战场目前都包含三大部分:战场地图、对战双方以及战斗过程。

战场地图 地图在以前的文章中已经作了说明,这里再也不赘述。

对战双方 对战双方的单位是战斗组,这里用战斗组编号区分各组,而不是仅用两个枚举来简单表示,是考虑到有不少组同时存在且同时对战的状况。

真正发生战斗的被称为战斗单位,每一个战斗组由若干战斗单位组成。战斗数据、战斗组与战斗单位之间的关系以下图。

在这里插入图片描述
战斗过程 战斗打响时,从两个战斗组进入战场,到双方轮流移动、攻击,最终分出胜负,发生的一切事情,都是战斗过程,这个后面会详细说明。

| 战斗的流程 战斗流程包含了战斗的核心逻辑,是战斗能正常进行且完成的规则,咱们用下图来描述一场战斗的基本流程。

在这里插入图片描述
| 将数据与显示分离 这里仍是采用了将数据与显示分离的处理方法,先看一张数据处理的流程图吧。
在这里插入图片描述
图中很关键的一个内容是战斗过程数据,上面说起它实际上是包含了自战斗单位进入战场,到战斗最终完结之间的全部过程数据。

其实,当开始一场自动战斗时,战斗计算器会瞬时计算完整场战斗的过程及结果,但这些结果只是数据,并无呈现给玩家。

当咱们须要把这场战斗呈现出来时,把这份数据传递给一个对应的显示(播放)器便可。就好像后端和前端的分工同样,一个负责产生数据,一个负责将数据呈现。

在这里插入图片描述
数据与对应的显示器
在这里插入图片描述
战斗数据的显示器
在这里插入图片描述
地图格子显示器
在这里插入图片描述
战斗单位显示器

| 顺序分步呈现数据 我这里使用协同函数(Coroutine)的嵌套来分步呈现战斗过程。

在这里插入图片描述
战场显示器开启逐步呈现战斗过程(战斗单位的行动)
在这里插入图片描述
战斗单位显示器根据本身的动做数据呈现具体动做,如:
在这里插入图片描述
进入战场
在这里插入图片描述
选择目标并移动(青色框:发动攻击方,黄色框:攻击方的目标)
在这里插入图片描述
选择目标并攻击(青色框:发动攻击方,黄色框:被攻击方)

| 分离的意义 走吧,走吧,人总要学着本身长大。

人是这样,数据也是。

其实直接使用一个继承与MonoBehaviour的脚本,把各类须要的数据都装在里面,直接挂在Prefab上,而后用一个控制器一边算一边呈现给玩家,实现起来很是容易。

可是,考虑到后台可能有多场战斗同时在进行;且后期能够在短期内进行多场战斗、收集数据来作战斗数值平衡。将数据分离,让数据能够自行计算,就变得十分重要了。

在这里插入图片描述
20x20地图下,10 vs 10的千场战斗结果计算,能够在很短的时间内完成

4、加入玩家控制

| 目标 分配一个角色给玩家手动操做,每回合玩家有一次行动机会,行动的规则是:可从移动、攻击、待命中选择一次操做,当玩家选择移动后,还可选择一次攻击或待命的操做;玩家也能够不进行移动直接选择攻击或待命,这也会结束当前行动回合。

实现后的效果以下图:

在这里插入图片描述
此次要作的很是少,由于以前已经写好了战斗逻辑,只须要把手动操做加入便可。

| 准备工做 首先,为战斗单位设置一个是否为手动操做的标记。

在这里插入图片描述

其次,为战斗单位设定一个手动操做状态,标记这个战斗单位是否能够移动、攻击。

在这里插入图片描述
最后,新增一种新的战斗单位操做状态和动做,分别是等待玩家操做状态及等待手动操做的动做。
在这里插入图片描述
在这里插入图片描述
当轮到玩家操做时,战斗的单位会显示绿色边框。

| 手动操做的逻辑 大概思路是:战场维护一个战斗单位的行动队列,每当轮到一个单位行动时,若是这个单位是自动操做的,那么它会本身决定目标,移动并攻击,上回咱们将数据与渲染进行了分离,所以它只是产生了一次行动数据(Action)。 若是这个单位是手动操做的,那它会产生一个等待手动输入的行动数据,同时返回等待玩家操做状态。

当战场发现本身收到了一个等待玩家操做的状态,就明白其实刚才的家伙并无生成任何有意义的战斗数据,所以它必须通知本身的渲染器(那个用来显示的模块,上回咱们介绍过):嘿,帮我问问他到底想干吗?

渲染器经过UI与玩家进行交互,得到玩家要移动、攻击或是待命等“有用”的战斗信息后,再回头通知战场“这个家伙已经行动过了”,战场再让下一个单位行动。

在这里插入图片描述
简易的行动流程图

因为下一回咱们才会加入战场UI,这里咱们先用Unity自带的GUI代替。

在这里插入图片描述
用GUI显示一个列表,接收玩家的操做

5、添加经常使用的界面

| 目标 使用Unity自带的UGUI替换以前的GUI来实现一些经常使用界面: 一、包含开始战斗按钮及战斗结束时提示文字的主界面。 二、玩家手动操做时,辅助选择移动、攻击及待命的弹出面板。 三、点选地图、战斗单位时,弹出的详情展现面板。

实现后的效果以下图:

在这里插入图片描述
战斗结束时显示的友情提醒
在这里插入图片描述
行动选择菜单
在这里插入图片描述
战场单位信息面板

不难看出,当UGUI碰撞上专业的素材后,一个个绝美的界面瞬间跃然屏上。这也给了那些常说“程序员不懂美”的家伙们一记响亮的耳光。

| 关于如何制做界面 这些界面都是用UGUI制做的,并无什么难度,相信上手过UGUI或NGUI的同窗,只要碰到精美的纹理贴图,都能轻松完成。

在这里插入图片描述
使用的是一套高雅灰主题的专业UI纹理素材 本文不会对UGUI的使用作详细介绍,咱们将重点聊聊对界面的管理。 由于只要找到了方法,作出漂亮的界面就只是时间问题罢了。 而至于证实“程序员也能凭本身的能力做出专业界面”这件事,相信上面已经作到了。

受项目大小及时间所限,这里会使用一套轻量级的界面管理方式。而在此以前,让咱们先作一些前期准备工做。

| 分配相机 为界面的绘制单独分配一个相机(界面相机),并调整界面相机和战场相机上的Clear Flags、CullingMask和Depth设置。

在这里插入图片描述

界面相机与战场相机的设置

整个界面系统的根画布(ScreenCanvas),是一个类型为ScreenSpace-Camera的Canvas,它的父节点ScreenUIRoot上有专门渲染界面的界面相机。

| 设置层级 ScreenCanvas下设5个节点,分别对应5个层级:背景层、基础层、弹出层、顶层和Debug层.

在这里插入图片描述

界面的层级结构

各层的功能为: 背景层:装饰性的、非功能性的界面。 基础层:常驻的界面(主界面、角色头像、快捷操做栏等)。 弹出层:点击后弹出的界面(各功能界面)。 顶层:强制显示在最上层的界面(Tips界面或走马灯等)。 Debug层:开发时辅助调试用。

这些层级从下到上放置,遮挡关系是上层遮挡下层。固然,其顺序、层数和名称可根据实际需求进行调整。

须要注意的是,这5个节点(Transform)并不是是必须的,它们只是为了Debug时能更直观的查看层级间的关系,真正用于用于区分层级的是Canvas上的SortingLayerOrderInLayer属性。

在这里插入图片描述
SortingLayers设置中的层级与对应的节点
在这里插入图片描述
不一样界面上Canvas所设置的Layer和Order值

| 界面管理流程

比起代码,我以为仍是看图来的更直观些。

在这里插入图片描述

界面管理流程

整个界面管理能够简单拆解为四个部分:打开界面、关闭界面、层级刷新及界面刷新,下面咱们依次介绍。

| 打开界面

在这里插入图片描述
打开界面的逻辑很简单,但须要注意的是,为了更好的使用内存,界面管理器维护了两个界面缓存区:常驻缓存临时缓存。打开界面时若是须要加载新的界面,先去这两个缓存区中查看一下是否有缓存过的界面,从缓存区加载比从新读取新的界面效率要高。

| 关闭界面

在这里插入图片描述
当咱们要关闭界面时,界面管理器会根据它的“存储策略”决定其被关闭后的去留,有些会被放入到常驻缓存区,有些会被放到临时缓存区,而有些则会被直接移除。

这就比如两性交往中女生犯错一般当时就会被原谅;男生犯错一般须要好好表现一段时间才有可能被原谅;而单身狗连犯错的机会都没有。

| 层级刷新

在这里插入图片描述
上面介绍了打开和关闭界面。其实它们均可以被当作是在“显示界面”。由于关闭界面也能够理解为“被这个界面遮挡覆盖的界面,可能须要显示了”。

所以,当有界面被打开或关闭后,界面管理器会从上到下的让各层刷新本身的显示状态及对屏幕的遮挡状态,并将这个遮挡状态向下传递,用做后面层级的显示判断。

| 界面刷新 咱们知道界面的刷新和显示是有代价的,由于它们会对CPU及GPU的性能形成开销。所以我为每一个界面设置了是否遮挡了屏幕、不可见时是否仍然刷新及Dirty属性

若是一个界面遮挡了屏幕,那么它下面的界面首先应该被“隐藏”以减少渲染的压力;其次若是不可见的界面没有被设置为“不可见时仍然刷新”,则当须要刷新它时(界面数据发生了变化),也只是被打上Dirty标记,并在下次须要显示的时候再刷

| 界面的生命周期函数 上面流程图中的蓝色部分,是界面的生命周期函数,它们会在适当的时间被界面管理器、层级或界面自身调用。 Init:初始化,界面首次被加载后调用。 OnPush:界面被显示前,加入层级时被调用。 OnShow:界面被显示时调用。 UpdateView:界面须要被刷新时调用。 OnHide:界面被隐藏时调用。 OnPopup:界面被关闭,从层级中移除时调用。 OnExit:界面不须要被缓存,被Destroy前调用。

在这里插入图片描述
一次完整的界面打开、刷新、关闭过程

| 界面的存储策略 界面被关闭后会根据预设的存储策略决定去留,这里定义的存储策略有如下三种。

自动移除:不多用到的界面,每次关闭时会被直接Destroy掉。

在这里插入图片描述

自动移除策略

临时缓存区:较为经常使用的界面,在关闭时咱们把它放入一个有深度设置的缓存区,这个缓存区当收入一个新的界面时,会判断缓存量是否已超过预设深度;若是超过了预设深度,会将最先缓存的界面弹出并Destroy掉。

在这里插入图片描述

临时缓存区策略(深度为2)

常驻缓存区:须要频繁开关的界面,在关闭时咱们会把它们放入一个没有深度设置(缓存个数限制)的缓存区。

在这里插入图片描述

常驻缓存区策略(仅惟一存在界面可被设置为常驻)

| 界面配置 我使用ScriptableObject对象做为界面配置的载体。每次建立新的界面时,须要一样建立一个配置对象,并将二者进行关联。界面自身及使用者能够经过读取这个对象获取界面的配置信息

在这里插入图片描述

界面配置文件的属性构成

固然咱们能够稍微修改Editor,添加一些辅助工具帮助咱们快速生成界面配置文件。

在这里插入图片描述
一个简单的配置文件刷新器

6、为战斗单位添加血条,加入伤害文字特效

| 目标 为战斗单位添加血条,加入伤害文字特效。

实现后的效果以下图:

在这里插入图片描述
极其精致的血条
在这里插入图片描述
与3A游戏同款的伤害文字特效 | 添加血条 添加一个简单的血条,主要由三部分构成:红色的底,绿色的条和生命值。

偷个懒,直接使用SpriteRenderer + TextMesh Pro来完成它。

在这里插入图片描述
一个简单的血条

每次生命值变化时须要更新两个东西:绿条的长度和生命值。

更新文字很容易,直接设置便可;更新绿条的长度呢?只要调整它在x方向上的缩放比例就好了。

在这里插入图片描述
经过Scale X来控制绿条的长度

可是须要注意将Sprite的锚点设置为Left,这样仅调整x方向的缩放比例就能达到目的;若是锚点为Center的话,还须要同时调整Position X。

在这里插入图片描述
血条所用图片的导入设置

| 添加伤害文字特效 好吧,再偷个懒,直接用一个TextMesh Pro配合一个Animator便可...

在这里插入图片描述
一个简单的伤害文字特效

作法很简单,将文字的动画直接作在一个Animation Clip中,而后用Animator Controller控制播放它们就好了。

在这里插入图片描述
录制动画

在这里我作了两个动画:一个普通伤害的动画,和一个暴击伤害的动画(后面会用到)。固然,它们之间的显示效果差异很是大。

在这里插入图片描述
普通伤害文字
在这里插入图片描述
暴击伤害文字

后面就很是简单了,在Animator面板中设置动画播放规则,好比经过不一样的Trigger来播放普通伤害特效或暴击伤害特效,而后再在代码中根据伤害类型设置对应的Trigger便可。

在这里插入图片描述
用Trigger来区分这个Animator应该播放哪一个动画
在这里插入图片描述
播放动画

7、扩展做战单位

丰富战斗元素,加入并实现手动释放不一样类型的技能。 | 目标 加入一些常见、简单的技能类型,如:

一、单体远程 对远程一个敌方单位进行攻击。

在这里插入图片描述
远程攻击单体目标

二、单体远程带范围效果 对远程一个敌方单位进行攻击,同时对其周围必定距离内全部敌方单位形成相同伤害。

在这里插入图片描述
单体远程带范围效果

三、以自身为中心的范围技能 以自身为中心,对周围必定距离内全部敌方单位形成伤害。

在这里插入图片描述
以自身为中心的范围技能

四、远程指定范围的技能 远程指定攻击必定范围内的全部敌方单位。

在这里插入图片描述
远程指定范围的技能

五、单体恢复 恢复本身或一个友方单位的HP值。

在这里插入图片描述
单体恢复

须要提早声明的是,本文主要记录的是在手动释放技能时,操做展现上的一些关键事项;技能计算的逻辑请见代码;AI释放不一样类型技能也将放在下回。

| 增长效果显示 我为地块、战斗单位设置了不一样的显示状态以便更直观的获取操做反馈。

在这里插入图片描述
战斗单位、地块的显示状态

不管是地块仍是战斗单位,都是经过简单的状态机来实现不一样显示效果的切换。

| 地块的效果显示 因为地块会包含多种状态共存的状况,好比上面的远程范围攻击:某些地块会被同时设置为技能释放范围和技能效果覆盖范围。

在这里插入图片描述
部分地块既在技能释放范围内,又在技能效果范围内

为了解决这种状况,地块的显示状态判断使用了位运算。

在这里插入图片描述
使用位运算来控制显示状态

| 战斗单位的效果显示 战斗单位不涉及多种状态同时存在的状况,处理起来就简单多了。

在这里插入图片描述
战斗单位的显示状态

| 技能信息 与以前的界面配置同样,我仍然使用ScriptableObject做为技能信息的载体,由于这样实现起来最快捷。

在这里插入图片描述
记录技能信息的ScriptableObject

因为目前包含的技能类型较少,数值计算也十分简单,所以只须要少许的属性就足够了,这里就再也不赘述了。

| 手选技能的操做规则 其实,这些技能的计算逻辑并不复杂,麻烦一些的是不一样类型技能在手动操做时的规则及显示逻辑。 我在这里制定了简单的操做规则:

一、对于单体近战、单体远程、单体恢复技能: 选择技能后显示技能释放范围,标出范围内外的单位;点击可选单位则释放技能。

在这里插入图片描述
对单体目标技能的操做

二、对于单体远程带范围效果的技能: 选择技能后显示技能释放范围,标出范围内外的单位;点击可选单位后展现技能效果范围,标出范围内的单位;再次点击该单位后释放技能。

在这里插入图片描述
对单体远程带范围效果技能的操做

三、对于以自身为中心的范围技能: 选择技能后显示技能效果范围,标出范围内外的单位;点击任意单位后释放技能。

在这里插入图片描述
对以自身为中心的范围技能的操做

四、对于远程指定范围的技能: 选择技能后显示技能释放范围;点击范围内任意地块,显示技能效果范围,标出范围内外的单位;再次点击相同地块释放技能。

在这里插入图片描述

对远程指定范围技能的操做

五、操做任意类型技能时,点击鼠标右键为取消。

| 简单的技能分析 为了实现上述操做逻辑,我在点击使用技能后加入了一个简单的技能分析步骤,它会遍历场上全部战斗单位,并根据释放者及技能类型将他们划分为可被选中的、队伍不符的、距离不符的和状态异常的四类,这样后面的操做逻辑实现起来就简单多了。

在这里插入图片描述
点击使用技能后的流程
在这里插入图片描述
技能分析结果

| 增长技能操做界面 我微调了战斗单位的操做面板,为攻击按钮增长了一个选择技能的二级面板。添加了一个一级面板透明处理的小设置来区分层级;以及在不一样屏幕位置点击时的面板弹出位置的优化,以防止面板弹出到屏幕之外没法操做。因为这不是本次介绍的重点,就不在这赘述了。

在这里插入图片描述
技能选择面板

8、加入AI系统(上)

创建超级简单的AI系统。 | 目标 加入一个超级简单的AI系统,会自动释放不一样类型的伤害技能。

在这里插入图片描述

自动释放技能的AI

须要提早说明的是,创建简单的AI系统预计将拆分为三篇更新。

第一篇(本篇)经过加入一些简单的AI逻辑,保证战斗单位能够自动选择(伤害)技能、自动做战,进而顺利的完成一场战斗。

第二篇会进一步丰富AI的决策系统,让它的表现更具期待性,使战斗变得更加有趣。

此外,我邀请了个人好友Aillieo,拜托他按照本身的方式也设计一个AI系统。

所以,我会在第三篇介绍他所设计的AI系统,并对这三篇作一个总体的总结。

| 很是简单的AI系统 我的觉得,有意思的AI系统能够简单的定义为:

让人以为符合逻辑,却又在必定程度上超出了预期。

如何实现一个很是简单的AI系统呢?为了让问题变得再简单些,我将AI的行为拆解成固定的三个步骤: 一、肯定攻击目标; 二、向攻击目标移动; 三、使用技能。

| 肯定攻击目标 将”合理“的目标设定为攻击目标,是件并不太容易的事情。

这里我且不谈那些优秀的游戏是怎么作的,由于我也不知道。只说说我目前所使用的方法:仇恨系统。

在这里插入图片描述

AI使用仇恨列表肯定攻击目标

每当一个战斗单位在战场中被敌人攻击时,他就会偷偷的在本身的小本本里记下攻击者的名字,以及他们的罪行。

当轮到他行动时,他就会掏出本身攥了好久的小本本,按照以前它们揍本身的程度进行降序排列,而后按照这个名单,判断本身反击的可能性。

这里,没有反击的可能性,指的是:若是目标已经被人包围,本身却又是一个近战角色没法靠近,那他就会嘟囔着“哼饶你一条狗命”,而后继续看下一我的。

直到肯定这个家伙能够被本身攻击到,他就会合上小本本,把他的名字刻上本身的心头,而后准备开始下一个步骤:向他移动。

| 向目标单位移动 向目标移动就很简单了,经过A-Star算法找到移动路径后,行动便可。

在这里插入图片描述
肯定目标后沿路径移动

可是这里有一个小问题:应该选择哪一个格子做为移动的终点呢?

特别是当攻击者是某些远程攻击单位,好比游戏中常见的魔法师或者弓箭手,每次都走到目标旁边去攻击,感受上就有点像“送外卖”。

其实解决方法也很简单,在导航时仍然选择目标所在位置作为导航终点,但在距离终点必定距离时,中止导航并返回导航路径便可。这个中止距离,就是远程攻击单位的射程,或者手动设定的某个值。

在这里插入图片描述

射程为2的小红,导航中止在距离小蓝两个单位的格子上

这与“真正的爱情,能跨越一切障碍”是一个道理。

固然,若是这我的儿并不在天边,而在触手可及的地方,那他根本就不用移动,直接进入下面的环节吧。

| 对目标使用技能 光说,不练,假把式。

好容易走到了他(她)的身边,总得有所表示吧?

试想一个场景:你很喜欢一个女孩儿,在表白的关键时刻,你有一百种表达方法,但你却只能选择一种,究竟哪一种才是最有效的呢?

若是是真实的生活,答案很简单:看运气。

可是游戏则不一样,你能够用S/L大法(存、读档大法)来不断重试,直到找出效果最好的那一种!

决策将要使用的技能也能够是同样的。

这里我且不谈那些优秀的游戏是怎么作的,由于我也不知道。只说说我目前所使用的方法:简单的计算全部可用技能的释放回报。

在这里插入图片描述
计算技能得分并肯定所使用的技能

计算技能释放得分的公式异常复杂,因为这并非一篇学术性论文,所以这里不作详细的解释和说明,只把公式列出便可:

技能释放得分 = 技能形成的总伤害 ÷ 技能消耗的能量值

也就众所周知的:

从零点五开始用Unity作半个2D战棋小游戏(八)

天啊,好麻烦。

可是,在获得了按照释放得分降序排列的可用技能列表后,带着何种的心情、用着怎样的姿式、使用哪一个技能的问题,就变得十分容易了。

可能咱们只须要注意下远程范围技能的释放点选择问题便可。

从零点五开始用Unity作半个2D战棋小游戏(八)
释放影响半径为2的远程范围技能

对于远程范围技能,咱们固然可使用一些方法,找到覆盖最多目标的释放点。

但为了省事儿,我这里是这么处理的:当目标超过技能释放距离时,尝试找到释放技能时,能够覆盖到目标单位的点,而后从这里随便选一个便可。固然,若是目标自己就在技能释放半径内,就选它为释放中心了。

从零点五开始用Unity作半个2D战棋小游戏(八)
红色区域为覆盖半径为2的技能在释放时,能够伤害到蓝色格子的释放点

| 能量值 固然,为了帮助AI计算出哪一个技能的释放得分更高,我为每一个战斗单位都增长了一个能量值的属性(你也能够认为它是魔法值);为每一个技能增长了释放的能量消耗;同时还为游戏增长了每次行动时恢复10个单位能量的设定。可是因为这些逻辑都很简单,这里就不赘述了。

最后,咱们来回顾下整个行动流程吧: 一、打谁; 二、去哪打; 三、怎么打。

从零点五开始用Unity作半个2D战棋小游戏(八)
完整的AI行动流程

| 写在最后 至此,创建超级简单的AI系统篇就介绍到这了。如你所见,这里只是实现了很是简单的AI行动逻辑,并无体现出各类类型AI的不一样,咱们下期将尝试着解决这个问题。

相关文章
相关标签/搜索