DOS ASCII Animation Lagging без постоянного ввода, скомпилирован Turbo C

Вот диковинка из прошлого!

Я пишу игру ASCII Pong для командной строки (да, старая школа), и я пишу напрямую в видеопамять (доп. 0xB8000000), поэтому я знаю, что рендеринг выполняется быстро (в отличие от gotoxy, а затем рендеринга printf)

Мой код работает нормально, код отлично компилируется под Turbo C ++ V1.01, НО анимация тормозит ... теперь подождите, подождите, есть кавает! Под моим супербыстрым турбонаддувом Dell Core 2 Duo это кажется логичным, однако, когда я держу клавишу на клавиатуре, анимация становится плавной, как нижняя часть только что скомпилированного ребенка.

Я подумал, что, может быть, это произошло из-за того, что я замедлял работу компьютера, перегружая буфер клавиатуры (ну что ж, да ладно ...), но потом я быстро поумнел и попытался скомпилировать для DJGPP и Tiny C Compiler, чтобы проверить, совпадают ли результаты . На Tiny C Compiler я обнаружил, что не могу компилировать "дальние" типы указателей ... все еще запутался в этом, но я смог скомпилировать для 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);
}

person Parad0x13    schedule 21.07.2010    source источник
comment
Вы пытались сделать задержку повтора КОРОТКОЙ и максимизировать частоту повтора в настройках клавиатуры на панели управления?   -  person bits    schedule 21.07.2010
comment
Я не понимаю, почему настройки клавиатуры могут стать предметом исследования, это не настройки клавиатуры, нажатие и удержание клавиши было просто способом вызвать плавный рендеринг по какой-то причине, и я хотел бы знать, почему   -  person Parad0x13    schedule 21.07.2010
comment
Задержка повтора точно связана с вашим вопросом здесь. Это означает, что существует задержка между вашим первым ключевым событием и последующими ключевыми событиями потоковой передачи. Я предлагаю вам посмотреть это в настройках клавиатуры. Сообщите, что вы нашли.   -  person bits    schedule 21.07.2010
comment
Моя задержка повтора установлена ​​на одну полосу ниже самой быстрой (из 4), а моя частота повтора самая высокая. Я получаю одни и те же результаты независимо от того, что я установил для задержки повтора, но когда я установил самую медленную частоту повтора, я не могу добиться плавного рендеринга. Этого следовало ожидать, но он не показывает мне, что изнутри заставляет прерывания клавиатуры влиять на мои вызовы рендеринга (по крайней мере, в моем понимании) и, кстати, спасибо за вашу помощь, я чувствую, что, возможно, я продвигаюсь вперед   -  person Parad0x13    schedule 21.07.2010


Ответы (2)


Во-первых, в коде:

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 на самом деле есть 4 области отображения, между которыми можно переключаться. Если вы можете переключаться между двумя из них для своей анимации, и ваша анимация должна появляться мгновенно. Это называется двойной буферизацией. У вас есть один буфер, который действует как текущий буфер дисплея, и один, который является рабочим буфером. Затем, когда вас устраивает рабочий буфер (и время подходящее, если вы пытаетесь обновить экран с определенной скоростью), вы меняете их местами. Я не помню, как это сделать, но подробности найти не составит большого труда. Я бы посоветовал вам оставить исходный буфер в покое и восстановить его после выхода, чтобы программа оставила экран примерно в том состоянии, в котором она была запущена. Кроме того, вы можете использовать другой буфер для хранения отладочного вывода и затем, если вы удерживаете пробел или что-то еще, этот буфер может отображаться.

Если вы не хотите идти по этому маршруту и ​​меняется только «X», то вы можете отказаться от очистки экрана и просто очистить последнее местоположение «X».

Разве экранный буфер не является массивом из 2-х байтовых единиц - одна для отображаемого символа, а другая для атрибутов? Думаю, да, поэтому я бы представил его как массив:

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

Это снизит вероятность того, что вы сделаете ошибки, связанные с смещениями.

Я бы также, вероятно, прочитал и записал их в буфер как 16-битное значение, а не как байт, хотя это не должно иметь большого значения.

person nategoose    schedule 21.07.2010
comment
Отличный ответ, +1. Я согласен, что проблема возникает из-за специфичной для C-компилятора реализации kbhit (). - person Sanjay Manohar; 22.07.2010
comment
Я исправил ошибочные проблемы с моим кодом, но в основном с вызовами kbhit (). Фактически, я полностью вырезал их из своего кода, чтобы вы не могли выйти из программы только через пользовательский ввод, и я все равно получаю те же результаты. Даже если функция kbhit () не указана в источнике, я все равно получаю прерывистую анимацию, которая сглаживается, когда я удерживаю кнопку. Хотел бы я сказать, что это устранило проблему, но, к сожалению, этого не произошло. Я ценю ваши советы по лучшему программированию, но проблема все еще существует! - person Parad0x13; 22.07.2010
comment
Я только что узнал, что если я перейду в полноэкранный режим, не будет задержек анимации! В чем может быть разница? - person Parad0x13; 22.07.2010
comment
Готов поспорить, что у вас прерывистая анимация не из-за того, что нажатие без клавиш происходит медленнее, а быстрее. Настолько быстро, что происходит более одной перерисовки экрана, когда ОС фактически перерисовывает окно DOS. Он перерисовывает его в середине вашего обновления. В полноэкранном режиме ваша программа может иметь реальный доступ к реальному оборудованию, поэтому ОС не играет такой роли. Увеличьте задержку и посмотрите. - person nategoose; 22.07.2010

Я понял, почему он не рендерился сразу, таймер, который я создал, в порядке, проблема в том, что фактическое значение clock_t имеет точность только до 0,054547XXX или около того, поэтому я мог рендерить только со скоростью 18 кадров в секунду. Я бы исправил это, используя более точные часы ... это совсем другая история.

person Parad0x13    schedule 24.07.2010