个人独立游戏开发日志 #2 // 2019.07

2019.07.12

这一天我决定暂时中止原定项目的开发(个人项目代号N1),转而投入了我在半年前基本完成策划的但并未投入开发的一个小品级游戏(个人项目代号A2)。

与N1相关的针对性学习时长加上开发时长约十天。十天前我立项的原因在于想要借助此惊艳的想法在八月份的NextIdea大赛上取得较好的成绩。而目前决定暂停开发的原因是:随着开发深入,渐渐发现自己很难在余下三周内完成我所需要的战斗系统优化以及DEMO所需美术资源的制作,我不想让这么好的一个想法因赶工而导致表现粗糙。

而对于现在转而开发A2,有两个主要原因:
一是技术原因,因为在A2的策划之初,我认为自己暂时没有能力去开发所需的功能并呈现出我想要的效果,而如今回顾发现,当初我眼中的各种难关,居然在开发N1的过程中,我就已经学会了处理它们的方式。甚至这十天里的收获有70%可以复用于N1;
二是美术原因,在N1中,我计划采用一种纯粹的传统水墨风格,这种风格在游戏上的运用几乎不曾出现,十分缺少可供学习的案例,所以驾驭起来极其困难。为了它我还泡在父亲的画室里长达一周寻求灵感,但可惜至今没有较好的解决方案,甚至毫无绘制思路。而对于A2,我就打算采用像素风格,而之前我也学习像素画约半个月,对我所需的简易角色绘制有一定的思路。
虽然我并不一定能在比赛上拿到成绩(毕竟民间有很多独立大佬),但我也必须得保证DEMO的品质以防止留下遗憾。所以从这些角度看来,现在转而开发A2会更适合三周后的比赛。

2019.07.14

A2将会是一个基于战棋的底子但富含极多创新元素的roguelite游戏。正如之前说的,独立开发N1为我积攒了一些架构方面等的经验与常用的工具方法等,我相信A2的开发会比较顺利,不过困难也在于:我从未接触过战棋游戏的开发,尤其是其中众多的四边形或六边形网格与玩家操作的交互的编码细节比较繁琐,所以起初我甚至做好了在学习搭建最基本的战棋战斗系统上要花去三天的准备。但经过初步学习后,我发现一种有效的思路——所有地方能暴力遍历就用遍历,虽然浪费计算机资源,但能极大简化编程思路,而且这种每秒不到千次运算的资源浪费对于这个体量的迷你独立游戏以及正常游戏电脑CPU来说是可以接受的。一旦掌握了这个思路,一切就顺畅起来。
历经12晚上的初步学习,13下午加晚上的从零开始搭建工程之后,今天我就初步完成了底层战棋系统的搭建,但操作仅限于玩家的移动、攻击等,尚未完善敌人的AI(但工作量也不大),也搭建好了完备的测试场景适用于未来每个新功能的测试,并添加了一些基础的UI、各类高亮范围指示等,进度的神速使我感到满意。
接下来我将先借助DOTween与先前购买的Sprite Trail插件完善移动的动画显示,并绘制简单的像素画通过Animator实现简单的攻击动画,使移动和攻击显得自然。
随后再搭建Excel数据库,实现正式战斗场景的数据初始化。
首周的开发重心将会放在搭建基础框架与制作最小可玩版本上,将仅包括玩家的移动攻击、关卡与数值系统、完善界面与交互等元素。
如果一周之内能够完成这些,将会是意外之喜。
而本游戏中独有的创新部分,将在下个开发周期添加至系统中,届时只有少数内容需要对框架进行修改,另外一大部分内容则只需在现有的框架上进行方法的重写与扩充即可。

2019.07.15 – 与Bug斗智斗勇 – 睡眠很重要

