Как уменьшить мерцание/отставание при проклятиях?

avatar
Challos
9 августа 2021 в 02:45
130
1
0

В настоящее время пытаюсь решить проблему с мерцанием в очень маленькой игре, которую я делаю в ncurses с C++. Мерцание не вызывает отвращения, но я могу себе представить, что оно гораздо более раздражает, когда одновременно визуализируется/двигается больше объектов. Код ниже. Также есть заголовочный файл, но я не думаю, что он здесь уместен.

#include "game.h"


// only real note is that the sprite char** must be NULL terminated
class Entity {
  public:
    int x;
    int y;
    //these are mainly here for bounds checking
    int width;
    int height;

    //position and sprite are needed by outside methods, but nothing else should be
    // this needs to be stored as an array of chars* in order to properly render the sprite
    // the sprite array also needs to be null terminated
    const char** sprite;
    Entity(int y, int x, const char** sprite) {
      this->y = y;
      this->x = x;
      this->sprite = sprite;
      this->width = getWidth();
      this->height = getHeight();

    };


    int getWidth() {
      int w_max = 0, i = 0;
      while (this->sprite[i] != NULL) {
        int line_width = strlen(sprite[i]);
        if (line_width > w_max) {
          w_max = width;

        }
        i++;
      }

      return w_max;

    }

    int getHeight() {
      int current_height = 0, i = 0;
      while (this->sprite[i] != NULL) {
        current_height++;
        i++;
      }
      return current_height;

    }

};

class Player: public Entity {
  public:
    Player(int y, int x, const char** sprite) : Entity (y, x, sprite) {}

    int move(int proposed_direction) {
      int right = 0, down = 0;

      switch(proposed_direction) {
        case KEY_LEFT:
          right--;
          break;
        case KEY_RIGHT:
          right++;
          break;
        case KEY_UP:
          down--;
          break;
        case KEY_DOWN:
          down++;
          break;
        case 'q':
          endwin();
          exit(0);
          break;
        default:
          down++;
          break;

      }
      this->y += down;
      this->x += right;

      return -1;

    }


//    int check(int proposed_direction) {
//      return -1;
//
//    }
};


void screenStart() {
  initscr();            
  noecho();
  curs_set(FALSE);
  //honestly not sure why this is required
  nodelay(stdscr, TRUE);
  //timeout(-1);
  keypad(stdscr, TRUE);

}

void drawEntity(Entity* entity) {
  
  //this is to print out every line of the sprite in order at the right place
  for (int i = 0; entity->sprite[i] != NULL; i++) {
    // the + i is there because it draws horizontally line by line
    mvprintw(entity->y + i, entity->x, entity->sprite[i]);

  }
  

}

int main() {
  screenStart();
  const char* player_sprite[] = {"XX", "XX", NULL};
  Player* player = new Player(15, 15, player_sprite);

  int ch;
  for (;;) {
    erase();
    if ((ch = getch()) == ERR) {
      drawEntity(player);

    }

    else {
      player->move(ch);
      drawEntity(player);

    }
    wnoutrefresh(stdscr);
    doupdate();
    napms(16);

  }

    endwin();

  return 0;

};

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

РЕДАКТИРОВАТЬ: Новый код ниже, после изменения порядка перерисовки/очистки/вызовов без мерцания:

for (;;) {
    werase(win);
    auto startTime = std::chrono::steady_clock::now();
    box(win, 0, 0); 
    player->drawEntity();
    int ch = wgetch(win);
    if (ch != ERR) {
      player->move(ch);
    }   

    auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - startTime);
    napms(WAIT_TIME - diff.count());

  }
Источник

Ответы (1)

avatar
Thomas Dickey
10 августа 2021 в 23:15
0

Этот фрагмент вносит основной вклад в мерцание:

erase();
if ((ch = getch()) == ERR) {
  drawEntity(player);

}

erase изменяет весь экран, а следующий getch выполняет refresh (фактически очищает экран), прежде чем ваш код перерисовывает экран. Если вы измените его организацию таким образом, что за erase следует перерисовка, то это устранит большую часть обновлений экрана, т. е. небольшая активность на фактическом терминале.

Challos
10 августа 2021 в 23:59
0

Да, это был ответ, который мне дал кто-то другой, и это правильно. Ни один из учебников/других сообщений на SO, который я нашел, на самом деле не показал пример этого, поэтому я буду редактировать то, что у меня есть в настоящее время, в свои вопросы. Спасибо!