42

从零点五开始用Unity做半个2D战棋小游戏(三)-腾讯游戏学院

 5 years ago
source link: http://gad.qq.com/article/detail/288913
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
好久不见。

这是第19篇与游戏开发有关的文章。

| 写在最前
这次想要一个简单且传统的战棋小游戏,大概的玩法是:在2D世界里创建一张由六边形地块组成的战斗地图,敌我双方依据体力在地图上轮流行动并向对方发动攻击,先消灭掉所有敌人的一方将获得胜利。

预计将分为以下几篇(未完成前可能会调整...):

1、创建战场(已完成)
根据预定尺寸生成战场地图,并随机一些障碍物。

2、添加地图功能 (已完成)
实现战场格子点击反馈,地图导航及范围选定。

3、添加对战双方
向战场中添加作战单位,作战单位轮流行动,可进行移动、攻击。

4、加入玩家控制
玩家可控制一个战斗单位,手动选择移动目标及攻击单位。

5、添加战场UI
添加可以随时显示战况的Hud、为作战单位添加血条等。

6、扩展作战单位
丰富作战单位的类型,添加职业,并加入若干不同类型的技能。

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

8、规范战斗配置
可以通过规范化的数据结构配置战场、职业、技能、道具等。

上次的主题是:添加战场地图功能。
本次的主题是:添加对战双方

项目代码会上传至我的Github:https://github.com/elsong823/HalfSLG,有兴趣的同学请自取。


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

实现后的效果如下图:
5c1c57a531d96.gif


| 准备工作
在开始之前,我们先做一些准备工作。

显示格子坐标
为格子添加Text Mesh Pro组件以显示格子坐标,方便调试。
5c1c57c51f4c7.png
添加了坐标显示,调试起来感觉方便多了呢

增加地图功能:放置出生点
我不想看到战斗单位在刚进入战场的时候是随机摆放位置的,因此我需要为它们提供一些出生点。这样当战斗单位初入战场时,会向战斗地图请求一个本方可用的出生点,如果请求成功则加入战场,并设定在那个位置上;如果请求失败则不会进入战场,避免出现乱占位置的情况。

5c1c57d14b7d1.png
想象中出生点的位置,最上、最下排奇数位置放置出生点

5c1c57dd8828f.png
实际生成的情况(绿色为出生点)

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

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

5c1c57ff72e90.gif
点击起点和终点进行测试(红:起点,蓝:终点,灰:障碍,青色:目标格子)

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


| 添加战斗单位
因为是六边形瓦片地图组成的战棋游戏,因此我将战斗单位也表示成六边形,目前来看两者的Prefab并没有什么差别,通过设置Order值来确保战斗单位显示在地图格子的上方
5c1c5815038f4.png

为了更好的区分战场双方战斗单位的差异,我们给它们设置不同的颜色
5c1c58248343a.png

虽然从显示方式来看,战斗单位与地图格子并没有什么差别,可是如果从数据角度出发,两者的差别可就大了。为了更好的介绍战斗单位,让我们从上至下来梳理一下整个战场与战斗系统吧。


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

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

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

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

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

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

5c1c584c3700f.png


| 将数据与显示分离
这里还是采用了将数据显示分离的处理方法,先看一张数据处理的流程图吧。

5c1c585f24655.png
图中很关键的一个内容是战斗过程数据,上面提及它其实是包含了自战斗单位进入战场,到战斗最终完结之间的所有过程数据。

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

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

5c1c5873ef7a3.png
数据与对应的显示器

5c1c58811d039.png
战斗数据的显示器

5c1c589f3e418.png
地图格子显示器

5c1c58920127f.png
战斗单位显示器

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

5c1c58b668ba8.png
战场显示器开启逐步呈现战斗过程(战斗单位的行动)

5c1c58c0ddc3d.png
战斗单位显示器根据自己的动作数据呈现具体动作,如:

5c1c58cddd23d.gif
进入战场

5c1c58e91c37c.gif
选择目标并移动(青色框:发动攻击方,黄色框:攻击方的目标)

5c1c58dcb0475.gif
选择目标并攻击(青色框:发动攻击方,黄色框:被攻击方)

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

人是这样,数据也是。

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

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

5c1c58fc9dd81.gif
20x20地图下,10 vs 10的千场战斗结果计算,可以在很短的时间内完成

| 写在最后
至此,添加对战双方篇就介绍到这里,详细代码可以移步Github下载。

感谢您能读到这里。

愿不忘初心。

下回见。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK