Bagaimana cara menangani input dalam C tanpa menghentikan loop utama?

Dec 16 2020

Saya ingin program C saya memperbarui konsol setiap detik, dan itu berfungsi dengan baik sampai saya mencoba menangani masukan. Sekarang program berhenti karena menunggu masukan dari pengguna. Bagaimana saya bisa melakukan ini?

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

Jawaban

1 Vaillancourt Dec 17 2020 at 08:32

Saya mengambil kebebasan untuk mencoba dan menangani ini sendiri.

Hanya berdasarkan teks pertanyaan, tidak terlalu jelas jenis permainan apa yang Anda coba lakukan, jadi saya mengambil pendekatan semacam permainan ular , di mana ular akan bergerak, apa pun yang pemain lakukan (atau tidak lakukan) .

Saya menggunakan Sleepmondar-mandir di polling input dan tingkat redraw, dan _kbhit()untuk mungkin membaca karakter, dan clock_t/ clock()memperbarui permainan sekali per detik.

Sekarang saya bukan seorang cprogrammer, jadi saya tidak tahu apakah ckode ini "elegan" (mungkin tidak), tetapi berfungsi pada mesin saya (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;
}

Beberapa hal yang perlu diperhatikan:

  • mungkin ada cara lain (lebih baik) untuk mencapai ini: untuk saat ini, ketika saya menyegarkan (ClearScreen), layar "berkedip" sedikit.
  • pada Windows, OS akan "memacu" tingkat pengulangan karakter yang dikirim ke aplikasi, jadi ketika Anda menekan dmisalnya, program akan menunjukkan bahwa Anda menekan d, kemudian akan menunjukkan bahwa Anda tidak menekan tombol, maka itu akan menunjukkan Anda menekan dlagi, sampai Anda melepaskan kunci.
  • seperti penerapan Anda sendiri, ini tidak portabel karena sifat fungsi yang digunakan.
AliTeo Dec 16 2020 at 05:28

Meskipun saya harus menggunakan kode non-portabel, saya membuatnya berfungsi tanpa memerlukan perpustakaan pihak ketiga. Juga ternyata fungsi _kbhit () menunda buffer sehingga Anda tidak perlu menunggu 1 detik untuk memasukkan string lengkap. Namun string tertulis itu dipotong jika tidak ditulis dalam sedetik. Jadi saya juga menunjukkannya di fungsi ShowConsole ().

Catatan: Ini adalah kode non-standar, non-portabel. Bekerja di OS saya (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++;
    }

Dalam Fungsi ShowConsole ():

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