为什么 getch() 不读取最后输入的字符?
Why doesn't getch() read the last character entered?
我正在使用 ncurses
库用 C 编写一个 snake 游戏,其中屏幕每秒更新一次。玩过游戏的人都知道,如果用户输入各种键或者长按某个键,应该不会有'buffered'次按键被存储。换句话说,如果我按住 w
(向上键)并且 stdin
接收到一个 20 w
的序列,然后输入一个 d
(右键) ,我希望蛇立即向右移动,而忽略缓冲的 w
s.
我正在尝试使用 ncurses
函数 getch()
来实现此目的,但出于某种原因,我正在实现我刚才描述的不良效果;也就是说,在考虑最后按下的键之前,首先存储和处理任何长按键,正如这个 MWE 将说明的那样:
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
int main(){
char c = 'a';
initscr();
cbreak();
noecho();
for(;;){
fflush(stdin);
timeout(500);
c = getch();
sleep(1);
printw("%c", c);
}
return 0;
}
有什么方法可以修改此代码,以便 getch()
忽略任何缓冲文本?之前的 fflush()
似乎没有帮助。
首先回答您的直接问题:即使您一次阅读 stdin
一个字符,您通常也不想错过任何一个。如果 getch()
只是 return 最后输入的任何字符,使用 getch()
的输入例程将变得非常不可靠。
即使在贪吃蛇游戏中,您也确实希望输入所有字符。想象一下蛇向右移动,玩家想要通过击打 down 然后立即 left 来做一个 "u-turn"。如果您的游戏只拾取最后一个角色,蛇会直接向左移动并因此自杀。相当令人沮丧的游戏体验;)
您的问题的解决方案很简单:让您的游戏循环轮询输入比蛇移动更频繁,并维护请求的方向更改队列。我已经在 my curses-based snake game.
中做到了
这是游戏循环中的相关代码:
typedef enum
{
NONE,
LEFT,
DOWN,
RIGHT,
UP
} Dir;
// [...]
ticker_start(10);
while (1)
{
screen_refresh(screen);
ticker_wait();
key = getch();
if (key == 'q' || key == 'Q')
{
// quit game
}
switch (key)
{
case KEY_LEFT:
snake_setDir(snake, LEFT);
break;
case KEY_DOWN:
snake_setDir(snake, DOWN);
break;
case KEY_UP:
snake_setDir(snake, UP);
break;
case KEY_RIGHT:
snake_setDir(snake, RIGHT);
break;
case ' ':
// pause game
break;
}
if (!--nextStep)
{
step = snake_step(snake); // move the snake
// code to check for food, killing, ...
}
if (!--nextFood)
{
// add a new food item
}
}
ticker_stop();
下面是 snake 是如何实现和使用队列的:
struct snake
{
// queue of requested directions:
Dir dir[4];
// more properties
};
void
snake_setDir(Snake *self, Dir dir)
{
int i;
Dir p;
p = self->dir[0];
for (i = 1; i < 4; ++i)
{
if (self->dir[i] == NONE)
{
if (dir != p) self->dir[i] = dir;
break;
}
p = self->dir[i];
}
}
static void dequeueDir(Snake *self)
{
int i;
if (self->dir[1] != NONE)
{
for (i=0; i<3; ++i) self->dir[i] = self->dir[i+1];
self->dir[3] = NONE;
}
}
Step
snake_step(Snake *self)
{
dequeueDir(self);
// [...]
switch (self->dir[0])
{
case LEFT:
--newHead->x;
break;
case DOWN:
++newHead->y;
break;
case RIGHT:
++newHead->x;
break;
case UP:
--newHead->y;
break;
default:
break;
}
// [...]
}
祝你游戏顺利!
我做了更多研究并发现:
The flushinp()
routine throws away any typeahead that has been typed by the user and has not yet been read by the program.
这解决了我的问题。
下面建议的代码显示了如何忽略除最后输入的键之外的所有键。
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
int main( void )
{
char c = ' ';
char lastKey;
initscr();
cbreak();
noecho();
for(;;)
{
timeout(500);
while( (c = getch()) != EOF )
{
lastKey = c;
}
sleep(1);
if( EOF == c )
{
printw("%c", lastKey);
}
}
return 0;
}
我正在使用 ncurses
库用 C 编写一个 snake 游戏,其中屏幕每秒更新一次。玩过游戏的人都知道,如果用户输入各种键或者长按某个键,应该不会有'buffered'次按键被存储。换句话说,如果我按住 w
(向上键)并且 stdin
接收到一个 20 w
的序列,然后输入一个 d
(右键) ,我希望蛇立即向右移动,而忽略缓冲的 w
s.
我正在尝试使用 ncurses
函数 getch()
来实现此目的,但出于某种原因,我正在实现我刚才描述的不良效果;也就是说,在考虑最后按下的键之前,首先存储和处理任何长按键,正如这个 MWE 将说明的那样:
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
int main(){
char c = 'a';
initscr();
cbreak();
noecho();
for(;;){
fflush(stdin);
timeout(500);
c = getch();
sleep(1);
printw("%c", c);
}
return 0;
}
有什么方法可以修改此代码,以便 getch()
忽略任何缓冲文本?之前的 fflush()
似乎没有帮助。
首先回答您的直接问题:即使您一次阅读 stdin
一个字符,您通常也不想错过任何一个。如果 getch()
只是 return 最后输入的任何字符,使用 getch()
的输入例程将变得非常不可靠。
即使在贪吃蛇游戏中,您也确实希望输入所有字符。想象一下蛇向右移动,玩家想要通过击打 down 然后立即 left 来做一个 "u-turn"。如果您的游戏只拾取最后一个角色,蛇会直接向左移动并因此自杀。相当令人沮丧的游戏体验;)
您的问题的解决方案很简单:让您的游戏循环轮询输入比蛇移动更频繁,并维护请求的方向更改队列。我已经在 my curses-based snake game.
中做到了这是游戏循环中的相关代码:
typedef enum
{
NONE,
LEFT,
DOWN,
RIGHT,
UP
} Dir;
// [...]
ticker_start(10);
while (1)
{
screen_refresh(screen);
ticker_wait();
key = getch();
if (key == 'q' || key == 'Q')
{
// quit game
}
switch (key)
{
case KEY_LEFT:
snake_setDir(snake, LEFT);
break;
case KEY_DOWN:
snake_setDir(snake, DOWN);
break;
case KEY_UP:
snake_setDir(snake, UP);
break;
case KEY_RIGHT:
snake_setDir(snake, RIGHT);
break;
case ' ':
// pause game
break;
}
if (!--nextStep)
{
step = snake_step(snake); // move the snake
// code to check for food, killing, ...
}
if (!--nextFood)
{
// add a new food item
}
}
ticker_stop();
下面是 snake 是如何实现和使用队列的:
struct snake
{
// queue of requested directions:
Dir dir[4];
// more properties
};
void
snake_setDir(Snake *self, Dir dir)
{
int i;
Dir p;
p = self->dir[0];
for (i = 1; i < 4; ++i)
{
if (self->dir[i] == NONE)
{
if (dir != p) self->dir[i] = dir;
break;
}
p = self->dir[i];
}
}
static void dequeueDir(Snake *self)
{
int i;
if (self->dir[1] != NONE)
{
for (i=0; i<3; ++i) self->dir[i] = self->dir[i+1];
self->dir[3] = NONE;
}
}
Step
snake_step(Snake *self)
{
dequeueDir(self);
// [...]
switch (self->dir[0])
{
case LEFT:
--newHead->x;
break;
case DOWN:
++newHead->y;
break;
case RIGHT:
++newHead->x;
break;
case UP:
--newHead->y;
break;
default:
break;
}
// [...]
}
祝你游戏顺利!
我做了更多研究并发现:
The
flushinp()
routine throws away any typeahead that has been typed by the user and has not yet been read by the program.
这解决了我的问题。
下面建议的代码显示了如何忽略除最后输入的键之外的所有键。
#include <stdio.h>
#include <unistd.h>
#include <ncurses.h>
int main( void )
{
char c = ' ';
char lastKey;
initscr();
cbreak();
noecho();
for(;;)
{
timeout(500);
while( (c = getch()) != EOF )
{
lastKey = c;
}
sleep(1);
if( EOF == c )
{
printw("%c", lastKey);
}
}
return 0;
}