色彩空间的问题
经过一些测试后,我发现 Flash 在感知空间而不是线性空间中进行 alpha 混合和颜色变换。这在数学上是可疑的,但另一方面我们也该知道,很多绘图程序都是这样工作的,你希望你的消费级工具按照人们期望的方式工作,虽然这对于数学家来说是一种冒犯。但是从根本上来看,这是错误的!它会导致诸如抗锯齿之类的问题。
当你光栅化矢量图形并要求抗锯齿输出时,光栅器将输出 alpha 值,即所谓的「覆盖值」。这意味着如果给定像素被矢量形状半覆盖,则该像素将以 alpha = 0.5 输出。
但在 Flash 中,当某些东西的 alpha 为 0.5 时,这意味着它在感知上处于前景色和背景色之间的中间位置。
这完全不是一回事!
在不透明黑色像素之上绘制的半覆盖白色像素不应是感知的 50% 灰色。这不是光的工作原理,也不是矢量光栅化的工作原理。光栅器不能在不知道背景颜色的情况下说「这个像素应该在背景和前景颜色之间感知 xx%」。
在感知 (sRGB) 空间中完成的混合。顶部:黑色透明白色;中间:白底透明黑色;底部:灰色在线性(物理上准确)空间中完成的相同混合。请注意,50% 的覆盖率看起来与 50% 的灰色不同。
因此,我们的抗锯齿光栅化形状使用一种 alpha 定义,而我们的 Flash 导出的 alpha 透明度、渐变和颜色变换使用另一种定义。但是我们的渲染管道中只有一个 alpha 通道。那么渲染器应该如何解释 alpha 值呢?如果它将它们解释为感知混合因素,则半透明对象看起来是正确的,但一切的抗锯齿边缘看起来都是错误的。如果它将它们解释为覆盖率值,则反之亦然。有些东西总是看起来不对劲!
在此,我认为只有两个严谨的解决方案:1) 设定两个 alpha 通道,一个用于覆盖,一个用于感知混合;2) 在没有 AA 的情况下光栅化所有形状,将所有内容绘制到一个非常大的帧缓冲区,然后通过过滤将其缩小。
我必须承认,这些设想都没有获得实践。这些半透明的东西在 Flash 和游戏中看起来不对劲,我只是逐渐调整图形直到游戏看起来没问题。在 Flash 中的透明对象永远不会完全符合我设计他们的初衷,但它们并不多,这也不是什么大问题。
为了确保其他一切都正确,我制作了一个「颜色测试」图形,其中包含一堆不同强度的颜色、色调旋转效果 10 等等,让游戏显示它,并确保它在 Flash 中运行正确。
变成了比较颜色的问题。
帧率
原始的 Flash 游戏标称帧率是 24FPS,但实际上它们以 Flash Player 想要的任何帧速率运行。使用 Flash,你可能要求 24FPS 并得到 15FPS,或者要求 30FPS 突然得到 24FPS,这看起来一点也不严谨。
我想要把游戏重制成 60FPS,这意味着要在 Hapland 创作时期望以大约 24FPS 的速度播放这一事实动些手脚。Flash 的动画工具基于离散的帧,而不是连续的时间。
我首先让导出器将所有帧加倍,对于每个时间轴帧导出两个帧,这就直接地把 24FPS 提高到了 48FPS,但仍然不是 60,需要的动画仍然要快 25%。解决方法是老式的手工活:完整遍历游戏,然后手动将额外的帧添加到现在看起来太快的动画中。
至此,我们已对 Hapland 游戏进行了相当不错的 C++ 转换,肯定可以在现代计算机上运行至少再过一两年。但我就是无法摆脱应该尝试提供一些额外价值的感觉,所以加新活在所难免。除了重新绘制大量旧图形和动画外,我还进行了一些重大更改。
及时保存
我认为需要让 Hapland 3 不那么让人不知所措。这个游戏的关卡很长,有很多地方死掉了又得重新再来,也许这在 2006 年很有趣,但我们现在是成年人了,我们没有时间这样做。
保存状态是模拟器该有的功能,如果你按下「保存状态」,它会通过将控制台的内存转储到文件中来记录当前游戏的整个状态。然后,如果你搞砸了,按下「加载状态」,你就会回到要重试的地方附近。
在原始 Flash 游戏中实现保存状态是不可行的,因为 Flash 不让程序员访问其整个状态。但由于这次我使用的都是自己的代码,所以这是可能的。
我有一个叫做 Zone 的东西,它只是一个分配器,将其所有内存分配到一个固定大小的块中。所有场景节点都分配在当前区域内。为了实现保存和恢复,我只需要两个区域,活动区域和一个单独的「保存状态区域」。为了保存状态,我将活动区域 memcpy 到保存状态区域。要加载状态,我会以另一种方式返回 memcpy。
重复关卡
Hapland 的游戏时间并不是特别长,虽然一共有三个,但我们总是希望再想多给玩家几个小时的游戏时间。因此我决定给每个游戏一个「Second Quest」—— 原关卡修改版,布局和谜题略有不同。制作这样一个 Second Quest 比制作一个全新的游戏要省力,但仍能带来一些额外的价值。
创建 Second Quest 意味着我需要在大约 15 年来第一次重新开始 Flash 解谜游戏开发,老实说,这感觉不错。复古的 Flash 用户界面很棒,按钮都有边缘,图标是实物风格,空间也得到了充分的利用。
使用旧时代的 UI 让我感觉自己就像一位考古学家,正在发现某种被遗忘的罗马技术。失落的 UI 设计艺术,很整洁。
这是什么魔法?
尽管 Flash 的 bug 很多,速度也慢,还缺少一些极其基本的功能,但我基本上不讨厌使用它,当然使用现代应用程序是更顺手的。
为了防止第二个任务看起来与第一个任务太相似,它们需要有新的背景,整个场景也被水平翻转了。
Hapland 3。
Hapland 3 的 Second Quest。
音乐
在 BGM 方面,我使用自己硬盘里的内容,并额外制作了一些音乐,为每款游戏制作了快速的环境配乐。有一次在日本度假时,我无缘无故地在山顶上进行了一次野外录音,能够将其用于某些事情真是太好了。我从互联网上找到了一位音乐家来做标题屏幕音乐,并自己录制了一些吉他和弦作为片尾字幕,它们淹没在效果中,所以你不能说我吉他学得不好。
在工具上,我根据音乐使用 Logic 或 Live。我发现 Logic 更适合录音,而 Live 更适合声音设计。
成就系统
在 Steam 上,玩家总喜欢看成就,这一点不太好办,成就的设置取决于游戏设计师的思路,但其实也没什么大不了的。
把成就系统上传到 Steam 是一件痛苦的事情,你不能只定义一个列表并将其提供给他们的命令行工具,而是必须费力地点击 Steam 合作伙伴网站缓慢、令人困惑的 PHP 框架,然后将它们一个一个添加进去。
看起来,如果你是一家重要的大型游戏工作室,你就不必忍受这一点,他们为你提供了一个批量上传工具,但我显然不是其中之一。所以我查看了它构建的 HTTP 调用,保存了我的登陆 cookie,并编写了我自己的文件。
几次修改之后,我选择了一组适度的成就:一个用于完成每个 Hapland 游戏,一个用于每个 Second Quest,还有两个是解锁更大的秘密。任何没人能发现的愚蠢、晦涩的秘密都没有成就,你一定要对发生的事情感到满意。
Steamworks UI 下的成就系统。
Notarization 的问题
虽然我主要是在自己的 Mac 上开发游戏,但在开发的过程中苹果发明了「Notarization」,如果你在新版本的 MacOS 上运行任何应用程序,它会向苹果发出网络请求,询问应用程序的开发者是否向苹果支付年费。如果开发者不支付年费,MacOS 会弹出一个对话框,强烈暗示该应用程序是病毒并拒绝启动。
因此,Windows 将是该游戏的第一个,也许是唯一的发布平台。
使用的库
对于最终交付给最终用户的软件,我们通常希望将依赖性保持在最低限度,但使用一些高质量的软件也是必要的。除了 OpenGL 和标准操作系统之外,这是 Hapland Trilogy 可执行文件最终链接到的完整库列表:
- Steam SDK
- cute_sound
- stb_vorbis
- stb_image
结语
就是这样,如果你把技术做对了,人们在打游戏时根本不会注意到背后是什么,所以有时候这会让你想说:嘿,看看我做到了什么!
参考原文:https://foon.uk/how-flash-2022/https://news.ycombinator.com/item?id=34079543