如何减少NCurses C应用程序中的输入延迟

时间:2016-03-25 07:38:10

标签: c loops input delay ncurses

当我运行我的应用程序时,我得到了大量的输入延迟。

更多细节: 当我按下' a' a' s'' d' (我指定的输入键)对象移动但是在释放键后它会继续移动很长一段时间。源代码如下,但是代码的一小部分已经被删除以缩短问题,但是如果下面的源代码没有编译,我将所有代码都放在github上。 https://github.com/TreeStain/DodgeLinuxGame.git谢谢你的时间。 -Tristan

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。请随意批评我的方法和代码,因为我在编程方面相当新,并且可以接受我能得到的所有批评。

2 个答案:

答案 0 :(得分:4)

我认为原因是因为getch()一次只会释放一个输入字符(即使输入流中有很多排队),所以如果排队的速度比你快,那就删除&#39 ;它们来自流,循环将继续,直到队列清空,即使你释放密钥。此外,您还想(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()将返回ERR。 while(getch() != ERR) {}做的是继续调用getch()直到从队列中删除所有缓冲的输入字符,然后getch()返回ERR并继续。然后循环应该睡眠~17ms并重复。这些行应该强制循环只计算&#39;每隔约17毫秒一次按键,并且不会经常发生。

请参阅:http://linux.die.net/man/3/getch

答案 1 :(得分:1)

Ncurses没有单独检测按键和按键释放。在持有钥匙时不能移动物体,在释放钥匙后立即停止。

您观察到的现象源于两个因素的影响:自动重复键盘和缓冲键盘驱动程序。也就是说,用户持有一个密钥,这会生成大量的密钥事件,并且它们由驱动程序缓冲并在请求按键时提供给您的应用程序。

驱动程序和键盘自动重复功能都不受您的应用程序控制。您唯一希望实现的是处理关键事件的速度比从键盘输出的速度快。如果你想这样做,你必须摆脱主循环中的napms并在帧重绘之间处理按键。有很多方法可以做到这一点,但最直接的方法是使用timeout函数。

 timeout (timeToRefresh);
 ch = getch();
 if (ch == ERR) refresh();
 else processKey(ch);

每次使用实时时钟都需要计算timeToRefresh。