我正在使用ncurses
库在C中编写snake游戏,其中屏幕每秒更新一次。当玩游戏的人知道,如果用户输入各种键或长按键,则不应存储“缓冲”按键。换句话说,如果我按住w
(向上键)并且stdin
收到20 w
s的序列,然后输入d
(右键) ,我希望蛇立即向右移动,并忽略缓冲的w
。
我正在尝试使用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()
似乎没有帮助。
答案 0 :(得分:3)
首先回答您的直接问题:即使您一次从stdin
一个字符读取,您通常也不想错过一个字符。如果getch()
只返回最后输入的任何字符,则使用getch()
输入例程将变得非常不可靠。
即使在蛇游戏中,你也想要输入所有的角色。想象一下,蛇向右移动,玩家希望通过按向下进行“掉头”,然后立即离开。如果你的游戏只能拿起最后一个角色,蛇就会直接向左移动,从而自杀。相当令人沮丧的游戏体验;)
解决问题的方法很简单:让游戏循环轮询输入多比蛇移动更频繁,并维护一个请求方向更改的队列。我已经在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();
以下是蛇实现和使用队列的方式:
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;
}
// [...]
}
祝你好运!
答案 1 :(得分:0)
我做了更多研究,发现:
flushinp()
例程抛弃了用户输入的任何类型的未完成程序,但尚未被程序读取。
这解决了我的问题。
答案 2 :(得分:0)
以下建议的代码显示了如何忽略输入的最后一个键以外的所有键。
#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;
}