虚拟现实经历了几波成为“下一件大事”的浪潮,这将永远改变视频游戏的格局。在90年代初期,虚拟现实的潜力吸引了公众的想象力,报纸,杂志,电视新闻频道甚至是割草机人等大型电影都采用了引人注目的功能。尽管最终有一些VR产品推向市场,但更多的公司尝试了该技术。
这些公司之一就是Sega,他们寻求创建负担得起的VR解决方案,作为其家庭控制台Sega Genesis的附加组件。世嘉的家用VR产品将庞大的虚拟现实街机的成本控制在五位数的范围内,而其建议零售价仅为200美元。能做到吗?好吧,有点。
Sega VR头戴式耳机配有高频惯性测量单元和两个LCD屏幕,与当今的VR头戴式耳机具有许多基本设计。当世嘉在1993年向记者和零售商正式推出该装置时,这种设计就具有革命性意义,它承诺在虚拟现实的前沿开辟新的天地。得益于一家名为Ono-Sendai的初创公司许可的技术,他们奇迹般地达到了200美元的目标,该公司的专利跟踪解决方案仅需1美元即可制造。
Sega取消其VR头盔的官方理由值得怀疑:他们声称这种体验是如此逼真和身临其境,以至于在使用它时,玩家四处走动会造成很高的伤害风险。但是,取消注册的一个可能因素是Sega从斯坦福研究所获得的反馈,该警告警告说头痛,头昏眼花和疾病,特别是对于年幼使用者和儿童。在Retro Gamer Podcast的一集中,美国世嘉公司前首席执行官Tom Kalinske证实了这些问题是决定放弃该项目的主要因素。
到目前为止,我们对Sega VR的了解大部分来自贸易展览的外观,营销材料,专利文件和第一手资料。这意味着许多部门的技术细节仍是推测性的或完全未知的。但是,当回顾和研究突破了当时许多技术界限的硬件时,这些细节很重要!无论Sega VR是否实现了其雄心勃勃的目标,它在VR历史上仍然是一个引人入胜且引人注目的入口。
为了研究这种性质的硬件,如果您无权访问硬件或其实现详细信息,那么访问软件通常是次要的事情。该软件将准确告诉您对硬件的期望,并给出了这些期望,您可能会发现您有足够的信息来模拟硬件。至少,您将拥有足够的信息来仿真符合软件期望的硬件版本,而这正是我们的目标。
Sega VR正在开发多个产品,但是由于从未发布过硬件,因此直到今天,它们都没有出现过。由于迪伦·曼斯菲尔德(Dylan Mansfield)在游戏亚历山大港(Gaming Alexandria)上所做的不可思议的努力,其中之一已被追回。 Dylan与Futurescape Productions的联合创始人Kenneth Hurley取得了联系。
他的公司从事的Sega VR游戏叫作Nuclear Rush,是第一人称动作游戏,该游戏的介绍是对1993年以来最好的描述:
一位知情人士(仅是职业经纪人)告诉您秘密区域,充满了老式反应堆的废物。
有时候,游戏被误认为是Sega VR的另一款游戏Iron Hammer,可能是由于存在许多视觉和描述上的相似之处。在1993年夏季CES上提供的一些简要介绍中,“核冲刺”也缺少游戏最终版本中使用的HUD,从而使外观更加鲜明。
在Dylan联系Hurley并解释了他对Sega VR的兴趣之后,Hurley掏出了一张日期为1994年8月6日的CD-ROM。幸运的是,在26年之后,它仍然完好无损,其中包含了Nuclear Rush的完整源代码以及源代码和工具Hurley还在其他Sega Genesis游戏中使用过。
这使我们开始了旅程。我们将首先恢复并运行“核冲刺”,最后,在废弃世嘉超过25年之后,我们将复活令人难以置信的,未发布的世嘉历史。在此过程中,我们偶尔会深入了解技术细节,因此如果您迷路了,请随时使用下表。
尽管Hurley的光盘上都包含有Nuclear Rush的源代码,但找不到编译的二进制文件。为了找到可以再次组合的人,Dylan与我联系。乍一看,似乎我们具备了制作游戏所需的一切。所有艺术方面的依赖都在那里,并且大多数工具都存在,这些数据来自Hurley CD-ROM上的另一款游戏Monster Hunter。
大部分的Nuclear Rush游戏代码都使用C语言编写,依赖于Sierra 68000 C编译器,并带有一些样板代码(包括Sega VR头戴式耳机驱动程序)以及其他一些零碎的代码。游戏中最耗时的部分是缩放器,它也是用C编写的,但有所不同。 C代码按需生成本机M68K代码,然后调用生成的代码,这是花费大部分周期的地方。
除了标准的构建工具之外,构建过程还利用了一些专有工具来提取艺术数据并将其以可用于游戏的形式吐出。 CVTSCE.EXE用于静态背景/ UI屏幕。 ANM2FPA.EXE用于具有可选RLE的可缩放比例的精灵和动画。最后,使用标准票价(12位偏移,4位长度)LZSS压缩,使用LZSSC.EXE压缩CVTSCE.EXE的输出。
在第一次构建尝试时,我遇到了一个丢失的工具DUMP.EXE。查看预期的用法,很明显,该程序只是打开一个文件,并为每个字节向stdout发送文本,以便汇编程序可以提取二进制文件。在用Borland C ++ 3.0编写代码花了一些宝贵的时间之后,我构建了一个替换可执行文件,然后我就走了!
构建过程成功完成,并生成了一个COFF文件,其中ROM的重要部分分为几部分。我想保持在相同的MS-DOS环境下生成即用型ROM映像的整个过程,因此我短暂地回到了我的好朋友Borland处,并编写了另一个程序来解析COFF,正确地生成了带有这些部分的二进制文件放在ROM头中并计算校验和。
第一次尝试在仿真器中运行ROM时,我收到了一个让我非常高兴的屏幕:
当然,没有头部追踪器。游戏成功识别了该事实,并在禁用耳机支持的情况下进入了标题屏幕。标题音乐开始播放,一切似乎都很好,直到我按下开始并进入主菜单。游戏继续运行,但看起来菜单字体所使用的调色板刚刚被踩踏。我仍然可以阅读文字,因此我尝试加载到第一层…那时程序计数器从悬崖上跳下来,一切都爆炸了。
因此,就像这些项目经常发生的那样,是时候进行调试了!我从COFF中转储了所有调试符号,然后使用符号地址放置了几个断点,并查看在情况出现严重转折之前,我已将其插入到级别加载中。
经过反复试验,我确定游戏可以成功加载到第一级,甚至偶尔显示一两帧,然后缩放器从Sprite中拉出了一个不好的偏移,这在生产线上产生了非常糟糕的后果。您可能还记得我提到过必须从另一个游戏的数据中提取其中一些工具,事实证明这就是问题所在。在“核冲击”之后的某个时刻对工具进行了修改,并最终归结为缩放器主要入口点顶部附近的一些代码:
最后两个指针的增量将跳过完全未使用的数据,并且似乎在某些时候已将数据从ANM2FPA.EXE输出中删除。由于该数据尚未使用,因此修复很简单-我只需要删除这两个额外的指针增量即可。
这样,水平加载了,但是背景中的图块和一些HUD子画面仍然显示为乱码。事实证明,这些都是类似的问题,Monster Hunter工具共享的数据格式有了更多更改。在那些情况下,所表现出的问题所造成的灾难性较小。因此,最后,有了ROM,并在一个非常忙碌的星期六解决了所有这些问题,我成功地成功加载到一个关卡中并享受了我的第一次《核奔》!
随着游戏的启动和运行,我想返回并修复在此过程中遇到的其他错误。我不确定这些错误中有多少是混合工具集的结果,以及在游戏的最终版本中可能还剩下多少。请记住,此处的“最终”并不意味着“零售最终”,尽管游戏实际上已经完成,但它并没有享受到最后一次质量保证推动时产生的典型的漏洞。推测地说,基于整个代码中出现的少量WCES94预处理程序检查,Nuclear Rush计划在1994年冬季CES上再进行一次展示。据我所知,该游戏从未出现在展会上,这可能在预谋中起到了作用项目结束。可以说,距离这些事件足够远的地方,可以猜测肯尼思·赫尔利(Kenneth Hurley)的1994年8月6日发行的CD-ROM代表了相当不错的最终开发快照-接近终点线,但还没有为商业发行做好充分准备。
话虽如此,我认为在整个游戏中看到的猖palette的调色板重击很有可能会再次与工具集相关,而事实证明这是一个有趣的问题。 CVTSCE.EXE也已在Nuclear Rush之后的某个时候进行了修改,正如您可能会在前一段时间的使用情况截图中注意到的那样,该程序有几个选项可用于更改将调色板数据打包到最终二进制文件中的方式。似乎其中一些参数默认设置已更改,但是存在一个更深层次的问题。
CVTSCE.EXE提取的许多基于图块的源图像都包含多个调色板,并且在不同的基本偏移处具有不同数量的调色板。例如,一幅图像可能使用调色板1和调色板2,而另一幅图像可能仅使用调色板0,依此类推。输出格式旨在为文件中的第一个调色板指定索引,并指定调色板的数量。加载映像时,它将使用该索引和计数来确定要累加哪些现有调色板并将其转移到CRAM。这里的问题是,对于调色板索引/计数,没有通用的默认设置对“核高峰”中的所有图像都有意义,而且我没有看到任何每个文件的数据都可能有助于指示在目录内容之外使用的正确值。平铺地图本身。
这导致我修改了CVTSCE.EXE。我添加了一个选项来扫描图块地图,找出其中引用了哪些调色板,并使用该数据确定调色板范围和要输出的计数。我不知道这是否是该工具最初为Nuclear Rush所做的工作,但似乎可以完成工作!
在实际硬件上进行测试时,我遇到了下一个主要问题。 Nuclear Rush将水平滚动与基于滚动表的垂直滚动相结合,后来的Genesis模型可以毫无问题地处理。但是,较旧的模型将使背景平面的左列空白。发生这种情况时,它看起来像这样:
Nuclear Rush尝试通过检查硬件版本(寄存器$ A10001)来回避此问题,如果版本为0,它将退回到截断水平滚动位的位置。这意味着实际上只允许背景一次滚动一个图块,从而隐藏了列空白的问题。结果非常笨拙,而不是平滑的水平滚动,并且由于在某些Sega Genesis型号上毫无警告地出现,因此它不是理想的解决方案。我碰巧有一个Model 2 Genesis,它报告的版本大于0,但是仍然缺少后来的VDP更改来解决此滚动问题。当我第一次在硬件上运行游戏时,屏幕左侧的空白垂直栏引起了我的欢迎,因为该测试未能赶上我的特定硬件版本。
Nuclear Rush利用垂直滚动表来实现“滚动”效果,该效果在旋转时可见。通过为滚动表中的每个列条目累积一个小的偏移量,并通过应用对象所属的列的偏移量来补偿Sprite位置,可以达到该效果:
我没有完全检查效果,而是通过显示菜单中的选项来建立简单的修复方法。第一种模式禁用垂直滚动表(允许在较旧的硬件版本上进行平滑滚动),第二种模式强制进行平滑滚动(即使硬件在没有列空白的情况下也不支持它),第三种模式则强制截断滚动,第四种模式模式将恢复为原始硬件版本检查。它不是万能的,并且可以根据我的Model 2外壳进行改进,但可以用于后代。
之后,我继续解决问题并进行了少量补充,直到对游戏状态感到满意为止。这包括修复DMA计时错误和一些崩溃,使密码输入屏幕再次运行,以及修复仅在游戏以立体模式运行时出现的一些问题。
最后,是时候模拟Sega VR耳机了!这就是我一直以来非常兴奋的地方。如果我只有一个预先构建的ROM映像而不是源代码可以在这里使用,那么仍然可以实现。这将花费更多的时间,并且需要进行大量的拆装筛选,以找出游戏期望从头戴式耳机获得什么样的数据。但是,有了源代码,它变得容易得多!
Nuclear Rush源代码有两个文件可供Sega VR耳机通信参考。 HEADSET.ASM是美国Sega提供的原始驱动程序源代码,而VRDRV.ASM是Nuclear Rush使用的代码的略微修改版本。在两个副本中,处理读取耳机的核心例程均相同。驱动程序源还引用了“ VR.DOC / VR.TXT”,它可能包含有关耳机实现的一些有趣的技术信息,但不幸的是,该资源库中没有该信息。尽管拥有另一个信息源会很不错,但是仍然有足够多的信息可以仅从源代码创建耳机实现。
读取耳机是通过多路复用方案完成的,就像普通的Sega Genesis控制器一样。实际上,与耳机的所有交互都是通过控制器端口2进行的,驱动程序假定耳机已插入。
软件方面的耳机初始化从握手开始。软件将TR和TH引脚都设置为通过寄存器$ A1000B写入。与仅将TH引脚设置为写入的标准控制器不同,当通过寄存器$ A10005写入耳机时,这给了我们两位以指定不同类型的命令。 $ 60(TH + TR)的值写入指示耳机进入空闲模式,$ 40(TH)指示耳机执行重置。我的实现会等到它收到复位命令,然后使用该命令将仿真器踢入Sega VR模式。发出空闲命令后,软件会切换$ 20(TR),以推进可从$ A10005读取的数据。该软件立即期望在空闲命令后读取$ 70作为确认响应,并验证TH,TR和TL是否按预期设置。确认之后是耳机的标识符$ 08,$ 00,其后半部分已保留。如果交易的任何部分都没有按预期进行,那么驾驶员会取消整个交易,然后我们以“无耳机”模式进行操作。
初始握手完成后,将顺序读取所有耳机,以请求当前的偏航角,俯仰角和左/右眼位。明显没有滚动,这意味着耳机只能用于跟踪两个旋转轴。我不知道我们从IMU会看到什么样的漂移,或者在积累角度运动之前应用了哪种滤波器,因为耳机负责提供绝对坐标中的角度。这意味着该软件仅读取输出并按原样应用它,没有任何问题。尽管这使得仅凭软件很难知道硬件内部发生了什么,但是当我们考虑到Genesis中缺乏处理能力(和/或专用硬件)时,这种实现细节很有意义。我正在推测,但是可能有专用的硬件位于耳机外部,位于耳机和Genesis之间。
现在,我需要了解耳机后初始化数据流的格式,幸运的是,耳机驱动程序源在注释中很好地列出了所有内容:
* xxxx | xxxx | xxxx | LR X8 Y8 | X7 X6 X5 X4 | X3 X2 X1 X0 | Y7 Y6 Y5 Y4 | Y3 Y2 Y1 Y0 * X0-X8表示从0到360度的十六进制绝对YAW值* Y0-Y7表示绝对螺距值从0到+/- 30度* Y8是符号位向上查找Y8 = 0 *下移Y8 = 1
这是描述耳机读取功能返回的数据格式,但与从耳机实际读取的数据格式为1:1。 x位是32位寄存器顶部的未使用位,其中包含读取功能返回时耳机数据读取的累积。
这意味着我们获得了9个位来表示360度偏航角,并且得到了9个位(包括符号)来代表60度俯仰角,其中很大一部分未使用。 “ L R”位指定耳机期望接下来被扫出哪只眼睛。在我的实现中,我将角度作为32位浮点值进行跟踪,以便它们可以对高精度输入保持敏感。当将角度反馈给创世纪时,我将其钳位并对其进行量化/编码:
uint32_t encode_headset_angles(const float * pAngles){//假设角度已经被夹紧const uint32_t pitch =(pAngles [0]> = 0.0f)吗? (uint8_t)pAngles [0]:((uint8_t)-pAngles [0] ^ 0xFF)| (1 << 16); const uint32_t yaw =(uint32_t)pAngles [1];回音((偏航&0xFF)<< 8)| ((yaw&0x100)<< 9);}
该函数的输出与眼图位的仿真状态结合在一起,这使我们进入了显示同步的有趣话题。
核奔跑被设计为以15Hz的频率锁定运行,这意味着完整的游戏循环旨在每秒运行15次。但是,它仍然会以60Hz的频率击中VBlank中断。在VBlank中断结束时读取了耳机,但是在VBlank开始时,我们正在从先前的VBlank中读取耳机数据,以确定要设置哪只眼睛(可能启动DMA传输)以进行扫描。要添加到此处有趣的事件顺序,尽管头戴式耳机驱动程序源指出头戴式耳机支持以60Hz运行,但是Nuclear Rush仅在每隔一个VBlank末尾读取头戴式耳机。
当您将Genesis连接到旧的NTSC显示器时,假设VDP不在隔行扫描模式下运行,它将以大约60Hz的频率扫描新帧。通常,这意味着需要扫描到两个单独的隔行扫描场,但是对于两个单独的眼图显示器,我们可以轻松地将相同的信号解释为交错的逐行扫描图像,每个眼图以30Hz更新。我期望这就是世嘉VR耳机