15

我听说旧的街机横向滚动游戏使用特定的编程技巧来启用高性能横向滚动。

我知道几年前这些机器还不够强大,无法像现在这样每帧重新绘制整个屏幕。有一些技术,例如脏矩形,可以在背景静止且只有精灵移动时最小化重新绘制所需的屏幕区域。

上述方法仅在背景不改变时才有效(因此大部分屏幕像素保持静止)。

垂直滚动游戏,例如老式射击游戏,由于滚动而导致每一帧的背景都发生变化,因此事情变得更加困难。但是,可以利用像素被馈送到显示器的方式(逐行)。我想人们可以使用更大的缓冲区并将数据指针每帧“向下”移动几行,以便从另一个位置开始重新绘制,从而给人一种平滑滚动的印象。仍然只需要重新绘制精灵(以及屏幕边缘的一些背景),这是一个严重的优化。

然而,对于横版游戏来说,事情并不是那么简单明了。尽管如此,我知道过去的某个地方有人进行了优化,该优化(有一些限制)允许旧机器水平滚动背景而无需每帧重绘它。

IIRC 它被用于许多老游戏,主要是 80 年代的 beat'em ups,以及演示场景制作

你能描述一下这种技术并说出它的作者吗?

4

4 回答 4

11

我已经为老式 C64 编写了游戏,正是这样做的。基本上有两件事需要注意:

  1. 这些游戏没有使用位图图形,而是使用“重新映射”的字符字体,这意味着 8x8 像素的块实际上只是一个字节。

  2. 接下来要注意的是,硬件支持将整个屏幕移动 7 个像素。请注意,这不会以任何方式影响任何图形 - 它只是使发送到电视的所有内容都有点偏移。

所以 2) 可以真正平滑滚动 7 个像素。然后你移动了每个字符——对于一个完整的屏幕来说,它正好是 1000 字节,计算机可以处理,同时你将滚动寄存器向后移动了 7 个像素。8 - 7 = 1 意味着看起来你又滚动了一个像素……然后它就这样继续下去。所以 1) 和 2) 结合起来产生了真正平滑滚动的错觉!

在那之后,第三件事开始发挥作用:光栅中断。这意味着当电视/显示器即将开始在指定位置绘制扫描线时,CPU 会收到中断。这种技术使得创建分屏成为可能,这样您就不需要滚动整个屏幕,这与我的第一个描述相反。

更详细地说:即使您不想要分屏,光栅中断无论如何都非常重要:因为它在当时和今天一样重要(但今天框架对您隐藏了这一点)更新屏幕在正确的时间。当电视/显示器在可见区域的任何位置更新时修改“滚动寄存器”会导致一种称为“撕裂”的效果 - 您清楚地注意到屏幕的两个部分彼此不同步一个像素。

还有什么好说的?好吧,重新映射字符集的技术使得制作一些动画变得非常容易。例如,传送带和齿轮之类的东西可以通过不断改变屏幕上代表它们的“字符”的外观来设置动画。因此,只需更改字符映射中的一个字节,跨越整个屏幕宽度的传送带就可以看起来像是在到处旋转。

于 2011-11-13T17:22:19.027 回答
8

我在 90 年代做过类似的事情,使用了两种不同的方法。

第一个涉及“窗口化”,它受到 VESA SVGA 标准的支持。有些卡正确地实现了它。基本上,如果您有一个大于可显示区域的帧缓冲区/视频 RAM,您可以绘制一个大位图并为您想要显示的该区域内的窗口提供系统坐标。通过更改这些坐标,您可以滚动而不需要重新填充帧缓冲区。

另一种方法依赖于操作 BLT 方法,用于将完整的帧放入帧缓冲区。将页面传送到与屏幕大小相同的帧缓冲区既简单又有效。

我发现了这个旧的 286 汇编代码(在一个正常工作的17 年前的软盘上!),它将一个 64000 字节(320x200)的屏幕从屏幕外页面复制到视频缓冲区:

  Procedure flip; assembler;
    { This copies the entire screen at "source" to destination }
    asm
      push    ds
      mov     ax, [Dest]
      mov     es, ax
      mov     ax, [Source]
      mov     ds, ax
      xor     si, si
      xor     di, di
      mov     cx, 32000
      rep     movsw
      pop     ds
    end;

移动的rep movswCX 字(在这种情况下一个字是两个字节)。这是非常有效的,因为它基本上是一条指令,告诉 CPU 尽快移动整个事物。

但是,如果您有更大的缓冲区(例如,1024*200 用于侧滚动条),您可以轻松地使用嵌套循环,并在每个循环中复制一行像素。例如,在 1024 像素宽的缓冲区中,您可以复制字节:

start          count            
0+left         320
1024+left      320 
...
255*1024+left  320

left您想要从(屏幕左侧)开始的大背景图像中的 x 坐标在哪里。

当然,在 16 位模式下,需要对段指针(ES、DS)进行一些魔术和操作才能获得大于 64KB 的缓冲区(实际上是多个相邻的 64k 缓冲区),但效果很好。

这个问题可能有更好的解决方案(今天肯定有更好的解决方案),但它对我有用。

于 2011-11-13T17:28:21.757 回答
2

街机游戏经常采用定制的视频芯片或离散逻辑来允许滚动而无需 CPU 做(很多)工作。这种方法类似于 danbystrom 在 C-64 上所描述的。

基本上,图形硬件负责精细滚动字符(或图块),然后一旦滚动寄存器达到其限制,CPU 就会处理替换所有图块。我目前正在研究处理硬件中多个滚动背景的 Irem m-52 板。原理图可以在网上找到。

于 2012-02-23T22:47:00.037 回答
1

为了在 Commodore Amiga 上向右滚动,我们使用Copper将屏幕右移至 16 像素。当屏幕移动后,我们将 2 个字节添加到屏幕缓冲区的起始地址,而在右侧,我们使用Blitter将图形从主存复制到屏幕缓冲区。我们将屏幕缓冲区设置为比屏幕视图稍大,这样我们就可以复制图形而不会在视口右侧看到闪烁效果。

于 2014-04-22T09:29:20.130 回答