首页 > 解决方案 > 如何减少诅咒的闪烁/滞后?

问题描述

目前正在尝试解决我正在使用 C++ 在 ncurses 中制作的一个非常小的游戏中闪烁的问题。闪烁并不可恶,但我可以想象当更多实体同时被渲染/移动时它会更加烦人。代码如下。还有一个头文件,但我不相信它在这里相关。

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

  }

标签: c++ncurses

解决方案


这个块是闪烁的主要贡献者:

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

}

erase修改整个屏幕,然后在您的代码通过重新绘制屏幕进行跟进之前getch执行refresh(实际上是清除屏幕)。如果您更改它的组织方式,以便重新绘制erase之后,则消除了大部分屏幕更新,即实际终端上的活动很少。


推荐阅读