¿Cómo puedo manejar la entrada en C sin detener el bucle principal?

Dec 16 2020

Quiero que mi programa C actualice la consola cada segundo, y funcionaba bien hasta que intenté manejar la entrada. Ahora el programa se detiene ya que espera una entrada del usuario. ¿Cómo puedo hacer esto?

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

Respuestas

1 Vaillancourt Dec 17 2020 at 08:32

Me tomé la libertad de intentar abordar esto yo mismo.

Basándome únicamente en el texto de la pregunta, no está muy claro qué tipo de juego estás tratando de hacer, así que tomé el enfoque de una especie de juego de serpientes , donde la serpiente se moverá, lo que sea que el jugador esté haciendo (o no haciendo) .

He utilizado Sleepa pasearse por la entrada de votación y la velocidad de redibujado, y _kbhit()que tal vez leído un carácter, y el clock_t/ clock()a actualizar el juego una vez por segundo.

Ahora no soy cprogramador, así que no sé si este ccódigo es "elegante" (probablemente no lo sea), pero funcionó en mi máquina (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;
}

Un par de cosas a anotar:

  • Probablemente hay otras (mejores) formas de lograr esto: por ahora, cuando actualizo (ClearScreen), la pantalla "parpadea" un poco.
  • en Windows, el sistema operativo "acelerará" la tasa de repetición del carácter que envía a las aplicaciones, por lo que cuando presionas, dpor ejemplo, el programa mostrará que estás presionando d, luego mostrará que no presionas ninguna tecla, luego mostrará que está presionando dnuevamente, hasta que suelte la tecla.
  • al igual que su propia implementación, no es portátil debido a la naturaleza de las funciones utilizadas.
AliTeo Dec 16 2020 at 05:28

Aunque tuve que usar un código no portátil, lo hice funcionar sin la necesidad de una biblioteca de terceros. También resulta que la función _kbhit () retrasa el búfer, por lo que no es necesario esperar 1 segundo para ingresar la cadena completa. Sin embargo, la cadena escrita se cortaba si no se escribía en un segundo. Así que también lo mostré en la función ShowConsole ().

Nota: Este es un código no estándar ni portátil. Funciona en mi sistema operativo (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++;
    }

En la función ShowConsole ():

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