ASCII DOS游戏-渲染方法

9

我正在编写一个老式的ASCII DOS提示符游戏。说实话,我试图模拟ZZT,以了解这种游戏设计(即使它已经过时)。

我的进展不错,全屏文本模式能够正常工作,我可以创建世界并移动角色,但是我找不到一个合适的定时方法来渲染画面。

我知道我的渲染和预渲染代码很快,因为如果我不添加任何delay()或者(clock()-renderBegin)/CLK_TCK检查(time.h库)的话,渲染速度非常快。

我不想使用delay(),因为据我所知,它是特定于平台的,并且在等待期间无法运行任何代码(如用户输入和处理)。因此,我决定采用以下方式:

do {
    if(kbhit()) {
        input = getch();
        processInput(input);
    }

    if(clock()/CLOCKS_PER_SEC-renderTimer/CLOCKS_PER_SEC > RenderInterval) {
        renderTimer = clock();
        render();
        ballLogic();
    }
}while(input != 'p');

理论上,这应该完全正常。问题在于,当我运行此代码(将RenderInterval设置为0.0333或30fps)时,我得到的最高帧率远不到30fps,而是只有18fps。

我想也许我可以尝试将RenderInterval设置为0.0,看看性能是否会提高...但实际上并没有。即使使用RenderInterval为0.0,我最多只能获得18-20fps的帧率。

我认为可能是由于我不断调用所有这些clock()和“除以那个”的方法,导致CPU速度变慢,但当我将渲染和ballLogic调用从if语句的括号中取出并将RenderInterval设置为0.0时,我得到了非常快的渲染速度。

这对我来说没有意义,因为如果我保留if检查,它不应该仍然很慢吗?我的意思是它仍然必须执行所有计算。

顺便说一下,我正在使用Borland的Turbo C++ V1.01编译。


你和我一样,咖啡。(#throwstar seek). @Parad0x13: 如果你不介意离开DOS,我写了一个库来模拟这种图形风格在任何由SDL支持的平台上:http://libfake437.googlecode.com - Jack Kelly
如果您存储了“clock()”的结果并分配存储的值,则将节省对其的调用(最快的代码是您不调用的代码),并且它将更准确(否则,您将失去从第一次调用“clock()”,执行所有数学运算并处理分支之间的时间)。即使您不再将CPU使用率提高到最大,这种精度损失也会使游戏运行速度变慢,而这可能不是您想要的。(编辑:哈哈,刚看到这个日期,那就算了吧) - Grant Peters
5个回答

2

最佳的游戏体验通常是通过与显示器的垂直复位同步来实现。除了提供时间,这还将使游戏在屏幕上运行更加平滑,至少如果您连接了一个 CRT 显示器到计算机。

在80x25文本模式下,垂直复位(在VGA上)每秒发生70次。我不记得EGA/CGA的频率是否相同,但相当确定Hercules和MDA上的频率为50赫兹。通过测量,比如20帧的持续时间,您应该有足够好的估计来确定您正在处理的频率。

让主循环变成像这样:

  while (playing) {
     do whatever needs to be done for this particular frame
     VSync();
  }

  ...  /* snip */

  /* Wait for vertical retrace */
  void VSync() {
    while((inp(0x3DA) & 0x08));
    while(!(inp(0x3DA) & 0x08));
  }

1
clock()-renderTimer > RenderInterval * CLOCKS_PER_SEC

可能会更快地计算,如果预先计算RenderInterval * CLOCKS_PER_SEC 部分,甚至可能计算得更快。


谢谢您的回复,我看到了优化。即使有了它,问题仍然存在。不过我已经尝试过了。 - Parad0x13
编译器对循环进行各种优化,这可能会产生一些奇怪的结果(特别是如果它们包含分支)- 尝试查看两种情况下生成的代码。 实际上,处理器也会进行类似的工作(查找分支预测)。 - Ofir
我检查了预处理器,但是我无法找出启用了哪些优化。 - Parad0x13

1

我找到了为什么它不能立即渲染的原因,我创建的计时器没问题,问题在于实际的clock_t只精确到.054547XXX左右,所以我只能以18fps渲染。我解决这个问题的方法是使用更精确的时钟...但这是另一个故事了。


0

这个怎么样:你正在从 x (=clock()) 和 y (=renderTimer) 中减去。x 和 y 都被 CLOCKS_PER_SEC 除以:

clock()/CLOCKS_PER_SEC-renderTimer/CLOCKS_PER_SEC > RenderInterval

写成这样不是更高效吗:

( clock() - renderTimer ) > RenderInterval

我看到除法的第一个问题是,由于它发生在两个长整数之间,所以您将无法获得实数。第二个问题是,通过将RenderInterval * CLOCKS_PER_SEC相乘,可以更有效地摆脱它,从而简化操作。

添加括号可以增加可读性。也许通过简化这个公式,您将更容易地知道哪里出了问题。


我尝试了这些公式优化,但结果仍然相同。 - Parad0x13
如果您强制设置RenderInterval = 1会怎样? - Baltasarq

0

正如你在最近的问题中发现的那样,你受到CLOCKS_PER_SEC的限制,它只有大约18。每个时钟离散值只能得到一个帧,这就是为什么你被限制在18fps。

你可以使用屏幕垂直消隐间隔进行定时,这是游戏传统的做法,因为它避免了“撕裂”(其中一半屏幕显示一个帧,另一半显示另一个帧)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接