我的第一个合作项目——
开始
我们将参加4月的Ludum dare,这是一个为期3天的比赛。为了更好的完成,我们决定做一个小项目熟悉一下。我们的美术小祁提供了她的想法——我们将开发一个女巫召唤怪兽攻打冒险者的游戏!哈哈,听起来很有意思。这是一个塔防+小丑牌类型的游戏。
开发日记
3月7日
今晚,我们确定了游戏主题和具体玩法,不过实现小祁的滑动切换屏幕的方法我一时间没想出办法,目前我只做过按按钮切换场景的游戏,不过,策划Andy给出了很好的建议:可以通过在场景上设置三个摄像机来避免加载问题,是个很好的想法!有点迫不及待的想试试!
3月8日
今天,我实现了滑动游戏!或者说,避免加载场景的游戏?具体实现是通过设置三个摄像机,通过A/D键切换摄像机(改变摄像机的索引,设置显隐)。ヽ( ̄▽ ̄)ノ
我们还有一个设计是可以将第二个场景的卡牌拖入第三个场景,并且可以在某一范围内固定。
最开始我的小猪脑转了,想的是使用DontDestoryOnLoad()这个方法,结果试着运行很多次也失败!原来是我本来的思路就是控制摄像机显隐,没有加载新场景!T ^ T 于是,我想了新的办法:
- 按下鼠标 + 鼠标悬停在图片碰撞体上 -> 认为正在拖拽
- 抬起鼠标 -> 认为不在拖拽
- 如果在放置点附近 -> 固定图片
3月9日
我实现了昨天的想法!准备做塔防寻路的具体逻辑~
3月10日
今天,发生了一件让人悲伤的事。。。我理解错了策划的文档!我把第一个要实现的任务点理解错了,沟通后我才发现 T ^ T,今天又和策划交流了一下,确定了接下来要开发的具体内容:做塔防的具体逻辑!!!剩下的内容不急着实现。这件事情告诉我们,开发时要先考虑主要功能,再实现其他内容。
另外,今天我也在塔防游戏的制作方面有所收获!
- 路标数组(用
GUI里的调试画线方法辅助,这个后面写一篇文章记录) - 敌人逻辑
- 寻路:在
Update中朝目标点移动,到达目标点后更新目标点
- 寻路:在
- 角色随机生成
- 角色池的使用:创建角色 ->
SetActive(false)-> 添加进角色池列表。然后再通过一个public方法返回第一个未激活的角色,在随机生成的脚本中,SetActive(true)
- 角色池的使用:创建角色 ->
使用角色池的好处是:避免了频繁生成并销毁角色,减少了GC,提高了游戏性能。
3月11日
今天主要学习了用ScriptObject脚本来配置数据,用这个脚本配置数据的好处是:在Inspector窗口可视,避免硬编码。
下面是详细的实现步骤:
- 创建一个C#脚本,继承
ScriptableObject脚本,如下:
1 | using UnityEngine; |
这样操作后,下一次在Project窗口右键创建时就可以在Scriptable Objects/EnemyData路径下创建一个叫EnemyData的文件了。
另外,我还学到了一个修改同名字段的方法:选中后按住Ctrl + D(修改几次就按几次)
3月16日
前几天不太舒服加上比较忙,没有做太多游戏方面的更新。休息调整了一下,今天赶了一下进度。
目前为止,我已经完成了以下功能:
- 敌人寻路
- 塔的预制体制作
- 敌人血条
敌人寻路在以上内容已经提及,现在先来记录一下塔的预制体制作相关。
在塔的逻辑中,如何检测敌人进入塔的射击范围了呢?我们可以依照之前的思路,先给“塔”这个对象用ScriptObject脚本来配置数据。然后用碰撞器表示攻击范围(可以用OnDrawGizmos辅助查看),进入攻击范围的敌人,我们就把他加入攻击范围的列表,然后处理攻击逻辑。
处理攻击逻辑自然需要一个抛射物,这个抛射物也可以做成池对象来提升性能,在射击时间段中改变抛射物的位置即可。
再回来处理攻击逻辑,只要这个攻击范围的列表长度大于0,我们就需要生成一个抛射物,调用抛射物中的攻击方法。
最后,别忘了将被攻击掉的敌人加入已被移出的列表!方便切换处理下一波。
血条制作方面不多叙述,就是血条的Transform里面的Vector3.x乘以一个当前生命值与初始生命值的比,在Update()函数中不断更新就可以。
3月22日
在放置我方防御塔的方面,我本来想的是可能需要瓦片地图来实现(?不过这样还得和美术那边商量一下,不过,好消息是,我们组中又招了新的成员,其中,阿然老师比我更有经验,于是我决定去问问他。
他给我提供了一个思路:可以将整个画面定义为一张网格,通过程序设定好哪些地方(坐标)可以放置,这是个很好的注意,于是,我开始实现。
我了解到,这其实是一个经典写法,核心思路是将「世界坐标」映射到「格子坐标」。整个过程分为三步:
- 把“世界坐标”换成“格子坐标”
- 用一个结构记录哪些格子允许建造 / 已经被占用
- 鼠标移动时吸附到最近格子,点击时检查是否合法再放下建筑
首先来看把世界坐标转为格子坐标,我们需要建一个2D网格系统:
1 | using UnityEngine; |
FloorToInt让[0,1)都属于同一个格子,更符合“瓷砖”的直觉。+0.5f是把建筑放在格子中心,也可以改成左下角等。
我们分别讲讲这两个方法,首先来看世界坐标 -> 网格坐标:
首先,我们要知道,格子x = n的范围是[n * cellSize, (n + 1) * cellSize)
然后以(1.8, 2.5)为例,他就落在x = 1, y = 2的范围。
再来看网格坐标 -> 世界坐标:
我们最终得到的都是格子的正中心,返回Vector3是因为Unity中世界坐标默认是3D的,返回Vector3更实用;定位到格子中心是因为通常这个格子中心会作为锚点。
3月30日
最近想实现这样一个功能:拖拽卡牌时可以预览我将要放置的预制体。关于这个功能有一个问题值得思考:预览用的Prefab需要单独制作吗?关于这个问题的答案是:如果预览时的外观与实际需要的一样,功能一样或者更少的情况,就可以不用新做一个预览的Prefab。
另外,我还学到了一个新的小知识:当我们启用过滤器时,他会自动忽略trigger的检测,要想检测trigger需要像下面这样设置:
1 | var filter = new ContactFilter2D(); |
3月31日
今天我尝试将我的界面和阿然老师负责的第二个界面结合起来,通过阅读他的代码,其中关于垃圾桶的部分,我学到了UGUI中的事件接口:
IDropHandler:当物体扔进来的时候。打比方是扔垃圾进垃圾桶时触发IEndDragHandler:当手松开时触发。





