如何减少 NCurses C 应用程序中的输入延迟
How do I reduce input lag in an NCurses C Application
当我 运行 我的应用程序时,我遇到了大量的输入延迟。
更多详情:
当我按下 'w'、'a'、's'、'd'(我分配的输入键)时,对象会移动,但在键按下后它会继续移动很长一段时间被释放。源代码在下面,但是为了缩短问题,已经删除了一小部分代码,但是如果下面的源代码不能编译,我将所有代码都放在 github 上。
https://github.com/TreeStain/DodgeLinuxGame.git 谢谢你的时间。 -特里斯坦
dodge.c:
#define ASPECT_RATIO_X 2
#define ASPECT_RATIO_Y 1
#define FRAMES_PER_SECOND 60
#include <ncurses.h>
#include "object.h"
#include "render.h"
int main()
{
initscr();
cbreak();
noecho();
nodelay(stdscr, 1);
object objs[1];
object colObj; colObj.x = 10; colObj.y = 6;
colObj.w = 2; colObj.h = 2;
colObj.sprite = '*';
colObj.ySpeed = 1;
colObj.xSpeed = 1;
objs[0] = colObj;
//halfdelay(1);
while (1)
{
char in = getch();
if (in == 'w')
objs[0].y -= objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 's')
objs[0].y += objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 'a')
objs[0].x -= objs[0].xSpeed * ASPECT_RATIO_X;
if (in == 'd')
objs[0].x += objs[0].xSpeed * ASPECT_RATIO_X;
render(objs, 1);
napms(FRAMES_PER_SECOND);
}
getch();
endwin();
return 0;
}
render.h:
void render(object obj[], int objectNum);
void render(object obj[], int objectNum) //Takes array of objects and prints them to screen
{
int x, y, i, scrWidth, scrHeight;
getmaxyx(stdscr, scrHeight, scrWidth); //Get terminal height and width
for (y = 0; y < scrHeight; y++)
{
for (x = 0; x < scrWidth; x++)
{
mvprintw(y, x, " ");
}
}
for (i = 0; i < objectNum; i++)
{
int xprint = 0, yprint = 0;
for (yprint = obj[i].y; yprint < obj[i].y + (obj[i].h * ASPECT_RATIO_Y); yprint++)
{
for (xprint = obj[i].x; xprint < obj[i].x + (obj[i].w * ASPECT_RATIO_X); xprint++)
mvprintw(yprint, xprint, "%c", obj[i].sprite);
}
}
refresh();
}
object.h:
typedef struct
{
int x, y, w, h, ySpeed, xSpeed;
char sprite;
}object;
P.S。请随意批评我的方法和代码,因为我在编程方面还很新,可以接受所有我能得到的批评。
我相信原因是因为 getch() 一次只会释放一个输入字符(即使输入流中有很多排队)所以如果他们排队比你快 'remove'他们从流中,循环将继续,直到队列被清空,即使在您释放密钥后也是如此。此外,您需要 (1000 / FRAMES_PER_SECOND) 以获得所需的毫秒延迟时间(这会每秒创建 60 帧)。
改为在您的 while 循环中试试这个。
while (1)
{
char in;
/* We are ready for a new frame. Keep calling getch() until we hear a keypress */
while( (in = getch()) == ERR) {}
if (in == 'w')
objs[0].y -= objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 's')
objs[0].y += objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 'a')
objs[0].x -= objs[0].xSpeed * ASPECT_RATIO_X;
if (in == 'd')
objs[0].x += objs[0].xSpeed * ASPECT_RATIO_X;
render(objs, 1);
/* Clear out any other characters that have been buffered */
while(getch() != ERR) {}
napms(1000 / FRAMES_PER_SECOND);
}
从循环的顶部开始:while( (in = getch()) == ERR) {}
将快速调用 getch() 直到检测到按键。如果未检测到按键,getch() 将 return 出错。
while(getch() != ERR) {}
所做的是继续调用 getch() 直到所有缓冲的输入字符都从队列中删除,然后 getch() returns ERR 并继续前进。然后循环应该休眠 ~17ms 并重复。这些行应强制循环每 ~17 毫秒仅 'count' 一次按键,并且不会超过此频率。
Ncurses 不单独检测按键和按键释放。按住键时不能移动物体,松开后立即停止。
您观察到的现象是由两个因素共同作用的结果:自动重复键盘和缓冲键盘驱动程序。也就是说,用户按住一个键,这会产生大量的键事件,它们由驱动程序缓冲并在它要求按键时提供给您的应用程序。
驱动程序和键盘自动重复功能均不受您的应用程序控制。您唯一希望实现的是处理按键事件的速度比它们从键盘发出的速度更快。如果你想这样做,你必须在你的主循环中去掉 napms
并在帧重绘之间处理按键。有很多方法可以做到这一点,但最直接的是使用 timeout
函数。
timeout (timeToRefresh);
ch = getch();
if (ch == ERR) refresh();
else processKey(ch);
您需要使用实时时钟计算每次刷新时间。
当我 运行 我的应用程序时,我遇到了大量的输入延迟。
更多详情: 当我按下 'w'、'a'、's'、'd'(我分配的输入键)时,对象会移动,但在键按下后它会继续移动很长一段时间被释放。源代码在下面,但是为了缩短问题,已经删除了一小部分代码,但是如果下面的源代码不能编译,我将所有代码都放在 github 上。 https://github.com/TreeStain/DodgeLinuxGame.git 谢谢你的时间。 -特里斯坦
dodge.c:
#define ASPECT_RATIO_X 2
#define ASPECT_RATIO_Y 1
#define FRAMES_PER_SECOND 60
#include <ncurses.h>
#include "object.h"
#include "render.h"
int main()
{
initscr();
cbreak();
noecho();
nodelay(stdscr, 1);
object objs[1];
object colObj; colObj.x = 10; colObj.y = 6;
colObj.w = 2; colObj.h = 2;
colObj.sprite = '*';
colObj.ySpeed = 1;
colObj.xSpeed = 1;
objs[0] = colObj;
//halfdelay(1);
while (1)
{
char in = getch();
if (in == 'w')
objs[0].y -= objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 's')
objs[0].y += objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 'a')
objs[0].x -= objs[0].xSpeed * ASPECT_RATIO_X;
if (in == 'd')
objs[0].x += objs[0].xSpeed * ASPECT_RATIO_X;
render(objs, 1);
napms(FRAMES_PER_SECOND);
}
getch();
endwin();
return 0;
}
render.h:
void render(object obj[], int objectNum);
void render(object obj[], int objectNum) //Takes array of objects and prints them to screen
{
int x, y, i, scrWidth, scrHeight;
getmaxyx(stdscr, scrHeight, scrWidth); //Get terminal height and width
for (y = 0; y < scrHeight; y++)
{
for (x = 0; x < scrWidth; x++)
{
mvprintw(y, x, " ");
}
}
for (i = 0; i < objectNum; i++)
{
int xprint = 0, yprint = 0;
for (yprint = obj[i].y; yprint < obj[i].y + (obj[i].h * ASPECT_RATIO_Y); yprint++)
{
for (xprint = obj[i].x; xprint < obj[i].x + (obj[i].w * ASPECT_RATIO_X); xprint++)
mvprintw(yprint, xprint, "%c", obj[i].sprite);
}
}
refresh();
}
object.h:
typedef struct
{
int x, y, w, h, ySpeed, xSpeed;
char sprite;
}object;
P.S。请随意批评我的方法和代码,因为我在编程方面还很新,可以接受所有我能得到的批评。
我相信原因是因为 getch() 一次只会释放一个输入字符(即使输入流中有很多排队)所以如果他们排队比你快 'remove'他们从流中,循环将继续,直到队列被清空,即使在您释放密钥后也是如此。此外,您需要 (1000 / FRAMES_PER_SECOND) 以获得所需的毫秒延迟时间(这会每秒创建 60 帧)。
改为在您的 while 循环中试试这个。
while (1)
{
char in;
/* We are ready for a new frame. Keep calling getch() until we hear a keypress */
while( (in = getch()) == ERR) {}
if (in == 'w')
objs[0].y -= objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 's')
objs[0].y += objs[0].ySpeed * ASPECT_RATIO_Y;
if (in == 'a')
objs[0].x -= objs[0].xSpeed * ASPECT_RATIO_X;
if (in == 'd')
objs[0].x += objs[0].xSpeed * ASPECT_RATIO_X;
render(objs, 1);
/* Clear out any other characters that have been buffered */
while(getch() != ERR) {}
napms(1000 / FRAMES_PER_SECOND);
}
从循环的顶部开始:while( (in = getch()) == ERR) {}
将快速调用 getch() 直到检测到按键。如果未检测到按键,getch() 将 return 出错。
while(getch() != ERR) {}
所做的是继续调用 getch() 直到所有缓冲的输入字符都从队列中删除,然后 getch() returns ERR 并继续前进。然后循环应该休眠 ~17ms 并重复。这些行应强制循环每 ~17 毫秒仅 'count' 一次按键,并且不会超过此频率。
Ncurses 不单独检测按键和按键释放。按住键时不能移动物体,松开后立即停止。
您观察到的现象是由两个因素共同作用的结果:自动重复键盘和缓冲键盘驱动程序。也就是说,用户按住一个键,这会产生大量的键事件,它们由驱动程序缓冲并在它要求按键时提供给您的应用程序。
驱动程序和键盘自动重复功能均不受您的应用程序控制。您唯一希望实现的是处理按键事件的速度比它们从键盘发出的速度更快。如果你想这样做,你必须在你的主循环中去掉 napms
并在帧重绘之间处理按键。有很多方法可以做到这一点,但最直接的是使用 timeout
函数。
timeout (timeToRefresh);
ch = getch();
if (ch == ERR) refresh();
else processKey(ch);
您需要使用实时时钟计算每次刷新时间。