如何减少 flickering/lag 的诅咒?

How to reduce flickering/lag on curses?

目前正在尝试解决我用 C++ 在 ncurses 中制作的非常小的游戏中的闪烁问题。闪烁并不令人厌恶,但我可以想象当更多实体同时处于 rendered/moving 时它会更加烦人。下面的代码。还有一个头文件,但我认为它与这里无关。

#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;

};

我已经研究过减小终端大小、更好地计算超时等,但想确保在我承诺做某事之前我没有做任何重大错误。仅重绘 window 的一部分可能会有所帮助,但我不知道它是否可以很好地缩放?我也愿意接受任何关于诅咒的建议。

编辑:下面的新代码,在更改 redrawing/clearing/calls 的顺序后没有闪烁:

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());

  }

这个块是闪烁的主要原因:

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

}

erase 修改了整个屏幕,下面的 getch 在你的代码之前做了一个 refresh (实际上是 清除 屏幕)接下来是重新粉刷屏幕。如果您更改它的组织方式,使 erase 之后是 重新绘制 ,那么这将消除大部分屏幕更新,即 activity实际终端。