Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Juego en modo consola (C++) problemas con pulsación de tecla

Iniciado por J_F_NASH, 26 de Marzo de 2008, 08:05:12 AM

« anterior - próximo »

J_F_NASH

Utilizo getch() para leer el caracter pulsado:
switch(getch()){
case 77: //derecha.

break;
case 75: //"o" izquierda.

break;
}

El problema es que detiene el juego a la espera de que pulses una tecla. ¿Puede evitarse esto de alguna forma?


S2.

Prompt

Sip!


void main( )
{
   bool bExit = false;
   while( !bExit )
   {
       while ( !_kbhit( ) )
       {
           update( );  
       }

       ch = getch( );
       switch( ch )
       {
           case 'q':
           {
               bExit = true;
           }
           break;
       }
   }
}


Así mismo!

PD: #include <conio.h>

LC0


davur

No existe manera estándar ni en C ni en C++ de interactuar con un teclado más allá de las primitivas básicas proporcionadas por sus respectivas librerías (estándar). De hecho, ninguno de los dos requiere la existencia de un teclado en el sistema.

Tei

Este tema en concreto lo tengo investigado a fondo.

Mi conclusion es:

Aunque sea sorprendente, no hay una forma estandar de hacer esto, es imposible escribir un codigo en C++ que valga para todas las plataformas.

Por tanto, usa conio.h si lo tienes disponible sin ningun miedo.  Si quieres, aislalo en un fichero   entrada_win.cpp  y ten otro entrada_linux.cpp y otro entrada_mac.cpp, es todo lo mas que puedes hacer.

nota: valla, me ha ganado Davur por unos segundos :D


jalbam

Hola, buenas.

Para Linux o para Mac OS X y otros sistemas, puedes utilizar la biblioteca ncurses (a veces denominada curses) que substituye a conio.h

Los problemas que puedes encontrar es si no encuentra la biblioteca, aún teniendola instalada (prueba a cambiar ncurses.h a curses.h o al revés) y, sobretodo, la "ofuscada" línea que se necesita escribir para compilar el programa en GCC. Lo siento pero no la recuerdo.

En mi web tengo un ejemplo de un pong algo cutre programado en ANSI C (o se le acerca). Pude compilarlo en Linux y en Mac OS X sólo descomentando las lineas necesarias y cambiando alguna cosa, como el "cls" por "clear", etc. El código fuente está ya pensado y preparado para ser cambiado facilmente para compilar en otros sistemas. Puede darte algunos problemas con el "\n" y cosillas que son muy fádciles de solucionar (yo lo hice, pero siento no tener las versiones modificadas. Las hice para hacer las capturas de pantalla y las borré). No recuerdo muchas cosas, pero una de ellas creo que era que ponía más lineas de las posibles para la shell. Prueba a reducir la variable del alto del tablero, en el código fuente). El "juego" se llama Pongetet y puedes encontrarlo en la sección de juegos, en C. Esta es la dirección: http://www.granvino.com/jam

Te puede servir de ejemplo de cómo funciona ncurses, y espero que así sea.

Saludos :)
-----
Juan Alba Maldonado


Tei

Cita de: "zupervaca"Mas facil aun es usar el estandar: http://www.cplusplus.com/reference/clibrary/cstdio/getchar.html

getchar bloquea el proceso hasta que se pulsa una tecla, esto es un problema hasta para el juego mas sencillo, donde pretendes salir del bucle de recepcion de datos si.. bueno, no hay datos :D y continuar con cosas como render y tal.

LC0

Matizando lo que dice Tei, puedes darle a quinientas teclas que no va a pasar nada de nada hasta que no pulses intro o el carácter de fin de entrada, en general.

El tema de getchar es que te coge los carácteres de la entrada estándar, teniendo que indicar claramente cuando has terminado que introducir datos. Y esto es así para todas las funciones que maman de la misma entrada: getchar, gets, fscanf, etc. Funciones que no tienen porque tomar solo un caracter para satisfacer el capricho de su hermano pródigo getchar.

