2

这是过去的怪事!

我正在为命令提示符编写一个 ASCII Pong 游戏(是的,是 oldschool),并且我正在直接写入视频内存(添加。0xB8000000)所以我知道我正在快速渲染(而不是 gotoxy 然后 printf 渲染)

我的代码工作正常,代码在 Turbo C++ V1.01 下编译良好,但动画滞后......现在等一下,有一个警告!在我的超高速涡轮增压 Dell Core 2 Duo 下,这似乎是合乎逻辑的,但是当我按住键盘上的一个键时,动画变得像新编译的婴儿屁股一样流畅。

我想可能是因为我通过超载键盘缓冲区来减慢计算机的速度(真的吗?来吧......)但后来我很快变得聪明并尝试为 DJGPP 和 Tiny C 编译器编译以测试结果是否相同. 在 Tiny C 编译器上,我发现我无法编译“远”指针类型......仍然对此感到困惑,但我能够为 DJGPP 编译并且动画运行顺利!

我想编译它并让它适用于 Turbo C++,但这个问题在过去 3 天里一直困扰着我,无法解决。有谁知道为什么 Turbo C++ 常量调用我的渲染方法(下面的代码)会滞后于命令提示符但 DJGPP 不会?我不知道我是否正在编译为调试,我什至不知道如何检查我是否是。我确实将代码转换为 ASM,并且在源代码的标题中看到了看起来正在调试数据的内容,所以我不知道......

任何和所有的评论和帮助将不胜感激!

这是我所反对的一个简单示例,易于编译,因此请查看:

#include<stdio.h>
#include<conio.h>
#include<dos.h>
#include<time.h>

#define bX 80
#define bY 24
#define halfX bX/2
#define halfY bY/2
#define resolution bX*bY

#define LEFT 1
#define RIGHT 2

void GameLoop();
void render();
void clearBoard();
void printBoard();
void ballLogic();

typedef struct {
 int x, y;
}vertex;

vertex vertexWith(int x, int y) {
 vertex retVal;
 retVal.x = x;
 retVal.y = y;
 return retVal;
}

vertex vertexFrom(vertex from) {
 vertex retVal;
 retVal.x = from.x;
 retVal.y = from.y;
 return retVal;
}

int direction;

char far *Screen_base;
char *board;
vertex ballPos;

void main() {
 Screen_base = (char far*)0xB8000000;
 ballPos = vertexWith(halfX, halfY);
 direction = LEFT;
 board = (char *)malloc(resolution*sizeof(char));
 GameLoop();
}

void GameLoop() {
 char input;

 clrscr();
 clearBoard();
 do {
  if(kbhit())
   input = getch();
  render();
  ballLogic();

  delay(50);
 }while(input != 'p');
 clrscr();
}

void render() {
 clearBoard();

 board[ballPos.y*bX+ballPos.x] = 'X';

 printBoard();
}

void clearBoard() {
 int d;
 for(d=0;d<resolution;d++)
  board[d] = ' ';
}

void printBoard() {
 int d;

 char far *target = Screen_base+d;
 for(d=0;d<resolution;d++) {
  *target = board[d];
  *(target+1) = LIGHTGRAY;
  ++target;
  ++target;
 }
}

void ballLogic() {
 vertex newPos = vertexFrom(ballPos);

 if(direction == LEFT)
  newPos.x--;
 if(direction == RIGHT)
  newPos.x++;

 if(newPos.x == 0)
  direction = RIGHT;
 else if(newPos.x == bX)
  direction = LEFT;
 else
  ballPos = vertexFrom(newPos);
}
4

2 回答 2

1

首先,在代码中:

void printBoard() {
 int d;

 char far *target = Screen_base+d;  // <-- right there
 for(d=0;d<resolution;d++) {

您在d初始化之前使用变量。

我的假设是,如果您在 DOS 窗口中运行它,而不是启动到 DOS 并运行它,kbhit那么如果还没有按键,那就必须做更多的工作(间接地——在 DOS 框提供的环境中)排队。

这应该不会对您的运行时间产生太大影响,但我建议在没有按键的情况下您明确地将 设置input为某个常数。此外,input应该真的是一个int,而不是一个字符。

其他建议:

vertexFrom并没有真正做任何事情。

A = vertexFrom(B);

应该可以替换为:

A = B;

包含运算符的宏常量应该在它们周围加上括号。

#define Foo x/2

应该:

#define Foo (x/2)

这样您就不必担心运算符优先级,无论使用什么代码Foo

在 16 位 x86 PC 下,实际上有 4 个显示区域可以在它们之间切换。如果您可以在其中 2 个动画之间进行交换,那么您的动画应该会立即发生。它被称为双缓冲。您有一个缓冲区用作当前显示缓冲区,另一个缓冲区是工作缓冲区。然后,当您对工作缓冲区感到满意时(并且时间合适,如果您尝试以特定速率更新屏幕),那么您就可以交换它们。我不记得如何做到这一点,但细节不应该太难找到。我建议您可以单独保留初始缓冲区并在退出时恢复到它,以便程序将屏幕留在它开始时的状态。另外,

如果您不想走那条路线并且“X”是唯一改变的东西,那么您可以放弃清除屏幕而只清除“X”的最后一个位置。

屏幕缓冲区不是一个 2 字节单元的数组——一个用于显示字符,另一个用于属性?我想是的,所以我将它表示为一个数组:

struct screen_unit {
    char ch;
    unsigned char attr;
}; /* or reverse those if I've got them backwards */

这将减少您根据偏移量犯错误的可能性。

我也可能将它们作为 16 位值而不是字节读取和写入缓冲区,尽管这应该不会有太大的区别。

于 2010-07-21T18:29:50.147 回答
0

我弄清楚为什么它没有立即渲染,我创建的计时器很好,问题是实际的clock_t 只精确到.054547XXX 左右,所以我只能以18fps 渲染。我解决这个问题的方法是使用更准确的时钟......这是另一回事

于 2010-07-24T11:51:43.883 回答