c++ - 如何减少诅咒的闪烁/滞后?
问题描述
目前正在尝试解决我正在使用 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());
}
解决方案
这个块是闪烁的主要贡献者:
erase();
if ((ch = getch()) == ERR) {
drawEntity(player);
}
erase
修改整个屏幕,然后在您的代码通过重新绘制屏幕进行跟进之前getch
执行refresh
(实际上是清除屏幕)。如果您更改它的组织方式,以便重新绘制erase
之后,则消除了大部分屏幕更新,即实际终端上的活动很少。
推荐阅读
- elixir - IEx.Helper.h 的终端友好输出,同时输出到非 TTY 的东西
- javascript - 如何修复错误:找不到资源 android:style/TextAppearance.Material?
- regex - VB.NET MAC OUI 查找
- c# - 如何在运行时通过 CacheManager 修改加载的配置
- vba - 使用双常量作为对象访问 2016 子窗体 VBA?
- android - 从单独的进程(例如同步)访问 GreenDao
- android - 如何将 png 直接转换为 android 矢量绘图?
- matlab - Matlab 中水体的 OMP 分析,“索引超过矩阵维度。”
- regex - 如何表达不接受任何内容的正则表达式
- django-rest-framework - REST framework 中单个模型的多个端点