过去7个月里,我完全独自一人开发了一款2D增量六边形拼图Roguelite游戏。

在将它展示给1万多名来自不同WebGL平台的玩家后,我根据试玩反馈对底层游戏系统做出了相当重大的调整。

这款游戏是用Unity构建的,Steam上有免费Demo,如果你想看看这些系统的实际效果:
https://store.steampowered.com/app/4692090/Unstable_Nuclear_Reactor_Demo/

我还想分享一些在Unity方面遇到的问题以及我是如何解决的。

构建六边形网格

整个游戏围绕一个六边形网格展开,原子可以在其中合并成更高级的元素。

我首先想避免的是对每个瓦片的位置进行硬编码。棋盘可以有不同的大小和环形配置,所以我需要一种能够随时改变而不必每次都重写游戏逻辑的方案。

最终,我将实际的网格状态与用于显示它的GameObject分离开来。

事实证明这种做法让很多事情变得更简单:

  • 查找相邻六边形
  • 检查瓦片是否可以放置
  • 检测可能的合并
  • 解决连锁反应
  • 支持不同环形尺寸
  • 创建与特定瓦片互动的工具

这也意味着我可以改变棋盘布局,而无需改动大部分游戏代码。

处理连锁反应

合并系统成了项目中最有趣的部分之一。一次合并可能引发另一次合并,然后接着再引发一次,以此类推。

起初,很容易想到只是递归调用合并逻辑,但一旦涉及大量瓦片,这种做法很快就变得难以理清。于是,我开始将整个过程视为一系列状态变化。

基本流程如下:

  1. 找到有效的合并。
  2. 更新逻辑网格。
  3. 创建或更新生成的原子。
  4. 检查受影响的相邻单元格。
  5. 将任何新的合并加入队列。
  6. 持续进行,直到没有更多有效合并为止。

当我开始处理大型连锁反应时,这种方法效果就好多了。

工具与玩家状态

游戏中有几种改变常规规则的工具,比如移除、交换或修改原子。我遇到的一个问题是如何处理已解锁的工具。最初,玩家一旦解锁了某个工具,它就会自动可用。

一开始听起来还好,但经过试玩后,这降低了游戏的策略性,因为玩家可能同时拥有太多选项。

我最终将“已解锁”和“已装备”分成了两种不同状态。

这样一来,玩家可以永久解锁某个工具,但仍可以决定是否在当前回合中装备它。

这听起来只是一个小改动,但它意味着要将工具的解锁进度状态与其活跃的游戏状态分离开来。

在不移除随机性的前提下修复RNG

试玩反馈中最大的问题之一是,有些游戏回合仅仅因为糟糕的随机性而变得令人沮丧。

我不打算完全移除随机性。不确定性正是Roguelite结构有趣的一部分原因。

相反,我增加了暂存槽,玩家可以在其中临时存储原子或工具。

但这又带来了另一个问题。

暂存物品并非真正的棋盘瓦片,所以我必须确保游戏在物品被存储、使用或丢弃时能正确处理其状态。

最终,这种方法在不完全消除随机性的前提下,给了玩家更多应对不利局面的控制权。

为什么我要将游戏逻辑与视觉效果分离

独自完成所有工作使得这种分离变得更加重要。

当游戏逻辑与GameObject紧密耦合时,改变一个机制很快就会变成要同时改动动画、UI、输入处理、网格逻辑以及一堆其他东西。

将逻辑状态与视觉表现分离开来,使得迭代变得容易得多。

例如,游戏可以知道某个原子从一个逻辑六边形移动到了另一个,即使视觉动画还在播放中。

随着我添加更多的工具、连锁反应、教程、升级和其他系统,这一点变得特别有用。

从超过1万次试玩中我学到了什么

最大的教训大概不是Unity特有的。

作为开发者测试时感觉完全没问题的事情,第一次接触的人可能会觉得困惑或沮丧。最近很多工作其实并不是添加更多内容。

  • 而是减少摩擦。
  • 更好的教程。
  • 更清晰的升级UI。
  • 对工具更强的控制。
  • 不那么令人沮丧的随机性。
  • 更好地处理玩家意外操作。

以及修复那些只有成百上千人开始玩你的游戏时才会出现的奇怪边缘情况。

完全独自工作也促使我构建易于更改的系统,而不仅仅是能运行的系统。

我仍在迭代这个游戏,所以我很想知道其他Unity开发者是如何处理类似问题的,尤其是网格和连锁反应方面。