Jak mogę obsłużyć dane wejściowe w C bez zatrzymywania głównej pętli?

Dec 16 2020

Chcę, aby mój program w C aktualizował konsolę co sekundę i działał dobrze, dopóki nie spróbowałem obsłużyć danych wejściowych. Teraz program zatrzymuje się, ponieważ oczekuje na dane wejściowe od użytkownika. Jak mogę to zrobić?

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

Odpowiedzi

1 Vaillancourt Dec 17 2020 at 08:32

Pozwoliłem sobie samemu się tym zająć.

Opierając się wyłącznie na tekście pytania, nie jest bardzo jasne, jaką grę próbujesz zrobić, więc przyjąłem podejście do gry w węża , w której wąż będzie się poruszał, cokolwiek gracz robi (lub nie robi) .

Kiedyś Sleepchodzić odpytywanie wejściowy i szybkość odświeżania, a _kbhit()do może odczytać charakter i clock_t/ clock()zaktualizować grę raz na sekundę.

Teraz nie jestem cprogramistą, więc nie wiem, czy ten ckod jest „elegancki” (chyba nie), ale zadziałał na moim komputerze (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;
}

Kilka uwag:

  • są prawdopodobnie inne (lepsze) sposoby osiągnięcia tego: na razie, kiedy odświeżam (ClearScreen), ekran trochę "migocze".
  • w systemie Windows system operacyjny będzie „przyspieszał” tempo powtarzania znaku, który wysyła do aplikacji, więc gdy dna przykład naciśniesz , program pokaże, że uderzasz d, a następnie pokaże, że nie naciskasz żadnego klawisza, wtedy pokaże, że uderzasz dponownie, dopóki nie zwolnisz klawisza.
  • podobnie jak Twoja własna implementacja, nie jest przenośna ze względu na charakter używanych funkcji.
AliTeo Dec 16 2020 at 05:28

Chociaż musiałem użyć kodu nieprzenośnego, udało mi się go uruchomić bez potrzeby biblioteki innej firmy. Okazuje się również, że funkcja _kbhit () opóźnia buforowanie, więc nie trzeba czekać 1 sekundę na wprowadzenie pełnego ciągu. Jednak napisany ciąg był odcinany, jeśli nie został zapisany w ciągu sekundy. Więc pokazałem to również w funkcji ShowConsole ().

Uwaga: to jest kod niestandardowy, nieprzenośny. Działa w moim systemie operacyjnym (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++;
    }

W funkcji ShowConsole ():

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