C 蛇在等吗
我正在尝试用C和N课程做一个小的蛇游戏,我发现目前最难的部分是等待。我原以为我的代码会使主循环每秒(至少每秒)进行一次,但它非常不规则,无论getch()是否重新记录某个内容,都会变得更快/更慢C 蛇在等吗,c,ncurses,C,Ncurses,我正在尝试用C和N课程做一个小的蛇游戏,我发现目前最难的部分是等待。我原以为我的代码会使主循环每秒(至少每秒)进行一次,但它非常不规则,无论getch()是否重新记录某个内容,都会变得更快/更慢 ... timeout(1000); ... while(1) { if(!prey) { prey = TRUE; create_prey(s, map); } clear(); draw_map(map, s); ref
...
timeout(1000);
...
while(1)
{
if(!prey)
{
prey = TRUE;
create_prey(s, map);
}
clear();
draw_map(map, s);
refresh();
clock_gettime(CLOCK_REALTIME, &start);
flushinp();
c = getch();
clock_gettime(CLOCK_REALTIME, &end);
if(end.tv_nsec - start.tv_nsec < 1E9)
{
wait.tv_sec = end.tv_sec - start.tv_sec;
wait.tv_nsec = 1E9 - (end.tv_nsec - start.tv_nsec);
nanosleep(&wait, &wait);
}
move_snake(s, c);
}
。。。
超时(1000);
...
而(1)
{
如果(!猎物)
{
猎物=真;
创建_猎物(s,map);
}
清除();
绘制地图;
刷新();
时钟获取时间(时钟实时和启动);
flushinp();
c=getch();
时钟获取时间(时钟实时和结束);
如果(end.tv\u nsec-start.tv\u nsec<1E9)
{
wait.tv_sec=end.tv_sec-start.tv_sec;
wait.tv\u nsec=1E9-(end.tv\u nsec-start.tv\u nsec);
奈米睡眠(&等待,&等待);
}
移走蛇(s,c);
}
我不会详细介绍游戏功能,因为这些功能运行良好。唯一的问题是睡眠部分。我看不出哪里会失败
感谢您的关注。此版本采用稍微不同的方法:
move\u snake()
必须为c
做好准备,以使其成为ERR
,表示没有输入,但这实际上不是新的约束。还要注意,nanosleep()
只保证睡眠的最短持续时间,而不是确切的持续时间;这将使任何基于该函数(或基于sleep()
)的实现都可能有点不规则
timeout(0);
wait.tv_sec = 0;
while(1)
{
clock_gettime(CLOCK_REALTIME, &start);
wait.tv_nsec = 1000000000 - start.tv_nsec;
nanosleep(&wait, &wait);
if(!prey)
{
prey = TRUE;
create_prey(s, map);
}
clear();
draw_map(map, s);
refresh();
c = getch();
flushinp();
move_snake(s, c);
}
//此代码块:
如果(end.tv\u nsec-start.tv\u nsec<1E9)
{
wait.tv_sec=end.tv_sec-start.tv_sec;
wait.tv\u nsec=1E9-(end.tv\u nsec-start.tv\u nsec);
奈米睡眠(&等待,&等待);
}
包含逻辑缺陷。
具体来说,如果开始时间为(例如)
5.9秒
结束时间是(例如)
7.1秒
然后代码会认为不到1秒
已经过去了,,
其中(实际上)1.2秒已经过去
因此,代码将设置一个纳秒睡眠时间
代码应该做的是检查经过的秒数和
nsec系列,类似于:
//注意:以下假设时间为无符号值
#定义一秒(1E9)
如果((end.tv_sec-start.tv_sec)=0)//未跨越秒边界
||//或--
(((end.tv_sec-start.tv_sec)=1)//未跨越秒边界>一次
&&//及
(end.tv\u nsecstart.tv_秒)
{//然后仍然在同一秒边界内
//计算此秒的剩余分数+分数时间到下一秒的分数
wait.tv\u nsec=(一秒-end.tv\u nsec)+start.tv\u nsec;
}
其他的
{//其他人已越过sec边界
//开始-结束之间的计算分数秒时间
wait.tv\u nsec=start.tv\u nsec-end.tv\u nsec);
}//如果结束,则结束
如果(0
如果不按任何键怎么办?您是否在无延迟模式下运行?如果getch
阻止等待输入,屏幕似乎将停止更新。如果使用无延迟模式,我看不到对c==ERR
的任何检查,除非move\u snake
检查是否存在此问题。getch()无法因超时而不精确地阻止。但是,是的,ERR是由move_snake()处理的。这些其他函数运行需要多长时间?如果将第一个clock\u gettime
移动到if
语句和move\u snake
之间,会怎么样?然后,您需要在while循环之前再加一个clock\u gettime
。但是那样的话,在时间计算中除了if
语句之外,您将包括所有内容。而且,这几乎肯定不是问题所在,但是CLOCK\u MONOTONIC
可能比CLOCK\u REALTIME
更好。我尝试将第一个gettime放在if之前,但它没有做任何事情。你的计时来源太多了。执行非阻塞读取,而不是使用读取超时,并通过nanosleep()
执行所有刻度之间的延迟。另外,将所有可能的内容都封装在循环的计时部分,因为不能确定所有其他函数(draw\u map()
,move\u snake()
,…)的执行时间可以忽略不计。只需一个clock\u gettime
就可以轻松完成。如果你想变得偏执,你可以把nanosleep
放在一个循环中,以防它被一个信号打断:while(nanosleep(&wait,&wait)=-1&&errno==EINTR){}
。而且clock\u gettime
可以使用clock\u monotional
,以防ntpd
或其他决定扰乱系统时间的东西。的确,这要优雅得多。只需在输入后再绘制,就可以了。可惜你不能用这种方法提高速度。谢谢你,我今晚会睡得更好。不过,使用nodelay更合适。
// this code block:
if(end.tv_nsec - start.tv_nsec < 1E9)
{
wait.tv_sec = end.tv_sec - start.tv_sec;
wait.tv_nsec = 1E9 - (end.tv_nsec - start.tv_nsec);
nanosleep(&wait, &wait);
}
contains a logic flaw.
specifically, if the start time was (for example)
5.9 seconds
and the end time was (for example)
7.1 seconds
then the code would think that less than 1 second
has elapsed,
where (in reality) 1.2 seconds have elapsed
Therefore the code would setup a nanosleep time
what the code should do is check both the elapsed seconds AND
the elapsed nsec, in series, similar to:
// NOTE: following assumes times are unsigned values
#define ONE_SEC (1E9)
if( ((end.tv_sec - start.tv_sec) == 0) // not crossed sec boundary
|| // --or--
( ( (end.tv_sec - start.tv_sec) == 1) // not crossed sec boundary > once
&& // and
( end.tv_nsec < start.tv_nsec ) // end fraction not yet crossed start fraction
)
)
{ // then one second not yet elapsed
wait.tv_sec = 0; // not going to wait over 1 sec
if( end.tv_sec > start.tv_sec )
{ // then still in same sec boundary
// calc fraction remainder of this second + fraction time into next sec
wait.tv_nsec = (ONE_SEC - end.tv_nsec) + start.tv_nsec;
}
else
{ // else have crossed sec boundary
// calc fraction sec time between start-end
wait.tv_nsec = start.tv_nsec - end.tv_nsec);
} // end if
if( 0 < wait.tv_nsec )
{ // then, need to wait a while
nanosleep(&wait, NULL);
} // end if
} // end if