Как я могу обрабатывать ввод в C, не останавливая основной цикл?

Dec 16 2020

Я хочу, чтобы моя программа на C обновляла консоль каждую секунду, и она работала нормально, пока я не попытался обработать ввод. Теперь программа останавливается, так как ожидает ввода от пользователя. Как я могу это сделать?

while(true) {
    ShowConsole();  //Show
    Sleep(1000);        //Wait
    scanf("%s",&a)      //Handle Input  
    Update();
    ClearScreen();      //Clear
}

Ответы

1 Vaillancourt Dec 17 2020 at 08:32

Я взял на себя смелость попытаться решить эту проблему сам.

Основываясь исключительно на тексте вопроса, не очень понятно, какую игру вы пытаетесь сделать, поэтому я использовал подход своего рода игры со змеей , где змея будет двигаться независимо от того, что игрок делает (или не делает). .

Я Sleepрасхаживать входной опрос и скорость перерисовки, и _kbhit()чтобы возможно прочитать характер, и clock_t/ clock()обновить игру один раз в секунду.

Теперь я не cпрограммист, поэтому не знаю, является ли этот cкод «элегантным» (вероятно, нет), но он работал на моей машине (Windows, Visual Studio).

#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <ctype.h>
#include <windows.h>

typedef int BOOL;
#define FALSE ((int)0)
#define TRUE ((int)1)

void ClearScreen()
{
  // code here that clears the screen, see https://stackoverflow.com/a/42500322
}

int main( void )
{
  BOOL run = TRUE;

  clock_t lastTickClock = clock();
  int position = 0;

  char registeredCommand = 'd'; // Command that will be effective at the next game tick.
  while ( run )
  {
    char currentCharIfAny = 0; // The char that is read this loop.
    if ( _kbhit() )
      currentCharIfAny = _getch();

    if ( currentCharIfAny == 'a' || currentCharIfAny == 'd' )
      registeredCommand = currentCharIfAny; // We only respond to 'a' or 'd'.

    clock_t newClock = clock();
    if ( ( newClock - lastTickClock ) > CLOCKS_PER_SEC )
    {
      // This is the command handling/the game tick
      if ( registeredCommand == 'a' )
        position = max( --position, 0 );
      else if ( registeredCommand == 'd' )
        position = min( ++position, 24 );

      lastTickClock = newClock;
    }

    char buffer[1024];
    buffer[0] = 0;

    for ( int i = 0; i < position; ++i )
      strcat_s( buffer, 1024, " " );
    strcat_s( buffer, 1024, "_\n" ); // This underscore represents our "agent" or "game token" or "paddle".

    // The following is only for debugging purpose; it prints the character we're currently handling. 
    if ( currentCharIfAny >= 'a' && currentCharIfAny <= 'z' )
    {
      char lbuff[2]; lbuff[0] = 0;
      sprintf_s( lbuff, 2, "%c", currentCharIfAny );
      strcat_s( buffer, 1024, lbuff );
    }

    ClearScreen();
    printf( "%s\n", buffer );
    Sleep( 1000 / 60 );
    if ( currentCharIfAny == 'q' )
      run = FALSE;
  }

  printf( "\ndone. press a key to quit." );
  _getch();
  return 0;
}

Несколько замечаний:

  • вероятно, есть другие (лучшие) способы добиться этого: сейчас, когда я обновляю (ClearScreen), экран немного «мерцает».
  • в Windows операционная система будет "ускорять" частоту повторения символа, который она отправляет в приложения, поэтому, когда вы, dнапример, dнажимаете, программа покажет, что вы нажимаете , затем она покажет, что вы не нажимаете ни одной клавиши, затем он покажет, что вы dснова бьете , пока вы не отпустите клавишу.
  • как и ваша собственная реализация, он не переносится из-за характера используемых функций.
AliTeo Dec 16 2020 at 05:28

Хотя мне пришлось использовать непереносимый код, я заставил его работать без необходимости в сторонней библиотеке. Также оказывается, что функция _kbhit () задерживает буфер, поэтому вам не нужно ждать 1 секунду, чтобы ввести полную строку. Однако записанная строка обрезалась, если она не записывалась за секунду. Я также показал это в функции ShowConsole ().

Примечание. Это нестандартный, непереносимый код. Работает в моей ОС (Windows).

char key;
    if(_kbhit()){
        key = _getch();
        if(key == 13){                  //If Enter key is pressed
            HandleInput(moveInput);     //handle the input string
            memset(moveInput, 0, 6);    //clear input string
            moveIndex=0;                //reset string index to 0
        }
        moveInput[moveIndex] = key;     //If any other key is pressed, add the hit character to string input
        moveIndex++;
    }

В функции ShowConsole ():

ShowConsole(){
    .
    .
    .
    printf("%s", moveInput); // At the end of showing the graphics, also show the input that is being written.
}