从算法到冒险
Source: Dev.to

在我之前的文章中,我分享了我如何爱上迷宫算法的故事。我写了把 Jamis Buck 的 Ruby 代码移植到 Swift、构建可视化引擎以及理解“完美”迷宫带来的智力乐趣。
我以一个强大的 MazeGenerator 框架和一个技术演示结束了那个项目。我感觉已经完成了 90% 的工作。只需要再给它套上一层 UI,添加一个“开始”按钮,我就有一个游戏了。
我错了。我拥有的是模拟,而不是游戏。把前者变成后者的过程让我经历了糟糕的设计决策、严厉的反馈以及对“乐趣”认知的彻底转变。
“书呆子”陷阱 (v1.0)
在构建游戏之前先搭建游戏引擎时,你往往会重视错误的东西。我痴迷于算法。我想让玩家感受到 Recursive Backtracker(长而蜿蜒的河流)和 Prim’s Algorithm(短而有机的分支)之间的差别。
我的第一个游戏版本本质上就是我的技术演示,只是加了一些控制。
核心循环: 选择一种算法,生成迷宫,解开它,并尽量不要碰到墙壁,否则会被扣分。
“酷炫”功能: 一个“绘制模式”,玩家可以用手指画出一个形状,框架会在其周围生成迷宫。
教育角度: 信息屏幕解释“对角线偏差”和“生成树”。
我以为这很棒。实际上并不是。它很无聊。
现实检验
如果你自己都不玩自己的应用或游戏,就无法说服别人使用或玩它。我把游戏装到手机上,尝试用它来代替 Balatro 或 Mini Motorways。结果连一个小时都坚持不下来。没有任何东西能让我保持兴趣,也没有动力去争取更高分。我感到无聊。
而且,“绘制模式”呢?事实证明,用手指在手机屏幕上画掩码会得到一个块状、丑陋的形状。它是一个技术奇迹,却带来了糟糕的用户体验。
寻找乐趣
我已经开发了三个月。代码库里充斥着没人关心的功能(算法选择器、学习标签、地图编辑器)。我必须做出选择:发布一个没人会玩的技术演示,还是去掉书呆子特性,让游戏变得有趣。
我选择了后者。
我去掉了算法选择器,隐藏了教育内容,删除了绘制界面。
移动次数限制
突破出现在我停止因撞墙而惩罚玩家,并引入了基于移动次数的系统后。
在新版本中,玩家获得一个严格的移动次数预算来解开迷宫。
- 效率重要: 不能随意徘徊,需要规划路线。
- 公平有保障: 对于任意特定的迷宫种子,最优路径的步数是固定的。我可以使用 Dijkstra 算法数学计算出“完美分数”。
- 紧张感: 用光移动次数比计时器倒计时更让人感到压力(好压力)。
突然之间,它不再是赛车游戏,而是策略游戏。
构建“游戏”部分
一旦拥有了核心机制,我就需要把它包装进一个进度系统。游戏需要像一次旅程,而不是一个循环。
我加入了:
- 道具: 由于移动次数是关键,道具变成了经济类。 “拆墙”需要花金币购买,但可以帮助你在移动次数耗尽前到达隐藏的箱子并返回。 “跳跃”可以让你跳过长走廊,并把剩余的移动次数兑现。
- 地形: 我加入了冰面(滑动,节省移动次数但失去控制)和泥地(消耗双倍移动次数)。
- 进度: 我加入了逐渐变难的关卡——迷宫变大,箱子离最佳路径更远,地形更崎岖。每十关会奖励一次奖品。偶尔会解锁稀有头像或发现隐藏的宝石。
技术栈:SwiftUI
我用 SwiftUI 完成了整个游戏。
很多开发者劝我不要这么做,建议使用 SpriteKit 或 Unity。但对于基于网格的益智游戏,SwiftUI 出奇地足够强大。
- 状态管理: 网格由
VStack和HStack组成,根据游戏状态渲染。 - 动画: SwiftUI 的隐式动画让玩家在格子之间滑动时非常流畅。
- 迭代速度: 我只需在一个文件中修改 “泥地” 格子的视图,预览中整个游戏即可即时更新。
最难的部分是把“物品”和玩家层放在迷宫层之上。我最终使用了复杂的 ZStack 层叠结构,这种写法不推荐给胆小的开发者,但它确实起作用了。
上线
游戏 MZ: Maze Adventures 已经完成。
游戏中所有可爱的头像都是我妻子设计的;不那么可爱的则是我自己画的。音乐由我儿子作曲(他也决定下载游戏来玩,而不仅仅是听他自己的音乐)。它没有算法选择器,没有“绘制模式”,也没有教育讲座。
它只有迷宫。完美、令人沮丧、优美的迷宫。
你可以现在在 App Store 下载。