今天耗费约4小时才仅完成了最基础的敌人AI(只包括移动和攻击),实现原理十分简单,但在移动和攻击两个部分分别遇到了极难排查的BUG。移动部分:主要就是搜寻最近的目标→搜寻离目标最近的点→移动至目标格子三部分,但我发现敌人总是会移动到距离目标两格远的位置。即使移动的AI原理实现很简单,但因其逻辑深度较大,就导致了排查它的bug是一件极其繁琐的事情(十几个子过程逐行排查,几十个Debug.Log看得眼花缭乱),有将近一个小时的时间我沉浸在【它明明知道距离=2的点比距离=3的点要近,为什么就是不知道距离=1的点更近】这样的困扰中。
中途还顺便发现了我没有进一步筛选目标点(从诸多离目标同样最近的点中选择出离自己最近的点),导致移动逻辑异常等等细节问题(喜欢绕远路)。而就在我某一次修改Debug.Log中的内容时,这个【总是会移动到距离目标两格远】的bug却自己消失了,这使我十分困惑。但接下来攻击系统的bug又马上将我的注意力转移走了。
我发现,敌人在攻击完玩家单位后,如果玩家单位扣血至死亡,这个敌人居然不会马上结束回合,反而会转而移动并攻击下一个目标,这在当时让我感到诡异:因为我的代码中,每个敌人最多只会移动并攻击一次。
又经两个小时的排查后发现,是我的【行动数组下标更新】的方法出了问题。
因为整个战斗都是围绕着BattleCtrl中的一个ArrayList units(保存所有单位对象)和一个int indexActing(记录当前行动单位在ArrayList中的的下标)来进行的,以及每个对象自身还会记录自己的下标。所以在每当有新单位生成、摧毁时都应当及时更新一遍和这个数组以及下标有关的数值。
这个【行动数组下标更新】的方法经历了如下几个版本(在单位被摧毁后调用):

  1. 遍历数组,更新他们自身记录的下标。就是因为最初的这个简单版本导致我排查BUG耗费约一个多小时,因为它并没有更新indexActing,从而导致了这种情况:敌人5攻击玩家2,玩家2被摧毁,在units数组中移除玩家2,此时他们的下标进行了一轮更新,原先的敌人5的下标前移而变成了4,但indexActing仍为5,所以导致接下来的结束回合方法,结束的是原先的敌人6即现在的敌人5的回合,这导致其他敌人白白失去了自己的回合而罪魁祸首因未被标记ifActed而又开始了下一个回合。
  2. 在发现bug成因后,我将此方法进行了相应完善:在刷新大家的下标前,临时记录indexActing对应的行动单位对象,在刷新之后再读取此对象中保存的自身下标赋值给indexActing。但这样改动之后居然没有任何效果,在又将近一个小时的【迷惑困扰+应付其他bug】之后,我才明白,当我想临时记录【indexActing对应的行动单位对象】时,因ArrayList已经执行过Remove操作了,所以整个数组的下标早就不同了,此时通过indexActing访问的,并不是真正的行动中的单位对象。
  3. 于是,将【临时记录indexActing对应的行动单位对象】这个操作移动到ArrayList.Remove之前,问题便完美解决,总历时约两个小时。其实还有其他更好的办法,比如:并不需要【临时记录indexActing对应的行动单位对象】,只需在刷新数组时根据他们自身的标志位IfActing来修改indexActing就可以了,这个方法更加简单直接。
    所以其实就是语句次序出了点问题,却使得我白白耗费了一些精力,我认为这主要是由于昨晚闹肚子没睡饱(似乎只有45小时,平时我一般休息810小时),导致我看一堆Debug.Log看得头晕眼花以致于浪费时间,否则会迅速排查出这个问题。
    至此,其作为战棋战斗本身的底层框架已基本完成,接下来只需补上头和尾,便可进行初步的测试。所以,今晚我将主要用来研究在Unity中读取.xls文件的相关用法,以及绘制基本交互UI。
    哦,补充一下,上午我快速学习并简单做了Animation的脚本回调(尤其是前摇结束时使攻击生效)以及打击感优化等(受击单位的两次闪白、顿帧),所以今天午间也并不仅仅是浪费在Debug过程中,略微有些欣慰。
    (unity的动画编辑器真的好强啊啊啊啊啊!动动手指就节省了好多美术和脚本方面的工作量)

2019.07.16 – 从零开始的关卡外地图

今天大概是正式开发以来工程量最大的一天。

