3.
Leyendo del teclado y escribiendo texto.
Contenido
de este apartado:
3.1.
Pautas generales.
Escribir una frase suele ser sencillo. Tendremos una orden capaz de
escribir un cierto texto en unas ciertas coordenadas. A veces no
será inmediato escribir algo que no sea un texto puro, como es
el caso del resultado de una operación matemática, pero
este problema es fácil de solucionar. Lo que suele ser algo
más difícil es escribir con tipos de letras
espectaculares: a veces (pocas) estaremos limitados al tipo de letra de
la BIOS de la tarjeta gráfica, y otras muchas estaremos limtados
(menos) a los tipos de letra de nuestro sistema operativo o que
incorpora nuestro compilador. Más adelante veremos una forma de
crear nuestros propios tipos de letra y usarlos en nuestros juegos, a
cambio de peder la comodidad de usar funciones "prefabricadas" como las
que veremos en este apartado y usaremos en nuestros pimeros juegos.
Lo de leer el teclado no suele ser difícil: es habitual tener
una función que comprueba si se ha pulsado una tecla o no (para
no tener que esperar) y otra que nos dice qué tecla se ha
pulsado. Incluso es habitual que tengamos definidas ciertas constantes,
para no tener que memorizar cual es el número asociado a una
cierta tecla, o el código ASCII que devuelve. En el caso de
Java, es algo más incómodo que en C o Pascal, pero
tampoco es especialmente complicado.
Vamos a verlo...
3.2
Texto y teclado Con C y Allegro.
A
la hora de escribir texto, casi cualquier biblioteca gráfica
tendrá
disponible una función que nos permita escribir un cierto
mensaje
en unas ciertas coordenadas. En el caso de Allegro, dicha
función
es:
- void
textout(BITMAP *bmp, const FONT *f, const char *s, int x, y, int color);
Debería
ser fácil de entender: el primer parámetro es
dónde
escribiremos el texto (en principio, usaremos "screen", la pantalla),
con
qué fuente (usaremos una llamada "font", que es la fuente
hardware
del 8x8 puntos habitual de la pantalla de 320x200 puntos). Finalmente
indicamos
el texto a escribir, las coordenadas x e y, y terminamos con el color.
Además,
tenemos dos variantes, por si queremos que el texto quede centrado en
torno
a esa posición x (la orden anterior indica la
posición de
comienzo), o que termine en esa posición:
- void
textout_centre(BITMAP *bmp, const FONT *f, const char *s, int x, y,
color);
- void
textout_right(BITMAP *bmp, const FONT *f, const char *s, int x, y,
color);
Si
se trata de un texto con parámetros como los que mostramos
con
"printf",
en la mayoría de bibliotecas gráficas no
podríamos
escribirlo directamente, deberíamos convertirlo primero a
texto,
con alguna orden como "sprintf". En Allegro sí tenemos la
posibilidad
de escribirlo directamente con:
- void
textprintf(BITMAP *bmp, const FONT *f, int x, y, color, const char
*fmt,
...);
Y
también tenemos dos órdenes equivalentes pero que
ajustan
el texto al centro o al lado derecho:
- void
textprintf_centre(BITMAP *bmp, const FONT *f, int x, y, color, const
char
*fmt, ...);
- void
textprintf_right(BITMAP *bmp, const FONT *f, int x, y, color, const
char
*fmt, ...);
Un
ejemplo del uso de estas órdenes sería:
/*----------------------------*/
/*
Intro a la programac de
*/
/*
juegos, por Nacho Cabanes */
/*
*/
/*
IPJ03.C
*/
/*
*/
/*
Tercer ejemplo: escribir
*/
/*
texto en modo
gráfico
*/
/*
*/
/*
Comprobado
con:
*/
/*
- Djgpp 2.03 (gcc 3.2)
*/
/*
y Allegro
4.02
*/
/*----------------------------*/
#include
<allegro.h>
#include
<conio.h>
main()
{
allegro_init();
if (set_gfx_mode(GFX_SAFE,320,200,0,0)!=0){
set_gfx_mode(GFX_TEXT,0,0,0,0);
allegro_message(
"Incapaz
de
entrar a modo grafico\n%s\n",
allegro_error);
return 1;
}
textout(screen,
font, "Ejemplo
de texto", 160,80,
palette_color[15]);
textout_centre(screen,
font, "Ejemplo
de texto", 160,100,
palette_color[14]);
textout_right(screen,
font, "Ejemplo
de texto", 160,120,
palette_color[13]);
textprintf(screen,
font, 100,40,
palette_color[12],
"Dos
por dos es %d",2*2);
getch();
}
END_OF_MAIN();
|
que
tendría como resultado:

Otra
orden relacionada con el texto y que puede resultar interesante es:
Si
el parámetro "mode" es 0 o positivo, el texto
será opaco
(se borra el fondo), y el color de fondo del texto es el que indique
"mode".
Si es negativo, el texto será transparente (en sus huecos se
verá
el fondo, que es lo habitual en otras bibliotecas gráficas).
Si
no se indica nada, se asume que el modo de escritura del texto es 0 (el
texto es opaco, con fondo negro).
En
cuanto a la introducción
de texto, en la
mayoría
de
bibliotecas gráficas, no tendremos nada parecido a "scanf"
ni a
"gets" que nos permita teclear un texto entero, y menos
todavía
corregirlo con las flechas del cursor, retroceso, etc.
Normalmente
deberemos leer letra a letra (con funciones como "getch" o alguna
equivalente),
y filtrarlo nosotros mismos. Allegro sí tiene un
pequeño
GUI (Interfaz gráfico de usuario), que nos
permitirá
abreviar
ciertas tareas como la entrada de texto o la pulsación de
botones.
Pero estas posibilidades las veremos más adelante.
Con
Allegro tenemos una función para esperar a que se pulse una
tecla
y comprobar qué tecla se ha pulsado:
Se
puede utiliza comprobando el código ASCII (ver
qué
"letra"
corresponde):
if ((readkey() & 0xff) == 'n') printf("Has pulsado
n");
o
bien comprobando el código de la tecla (el "scancode")
if ((readkey() >> 8) == KEY_SPACE)
printf("Has
pulsado Espacio");
Esta
forma de comprobarlo se debe a que nos devuelve un valor de 2 bytes. El
byte bajo contiene el código ASCII (la letra), mientras que
el
byte
alto contiene el código de la tecla.
Si
se pulsa Ctrl o Alt a la vez que una cierta tecla, el código
(scancode)
de la tecla sigue siendo el mismo, pero no es código ASCII.
Por
ejemplo, mayúsculas + 'a' devolvería 'A', ctrl +
'a'
devolvería
1 (y ctrl + 'b' = 2, ctrl + 'c' = 3 y así sucesivamente) y
alt +
cualquier tecla devuelve 0 (también devuelven 0 las teclas
de
función
F1 a F12 y alguna otra.
Por
eso, en los juegos normalmente usaremos el "scancode" de las teclas,
que
no varía. Eso sí, estos números no son
fáciles
de recordar, por lo que tenemos unas constantes preparadas, como la
KEY_SPACE
del ejemplo anterior:
KEY_A ... KEY_Z,
KEY_0 ... KEY_9,
KEY_0_PAD ... KEY_9_PAD,
KEY_F1 ... KEY_F12,
KEY_ESC, KEY_TILDE, KEY_MINUS, KEY_EQUALS,
KEY_BACKSPACE, KEY_TAB, KEY_OPENBRACE, KEY_CLOSEBRACE,
KEY_ENTER, KEY_COLON, KEY_QUOTE, KEY_BACKSLASH,
KEY_BACKSLASH2, KEY_COMMA, KEY_STOP, KEY_SLASH,
KEY_SPACE,
KEY_INSERT, KEY_DEL, KEY_HOME, KEY_END, KEY_PGUP,
KEY_PGDN, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN,
KEY_SLASH_PAD, KEY_ASTERISK, KEY_MINUS_PAD,
KEY_PLUS_PAD, KEY_DEL_PAD, KEY_ENTER_PAD,
KEY_PRTSCR, KEY_PAUSE,
KEY_ABNT_C1, KEY_YEN, KEY_KANA, KEY_CONVERT, KEY_NOCONVERT,
KEY_AT, KEY_CIRCUMFLEX, KEY_COLON2, KEY_KANJI,
KEY_LSHIFT, KEY_RSHIFT,
KEY_LCONTROL, KEY_RCONTROL,
KEY_ALT, KEY_ALTGR,
KEY_LWIN, KEY_RWIN, KEY_MENU,
KEY_SCRLOCK, KEY_NUMLOCK, KEY_CAPSLOCK
Finalmente,
podemos comprobar si se ha
pulsado una tecla, pero sin
quedarnos
esperando a que esto ocurra. No lo usaremos en nuestro primer juego,
pero
aun así vamos a comentarlo. En Turbo C y compatibles existe
la
función
kbhit(), que está declarada en "conio.h" En Allegro tenemos:
que
devuelve TRUE si se ha pulsado alguna tecla (después
comprobaríamos
con "readkey" de qué tecla se trata, y en ese momento,
"keypressed"
volvería a valer FALSE).
En
cualquier caso, hay una línea que deberemos incluir siempre
al
principio de nuestros programas,
si vamos a usar las rutinas de
control
de teclado que nos proporciona Allegro:
Existe otra forma de comprobar el teclado, que nos permitiría
saber si se han pulsado varias teclas a la vez, pero eso es
algo que no necesitamos aún, y que dejamos para un poco
más adelante...
3.3.
Teclado y texto con Pascal.
La
orden básica para escribir
texto en la pantalla
gráfica
con Free Pascal es:
También
existe una variante a la que no se le indica en qué
posición
queremos escribir, y entonces lo hará a
continuación del
último texto escrito. Es "OutText( texto )"
Si
queremos que el texto esté alineado
a la derecha o al
centro,
lo haríamos con "SetTextJustify(horizontal, vertical)",
donde
"horizontal"
puede ser LeftText (izquierda), CenterText (centro) o RightText
(derecha)
y la alineación vertical puede ser BottomText (texto bajo el
puntero),
CenterText (centrado) o TopText (sobre el puntero).
Existen
otras posibilidades, que no usaremos, al menos por ahora, porque no
existen
como tal en Allegro y lo haremos de la misma forma que con dicha
librería
(artesanalmente). Es el caso de emplear distintos tipos de letra, en
distintos
tamaños o escribir con otras orientaciones (por ejemplo, de
arriba
a abajo).
Lo
que no tenemos en FreePascal es ninguna orden equivalente a "printf"
para
pantalla gráfica. Sólo podemos escribir cadenas
de texto,
de modo que si queremos escribir números o expresiones
complejas,
deberemos convertirlas primero a cadenas de texto.
En
cuanto a la introducción
de texto, en la unidad crt
tenemos
las funciones equivalente s a las dos principales de Allegro:
"keypressed"
devuelve "false" mientras no se haya pulsado ninguna tecla, y "true"
cuando
se ha pulsado. Entonces se puede leer la tecla con "readkey". No
tenemos
garantías de que funciones como ReadLn vayan a trabajar
correctamente
en modo gráfico, así que en general tendremos que
ser
nosotros
quienes creemos rutinas fiables si queremos que se pueda teclear un
texto
entero, y más aún si queremos corregirlo con las
flechas
del cursor, retroceso, etc.
Eso
sí, no tenemos constantes predefinidas con las que comprobar
si
se ha pulsado una cierta tecla. Para las teclas
alfanuméricas no
es problema, podemos hacer cosas como
tecla
:= readkey;
if
tecla = 'E' then ...
Pero
para las teclas de función, readkey devuelve 0, y debemos
volver
a leer su valor para obtener el número de tecla pulsada. Es
fácil
crear un programa que nos diga el número de cada tecla
pulsada,
y crearnos nuestras propias constantes simbólicas,
así
que
no profundizamos más por ahora. Si en alguno de nuestros
juegos
usamos varias teclas, indicaremos entonces los códigos de
cada
una
de ellas.
Aun así, habrá un problema
que afectará a nuestros juegos con Free Pascal para Windows,
que comentaremos en el siguiente apartado.
Un ejemplo similar al anterior
en C sería este:
(*----------------------------*) (* Intro a la programac de *) (* juegos, por Nacho Cabanes *) (* *) (* IPJ03P.PAS *) (* *) (* Tercer ejemplo: escribir *) (* texto en modo grafico *) (* *) (* Comprobado con: *) (* - FreePascal 2.0 -Windows *) (*----------------------------*)
uses graph, crt, sysutils;
var gd,gm, error : integer;
BEGIN gd := 0; gm := 0; initgraph(gd, gm, '');
error := graphResult; if error <> grOk then begin writeLn('No se pudo entrar a modo grafico'); writeLn('Error encontrado: '+ graphErrorMsg(error) ); halt(1); end;
setColor(15); outTextXY(160,80, 'Ejemplo de texto');
setTextJustify(CenterText,CenterText); setColor(14); outTextXY(160,100, 'Ejemplo de texto');
setTextJustify(RightText,CenterText); setColor(13); outTextXY(160,120, 'Ejemplo de texto');
setTextJustify(LeftText,CenterText); setColor(12); outTextXY(100,40, 'Dos por dos es '+intToStr(2*2));
readkey; closeGraph; END.
|
Y su resultado sería

3.4.
Teclado y texto con Java.
Escribir
en un Applet de Java no es difícil. La orden
básica es "drawString":
- drawString(String
texto, int x, y);
Si
usamos esta orden, no es tan sencillo como en Allegro el ajustar el
texto
a un lado de esas coordenadas, al otro o centrarlo. Existen otras
órdenes
que nos permitirán hacerlo con facilidad, pero las veremos
más
adelante.
Lo
que sí es algo más complicado en Java es eso de leer
del
teclado. Además es
distinto en modo texto (consola) o en
modo
gráfico (como nuestro Applet). La forma más
sencilla (en
mi opinión) de hacerlo dentro de un Applet es implementando
un "KeyListener",
que nos da la estructura básica, a cambio de obligarnos a
detallar
las funciones keyPressed
(que se pondrá en marcha en el
momento
de apretar una tecla), keyReleased
(al dejar de apretar la
tecla)
y keyTyped
(que se activa cuando el carácter
correspondiente
aparecería en pantalla). Las tres funciones admiten un
parámetro
de tipo KeyEvent, que tiene métodos como "getChar"
que
dice
la letra que corresponde a esa tecla pulsada o "getKeyCode"
que
nos indica el código de tecla. La letra pulsada es
fácil
de comprobar porque tenemos las letras impresas en el teclado, sabemos
qué letra corresponde a cada tecla. Pero el
código es
menos
intuitivo, así que tenemos una serie de constantes que nos
ayuden
a recordarlos:
- VK_A
a VK_Z para las teclas alfabéticas.
- VK_0
a VK_9 para las numéricas y VK_NUMPAD0 a VK_NUMPAD9 para el
teclado
numérico adicional.
- VK_F1
en adelante para las teclas de función.
- VK_LEFT,
VK_RIGHT, VK_DOWN, VK_UP para las flechas del teclado.
- VK_SPACE,
VK_ENTER, VK_ESCAPE, VK_HOME, VK_END y similares para las
demás
teclas especiales.
De modo
que en la práctica lo usaríamos haciendo cosas
como
public
void keyPressed(KeyEvent e) {
if
(e.getKeyChar() == 'a' ) ...
o
como
public
void keyPressed(KeyEvent e) {
if
(e.getKeyCode() == VK_ESCAPE ) ...
Solo
tres comentarios más:
- En
el método "init" (que se encarga de inicializar nuestro
Applet
antes
de empezar a dibujar) deberíamos añadir
"addKeyListener(this);"
- Sólo
se leerán pulsaciones del "componente" actual. Esto quiere
decir
que si nuestro Applet tuviera varios componentes (cosas que no hemos
visto,
como cuadros de texto, listas desplegables, etc), nos podría
interesar
indicar cual queremos decir que sea el activo con "requestFocus()", que
en el ejemplo aparece dentro de un comentario por no ser necesario.
- Por
eso mismo, debemos hacer clic con el ratón en nuestro Applet
antes
de empezar a usar el teclado, o no nos hará caso, si no
está
"activo".
Todo
listo. Vamos a ver un ejemplo sencillo del uso de estas
órdenes:
/*----------------------------*/ /* Intro a la programac de */ /* juegos, por Nacho Cabanes */ /* */ /* ipj03j.java */ /* */ /* Tercer ejemplo: escribir */ /* en colores, leer del */ /* teclado */ /* */ /* Comprobado con: */ /* - JDK 1.4.2_01 */ /*----------------------------*/
import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class ipj03j extends Applet implements KeyListener {
char letra; public void paint(Graphics g) {
g.setColor(Color.green); g.drawString("Ejemplo de texto 1", 140, 80);
g.setColor(Color.blue); g.drawString("Ejemplo de texto 2", 160, 100);
g.setColor(new Color(0, 128, 128)); g.drawString("Ejemplo de texto 3", 180, 120); if (letra =='a') g.drawString("Has pulsado A", 60, 60); }
public void init() { //requestFocus(); addKeyListener(this); }
public void keyTyped(KeyEvent e) { } public void keyReleased(KeyEvent e) { } public void keyPressed(KeyEvent e) { letra = e.getKeyChar(); repaint(); } }
|
que
tendría como resultado:

 
|