zupervaca

Hola,

esta es la funcion de la libconio para linux

int getch()
{
       struct termios t;
       int c;

       tcgetattr(0,&t);
       t.c_lflag&=~ECHO+~ICANON;
       tcsetattr(0,TCSANOW,&t);
       fflush(stdout);
       c=getchar();
       t.c_lflag|=ICANON+ECHO;
       tcsetattr(0,TCSANOW,&t);
       return c;
}

La libreria completa esta aqui: http://sourceforge.net/projects/libconio/
getchar en windows es lo que indicais, pero en linux las funciones de termios nos ayudan un pelin ;)
Por desgracia la funcion _kbhit no existe en esta libreria :(

Saludos

Tei

Varias formas de hacerlo en Quake.

ezQuake (WIN), mi preferida, no la mejor que he visto:


/*
================
Sys_ConsoleInput
================
*/
char *Sys_ConsoleInput (void)
{
static char text[256];
static int len;
int c;

// read a line out
while (_kbhit())
{
c = _getch();

if (c == 224) {
if (_kbhit()) {
// assume escape sequence (arrows etc), skip
_getch();
continue;
}
// assume character
}

if (c < 32 && c != '\r' && c != 8)
continue;

putch (c);
if (c == '\r')
{
text[len] = 0;
putch ('\n');
len = 0;
return text;
}
if (c == 8)
{
if (len)
{
putch (' ');
putch (c);
len--;
text[len] = 0;
}
continue;
}

text[len] = c;
len++;
text[len] = 0;
if (len == sizeof(text))
len = 0;
}

return NULL;
}


Half-Life (windoze y linux)

/*
================
Sys_ConsoleInput

================
*/
#ifdef _WIN32
char *Sys_ConsoleInput (void)
{
INPUT_RECORD recs[1024];
unsigned long dummy;
int ch;
unsigned long numread, numevents;

while ( 1 )
{
if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
{
exit( -1 );
}

if (numevents <= 0)
break;

if ( !ReadConsoleInput(hinput, recs, 1, &numread) )
{
exit( -1 );
}

if (numread != 1)
{
exit( -1 );
}

if ( recs[0].EventType == KEY_EVENT )
{
if ( !recs[0].Event.KeyEvent.bKeyDown )
{
ch = recs[0].Event.KeyEvent.uChar.AsciiChar;
switch (ch)
{
case '\r':
WriteFile(houtput, "\r\n", 2, &dummy, NULL);
if (console_textlen)
{
console_text[console_textlen] = 0;
console_textlen = 0;
return console_text;
}
break;

case '\b':
if (console_textlen)
{
console_textlen--;
WriteFile(houtput, "\b \b", 3, &dummy, NULL);
}
break;

default:
if (ch >= ' ')
{
if (console_textlen < sizeof(console_text)-2)
{
WriteFile(houtput, &ch, 1, &dummy, NULL);
console_text[console_textlen] = ch;
console_textlen++;
}
}

break;

}
}
}
}

return NULL;
}
#else
char *Sys_ConsoleInput(void)
{
struct timeval tvTimeout;
fd_set fdSet;
unsigned char ch;

FD_ZERO( &fdSet );
FD_SET( STDIN_FILENO, &fdSet );

tvTimeout.tv_sec        = 0;
tvTimeout.tv_usec       = 0;

if ( select( 1, &fdSet, NULL, NULL, &tvTimeout ) == -1 || !FD_ISSET( STDIN_FILENO, &fdSet ) )
return NULL;

console_textlen = 0;

// We're going to shove a newline onto the end of the input later in
// ProcessConsoleInput(), so only accept 254 chars instead of 255.    -LH

while ( read( STDIN_FILENO, &ch, 1 ) )
{
if ( ( ch == 10 ) || ( console_textlen == 254 ) )
{
// For commands longer than we can accept, consume the remainder of the input buffer
if ( ( console_textlen == 254 ) && ( ch != 10 ) )
{
while ( read( STDIN_FILENO, &ch, 1 ) )
{
if ( ch == 10 )
break;
}
}

//Null terminate string and return
console_text[ console_textlen ] = 0;
console_textlen = 0;
return console_text;
}

console_text[ console_textlen++ ] = ch;
}

return NULL;
}
#endif


Quake2 (BEOS)

char *Sys_ConsoleInput(void) {

 // we use this when sending back commands
 static char text[256];
 int i;
 int avail;
 char key;
 field_t *history;

 if (ttycon && ttycon->value)
 {
   avail = read(0, &key, 1);
   if (avail != -1)
   {
     // we have something
     // backspace?
     // NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere
     if ((key == tty_erase) || (key == 127) || (key == 8))
     {
       if (tty_con.cursor > 0)
       {
         tty_con.cursor--;
         tty_con.buffer[tty_con.cursor] = '\0';
         tty_Back();
       }
       return NULL;
     }
     // check if this is a control char
     if ((key) && (key) < ' ')
     {
       if (key == '\n')
       {
         // push it in history
         Hist_Add(&tty_con);
         strcpy(text, tty_con.buffer);
         Field_Clear(&tty_con);
         key = '\n';
         write(1, &key, 1);
         return text;
       }
       if (key == '\t')
       {
         tty_Hide();
         Field_CompleteCommand( &tty_con );
         // Field_CompleteCommand does weird things to the string, do a cleanup
         //   it adds a '\' at the beginning of the string
         //   cursor doesn't reflect actual length of the string that's sent back
         tty_con.cursor = strlen(tty_con.buffer);
         if (tty_con.cursor>0)
         {
           if (tty_con.buffer[0] == '\\')
           {
             for (i=0; i<=tty_con.cursor; i++)
             {
               tty_con.buffer[i] = tty_con.buffer[i+1];
             }
             tty_con.cursor--;
           }
         }
         tty_Show();
         return NULL;
       }
       avail = read(0, &key, 1);
       if (avail != -1)
       {
         // VT 100 keys
         if (key == '[' || key == 'O')
         {
           avail = read(0, &key, 1);
           if (avail != -1)
           {
             switch (key)
             {
             case 'A':
               history = Hist_Prev();
               if (history)
               {
                 tty_Hide();
                 tty_con = *history;
                 tty_Show();
               }
               tty_FlushIn();
               return NULL;
               break;
             case 'B':
               history = Hist_Next();
               tty_Hide();
               if (history)
               {
                 tty_con = *history;
               } else
               {
                 Field_Clear(&tty_con);
               }
               tty_Show();
               tty_FlushIn();
               return NULL;
               break;
             case 'C':
               return NULL;
             case 'D':
               return NULL;
             }
           }
         }
       }
       Com_DPrintf("droping ISCTL sequence: %d, tty_erase: %d\n", key, tty_erase);
       tty_FlushIn();
       return NULL;
     }
     // push regular character
     tty_con.buffer[tty_con.cursor] = key;
     tty_con.cursor++;
     // print the current line (this is differential)
     write(1, &key, 1);
   }
   return NULL;
 } else
 {
   int     len;
   fd_set  fdset;
   struct timeval timeout;

   if (!com_dedicated || !com_dedicated->value)
     return NULL;

   if (!stdin_active)
     return NULL;

   FD_ZERO(&fdset);
   FD_SET(0, &fdset); // stdin
   timeout.tv_sec = 0;
   timeout.tv_usec = 0;
   if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
   {
     return NULL;
   }

   len = read (0, text, sizeof(text));
   if (len == 0)
   { // eof!
     stdin_active = qfalse;
     return NULL;
   }

   if (len < 1)
     return NULL;
   text[len-1] = 0;    // rip off the /n and terminate

   return text;
 }
}


Warsow (Quake2 based) - Linux

char *Sys_ConsoleInput(void)
{
static char text[256];
int     len;
fd_set fdset;
struct timeval timeout;

if (!dedicated || !dedicated->integer)
return NULL;

if (!stdin_active)
return NULL;

FD_ZERO (&fdset);
FD_SET (0, &fdset); // stdin
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
return NULL;

len = read (0, text, sizeof(text));
if (len == 0) { // eof!
stdin_active = qfalse;
return NULL;
}

if (len < 1)
return NULL;

text[len-1] = 0;    // rip off the /n and terminate

return text;
}


UFO-AI (Quake2 based) Mac

//_______________________________________________________________________________________________Sys_ConsoleInput()

char *Sys_ConsoleInput (void)
{
   static char myText[256];
   int     myLength;
   fd_set myFDSet;
   struct timeval myTimeOut;

   if (!dedicated || !dedicated->value)
   {
       return NULL;
   }

   if (!stdin_active)
   {
       return NULL;
   }

   FD_ZERO (&myFDSet);
   FD_SET (0, &myFDSet);
   myTimeOut.tv_sec = 0;
   myTimeOut.tv_usec = 0;
   if (select (1, &myFDSet, NULL, NULL, &myTimeOut) == -1 || !FD_ISSET (0, &myFDSet))
   {
       return (NULL);
   }

   myLength = read (0, myText, sizeof (myText));
   if (myLength == 0)
   {
       stdin_active = false;
       return (NULL);
   }

   if (myLength < 1)
   {
       return (NULL);
   }
   myText[myLength - 1] = 0x00;

   return (myText);
}




AROS ( el sistema operativo, por supuesto, http://aros.sourceforge.net/)

char *Sys_ConsoleInput(void)
{
   static char text[256];

//    printf("Sys_ConsoleInput\n");

   if (cls.state == ca_dedicated) {

FGets(Input(), text, 256);

return text;
   }

   return NULL;
}



Zquake (Linux)

char *Sys_ConsoleInput(void)
{
static char text[256];
char dummy[256];
int len;

if (!dedicated || noconinput)
return NULL;

len = read (0, text, sizeof(text));
if (len < 1)
return NULL;

// Tonik: if the line was longer than 256 chars,
// through away the remainder (FIXME)
while (read (0, dummy, sizeof(dummy)) > 0) {};

text[len-1] = 0;    // rip off the /n and terminate

return text;
}



Telejano (windows)


char *Sys_ConsoleInput (void)
{
static char text[256];
static int len;
INPUT_RECORD recs[1024];
int dummy;
int ch, numread, numevents;

if (!isDedicated)
return NULL;


for ( ;; )
{
if (!GetNumberOfConsoleInputEvents (hinput, &numevents))
Sys_Error ("Error getting # of console events");

if (numevents <= 0)
break;

if (!ReadConsoleInput(hinput, recs, 1, &numread))
Sys_Error ("Error reading console input");

if (numread != 1)
Sys_Error ("Couldn't read console input");

if (recs[0].EventType == KEY_EVENT)
{
if (!recs[0].Event.KeyEvent.bKeyDown)
{
ch = recs[0].Event.KeyEvent.uChar.AsciiChar;

switch (ch)
{
case '\r':
WriteFile(houtput, "\r\n", 2, &dummy, NULL);

if (len)
{
text[len] = 0;
len = 0;
return text;
}
else if (sc_return_on_enter)
{
// special case to allow exiting from the error handler on Enter
text[0] = '\r';
len = 0;
return text;
}

break;

case '\b':
WriteFile(houtput, "\b \b", 3, &dummy, NULL);
if (len)
{
len--;
}
break;

default:
if (ch >= ' ')
{
WriteFile(houtput, &ch, 1, &dummy, NULL);
text[len] = ch;
len = (len + 1) & 0xff;
}

break;

}
}
}
}

return NULL;
}


Y otros:
http://www.google.es/codesearch?hl=es&lr=&q=Sys_ConsoleInput+&btnG=Buscar

Prompt

Eres un chalao Tei! :) hehehehe

Eres un friki de los input de motores y de las mil versiones del quake! :P






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.