一下午加一晚上主要是用于从零开始构建关卡外地图(类似于Slay The Spire那个样子),还为此画了一些简陋的像素图标。不过我还想实现额外的效果,总而言之就是:1.生成道路的随机性极大(甚至允许从第一行的最左侧的房间到第二行的最右侧的房间之间存在道路的这种极端情况);2.在允许出现极端道路的情况下,仍保证每个房间的前后都有道路,都可以从起点开始按某种路线到达;3.不允许存在交叉道路;4.可以自由调整各种参数,包括总层数、每层的最多最少房间数量、房间生成位置的偏移程度、与页面的间距等。
虽然这个算法从构思到进行手稿测试仅花费了我不到一个小时的工夫(我相信我还是比较擅长算法设计的),但将它实装于工程中却耗费了约九个小时(不吃不喝不休息//真·人椅合一),但实现后还是倍感欣慰,因为非常实用,且开发的预期进度被大大加快。

道路的具体样子八月份之后才会再精修,届时应当多段sprite拼接而成,而不是这样简单的一个sprite的拉伸。
可能未来会有玩家说我这个地图结构是抄STS,但一方面是因为我确实认为这种地图类型是最适合我正在开发的游戏的玩法类型的,另一方面则是这样比STS随机性更强的地图其实要花里胡哨很多(虽然没什么用)。
极端情况存在太多似乎十分影响美观,未来会适当调整。

2019.07.19 – bugbugbug!

前两天工作量相对较少,算是一个小憩。昨天简单做了战斗场景中ui的完善,实时显示鼠标指向的单位的数据等、进行了初步的性格系统设计并实装到了角色生成器中,并完善了相应的主界面交互UI
今天首先是将测试版的人物实装到了战斗场景中,并配置了相应的一套基础动画。随后bug出现了:敌人攻击刚抬手时就会出现跳过回合并进入下一个单位的回合的情况,并且自己的回合结束部分功能也不正常。
排查过程略,主要是两个阶段:

  1. 我认为的AI协程就存在问题,原先为了保证AI行动在视觉上的节奏感,在判定准备进入下一步操作后,等待几百毫秒再执行操作,执行操作后却不再等待,马上进行下一次判定。排查过程中我认为是等待操作的次序出了问题(当时认为可能会导致等待后的自身参数或其他条件已经不适合继续执行操作,却仍然执行了),于是改为执行完一个操作后再进行等待,问题部分解决,但未全部解决。
  2. 最终发现问题出在animation的转换上,因为我将原版的测试人物改为新版测试人物实际上只改了一个animator,所以问题也只可能出在animation内部转换相关的部分,而确实新版的animation在转换时没有调整好参数,导致攻击回调混乱,如果早点发现这个逻辑关系就能省去很多时间。而此时我也渐渐明白,协程内部的语句次序是没有问题的,因为在此协程内也仅仅是单线程而已,而敌人AI除此单线程外并无其他操作。所以等待与否、等待语句的位置,都不会影响敌人自身参数的有序变化。

2019.07.22

前几天主要是完善各种UI相关,而这两天开始着手从零开始搭建卡牌系统,昨晚写了一套最基础的卡牌抽取与使用的测试系统,仅完成抽卡,销卡,及其可视化。

今天将其正式装入游戏系统,包括:卡牌数据库数据读取,卡组初始化,战斗系统中的牌库、手牌、墓地的卡牌轮回等等,最后一个系统是核心,也毫无意外地出现了bug:我发现,1.每一次洗牌后,牌库就会变得乱七八糟,比如原本的3张A和3张B会洗成2张A和4张B;2.将一张手牌移入墓地之后,手牌和墓地牌的总和也变得乱七八糟,例如原先是ABBCC,将最后一张C移入墓地后,手牌会变成BBCC,墓地则是C,起初我以为是某一句程序语句写错了,于是在经过一个多小时的排查逐行加Debug.Log之后,才发现实际问题所在:无论我将第几牌送入墓地,手牌每次只会移除第一张。

这个问题是由于我使用了Arraylist.Remove(obj)导致的,举个例子,即使我HandsList.Remove(HandList[5]),他依旧会移除HandList第一张牌。这个问题在我之前这样使用时从未出现。

查询资料后我才得知,Remove(obj)用的是名称匹配,而我在HandList中保存的是一个个的Card类,他们似乎不像GameObject那样有自己的个性名字。所以,即便我Remove(…[5]),依旧会变成移除第一个,因为他们的名字是一样的,这个问题以前从未出现,我大概猜想是因为以前我用ArrayList保存的都是GameObject,所以没有出现这个问题。
进一步测试后,我发现:如果ArrayList中保存的是GameObject,直接对其进行Debug.Log(List[i]),会输出 “物体名字(UnityEngine.GameObject)”,而如果保存的是Card这样的自定义类,Debug.Log(List[i])输出的就会是null,所以一切脉络就更加清晰了。(也难怪我之前判定List[i]==null 一直为true,当时没有好好深究现在果然带来麻烦了)

总之,以后老老实实用ArrayList.RemoveAt(int index);

卡牌系统的开发目前进度喜人,预计明天可以初步完成卡牌的使用与作用相关,届时将形成第一个可玩版本。

后记(2019.07.28):近日发现,继承自Monobehavior但未被挂载的都会被当成null,我的Unity编辑器中唯一的warning也一直是我的Card类继承自Monobehavior。所以,搞明白这一点,一切都理清了。

2019.07.24

昨日将卡牌系统做的差不多,已可以释放最基础的6种单体卡牌,并完善了相关交互。
今天下午用来画了第一个怪物史莱姆及其动画,实装入游戏。
晚上开始着手完善LevelMap的UI,包括浏览队伍成员与浏览当前卡牌等,浏览卡牌做的很顺利,做浏览成员时,发现我旧版的TeamMember[]得改成ArrayList以适应未来的队伍成员可变数组的需求,方便增加与删除队员,所以就先去系统化地做这个更换了。更换之后,战斗又出现了bug:第一个回合的角色的范围指示器总是失效,起初我以为是BattleCtrl中读取新的ArrayList导致的问题,排查后确认毫无关系。随后我发现,我控制范围指示器的函数在被调用时,它在start()里的程序居然还没执行完(还未初始化完毕)!这就导致了第一个回合调用范围指示器的函数失效。这真是从未出现过的情况,以后有机会需要好好钻研导致这个bug出现的根本原因,现在还是先将start()中的内容移至awake()以暂时应对。
后来在SpriteTrail的访问上也出现了同样的问题,解决方法:将不需外部条件的初始化提前至awake,即保证同期出现的其他脚本的start()可对他进行访问

2019.07.28

今天最重大的发现:自定义类对象的数组赋值,是将其下的各个对象的引用地址传递过去,并不是将数组地址传递过去。所以当Buffs[] b2= Buffs[] b1时,如果b1还未初始化,b2则会一直null。后续如果b1初始化了,b2也依旧会是null。

2019.07.29

今天于机缘巧合之下尝试导出工程。先后遇到了两个问题:

  1. 导出后无法读取.xlsx文件。解决方法:将DataBase由静态类改为单例,在编辑器中读取完之后直接将数据保存在此单例中,平时测试时常驻开启读取,需导出时,预先读取一遍,保存下来之后再导出即可(也可以用Asset等其他方法,以后再深入研究)。【当晚更新:改用了Editor重写Inspector的方法,一键读取,方便实用】
  2. 战斗场景的各种范围指示不正常(不显示任何功能的范围),且玩家角色无法进行移动、攻击,也无法释放卡牌,敌人却能正常行动。排查方法:因为没有精力去专门写一个控制台可视化的工具,我便开了个UI.Text专门测试用于管理范围显示的脚本gvc,并对gvc进行逐行可视化测试,排查后发现gvc的wid和height并未成功初始化(为0,这两个数值应该读取自battleCtrl),所以也无法正常遍历网格,这就导致了任何范围指示器都失灵,玩家也无法做出任何操作;而我初始化的内容在Awake()中,但我即便在update中重复调用awake或将gvc的激活次序排到battleCtrl之后均不行(至今让我很费解)。解决方案:将gvc的初始化改至Start()中,并且以防万一,在update中再次检测。【2019.07.30清晨补充:经多次测试,单纯依赖awake来对各个控制器进行初始化是十分不靠谱的,因为这两个脚本中,随时会出现其中一个awake时另一个还未加载完毕的情况。解决方法:awake只用于与自身有关的初始化,随后在start中进行由battleCtrl主持,对所有其他控制器进行需读取battleCtrl数据的初始化,随后再进行战斗初始化】
  3. 第二个问题在编辑器中并未出现,但导出至pc平台后却会出现。猜想是由于加载等方面的问题,以后需要在这部分深入研究。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值