C 蛇在等吗

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

我正在尝试用C和N课程做一个小的蛇游戏,我发现目前最难的部分是等待。我原以为我的代码会使主循环每秒(至少每秒)进行一次,但它非常不规则,无论getch()是否重新记录某个内容,都会变得更快/更慢

...
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