【问题标题】:What is the "side scrolling hack" from old games?什么是旧游戏中的“横向滚动破解”?
【发布时间】:2011-12-28 03:12:27
【问题描述】:

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

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

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

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

但是,对于横向卷轴游戏,事情并不是那么简单明了。不过,我知道在过去的某个地方,有人已经进行了优化(有一些限制),允许旧机器水平滚动背景,而无需每帧都重绘它。

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

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

【问题讨论】:

  • 两个出色的非重叠答案!我希望我能同时接受:-)

标签: graphics 2d demoscene


【解决方案1】:

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

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

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

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

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

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

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

【讨论】:

  • 不错。我在 PC 上尝试过类似的操作,但由于只能在一组中放置 256 个不同的“角色”而受到阻碍。
  • C64 的视频芯片中还有一个标志可以将显示水平缩小一个字符,这样您就不会注意到当前滚动的额外字符。
  • @yi_H 确实如此。现在你真的很了解细节! :-)
  • 请注意,在 c64 上,您还必须移动 1000 字节的颜色内存,与屏幕内存相反,它不能在复制屏幕上缓冲
  • ...这就是为什么滚动条总是只使用四种颜色!
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    街机游戏经常采用定制的视频芯片或离散逻辑,以允许在 CPU 无需执行(大量)工作的情况下进行滚动。这种方法类似于 danbystrom 在 C-64 上所描述的。

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

    【讨论】:

      【解决方案4】:

      我在 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 movsw 移动了 CX 字(在这种情况下,一个字是两个字节)。这非常有效,因为它基本上是一条指令,告诉 CPU 尽快移动整个事物。

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

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

      其中left 是您想要开始的大背景图像中的 x 坐标(屏幕左侧)。

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-08
        • 1970-01-01
        • 1970-01-01
        • 2013-10-28
        • 1970-01-01
        相关资源
        最近更新 更多