如何减少 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实际终端。
目前正在尝试解决我用 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实际终端。