You are on page 1of 24

Haciendo un juego bajo SDL

(Por Gorka Surez Garca)

Esta obra est bajo una licencia de Creative Commons. http !!creativecommons.or"!licenses!b#$nc!%.&!es! Sers libre de ' Copiar( distribuir( comunicar # ejecutar p)blicamente la obra. ' *acer obras derivadas. +ajo las si"uientes condiciones ' Atribucin. ,ebes reconocer # citar la obra de la -orma especi-icada por el autor o el licenciante. ' No Comercial. .o puedes utilizar esta obra para -ines comerciales. ' /l reutilizar o distribuir la obra( tienes 0ue dejar bien claro los t1rminos de la licencia de esta obra. ' /l"una de estas condiciones puede no aplicarse si se obtiene el permiso del titular de los derechos de autor. Para ms in-ormaci2n sobre la licencia( visite la direcci2n 3eb arriba e4puesta.

1) Introduccin a los videojuegos bajo SDL: 5a industria del videojue"o ha ido creciendo )ltimamente( superando incluso a la del cine( eso es o un mu# buen sntoma para la industria del videojue"o( o un mu# mal sntoma para la del cine. El caso es 0ue desde el punto de vista de la pro"ramaci2n( a-rontar la creaci2n de un videojue"o es un interesante reto. .o importa cuantos jue"os ha#as creado( siempre habr al"o nuevo 0ue hacer( o 0ue no ha#as hecho antes( al contrario 0ue otras areas donde sabemos 0ue siempre va a ser sota( caballo # re# (o altas( bajas # modi-icaciones( aplicado a la in-ormtica). 6 todo parece ir bien en la industria del videojue"o( si no -uera por un tema aparentemente 7sin importancia7. El tema en cuesti2n es( 0ue si bien la plata-orma del sistema operativo predominante 3indo3s( est in-estado hasta la saciedad de videojue"os( no pasa lo mismo con linu4( la alternativa ms interesante ante lo habitual del 3indo3s. 5a raz2n sin duda se encuentra en el no mu# sorprendente hecho( de 0ue la "ran ma#ora de los jue"os comerciales( son realizados con la /P8 ,irect9 de :icroso-t. /l no ser una /P8 de c2di"o abierto( es complicado encontrar bajo 5inu4 una /P8 0ue trabaje aparentemente de la misma -orma. Pero por suerte( "ente con tiempo libre # "anas de a#udar al mundo( crea /P8 multiplata-orma como S,5. Por ello "racias a libreras como la S,5( no tendremos 0ue reescribir partes del c2di"o( a la hora de portar un jue"o de 3indo3s a linu4 # al rev1s. +sicamente es como matar un pjaro de dos tiros( # tener no solo esa inmensa ma#ora de usuarios de 3indo3s( sino 0ue adems tambi1n mediante /P8s como esta( los usuarios de sistemas operativos alternativos (no solo linu4( tambi1n mac est soportado por S,5)( podrn tener el mismo jue"o con un es-uerzo mnimo( # esto e0uivale a ms posibles ventas. /s 0ue la industria saldra bene-iciada( # los 0ue no usan 3indo3s tambi1n. Por0ue a0uel 0ue se compra un :ac( no creo 0ue le sobre mucho dinero para un PC... 9, Para a0uellos 0ue hacen jue"os de -orma amateur( tambi1n sera importante su colaboraci2n( para 0ue plata-ormas como linu4 dispon"an de sus jue"os( para as ir 0uitando poco a poco una de las )ltimas barreras 0ue todava tiene linu4 (aun0ue ha# 0uien ase"ura 0ue es un 7mito7( si es 0ue el buscaminas da para miles de horas de jue"o( s)( :ac;S( # en "eneral todo a0uello 0ue no sea el camino )nico dictado por 3indo3s.

<

2) Creando ro!ectos ara la SDL: +ien dejando los rollos introductivos( 0ue todo el mundo se salta( pasemos a lo si"uiente. Si ests haciendo tu pro#ecto bajo 3indo3s( en la documentaci2n de las S,5 viene unas indicaciones importantes sobre como con-i"urar tu pro#ecto bajo =isual C'' >.?. @ienes dos opciones a la hora de crear un pro#ecto( 0ue sea una aplicaci2n Ain<% de Consola( o una aplicaci2n Ain<% de =entana. Si eli"es 0ue sea de consola( te saldr por detrs la bonita consola de :S$,;S tan 0uerida por al"unos. Eso puede 0uedar ciertamente -eo( por ello es recomendable ele"ir 7Ain<% /pplication7. Bna vez creado nuestro pro#ecto( 0ue deber estar vaco( para evitar 0ue el =C te meta mierda tpica de 3indo3s( hemos de ir a las propiedades del pro#ecto (pulsando /5@'CD( o su alternativa 7Project $E Settin"s...7). Bna vez nos sal"a el cuadro de dialo"o de las propiedades del pro#ecto( hemos de seleccionar la pestaFa 7C!C''7( all hemos de ele"ir la opci2n 7Code Generation7 de la propiedad 7Cate"or#7. Entonces es cuando hemos de cambiar tanto para el 7Ain<% ,ebu"7( como para el 7Ain<% Gelease7 de nuestro pro#ecto( la opci2n 7Bse run$time librar#7( # esco"er siempre :ultithreaded ,55. 6 en la pestaFa 75ink7( en la casilla de 7;bject!librar# modules 7 ha# 0ue a"re"ar S,5.lib # S,5main.lib. /un0ue otra -orma de hacerlo es escribiendo lo si"uiente en nuestro pro"rama
// Con este ifdef cuando lo compilemos con el gcc, se saltar este fragmento // de cdigo. Adems la directiva pragma comment (lib, "fichero.lib") nos sirve // para aadir nuevos ficheros.lib, sin tener ue meternos en las propiedades // del pro!ecto actual. "ifdef #$%&' "pragma comment (lib, "()*.lib") "pragma comment (lib, "()*main.lib") "endif

/s 0ue en resumidas cuentas ha# 0ue hacer al"o tal 0ue


"+ro,ect -. (ettings... -. C/C// -. Categor! 0 Code 1eneration -. 2se run-time librar! 0 3ultithreaded )**"

Sin embar"o para linu4( ese S; tan 7di-cil7( el )nico parmetro especial 0ue ha# 0ue poner al compilador "cc es el si"uiente -ra"mento de te4to Hsdl$con-i" $$c-la"s $$libsH. Para 0ue lue"o di"an 0ue linu4 es imposible de manejar. El caso es 0ue nos 0uedara una llamada al "cc tal 0ue
gcc 4sdl-config --cflags --libs4 -o prueba prueba.c

;bviamente es pre-erible 0ue os ha"ais un make-ile( para poder "estionaros pro#ectos con varios -icheros -uente( o un "ui2n shell si lo pre-iere al"uien. Eso es tan solo una cuesti2n de "ustos.

") Iniciando el juego con SDL: Para evitarnos problemas con cual0uier compilador de C( la -unci2n principal es pre-erible 0ue sea un
int main (int argc, char 55 argv) 6 //... 7

Bna vez lle"ados a este punto hemos de tener en cuenta el es0uema universal de las cosas( por lo tanto el es0uema universal de un pro"rama cual0uiera. @odo jue"o al menos( tiene una inicializaci2n( para lue"o pasara a la ejecuci2n del jue"o mismamente( # terminar con una -inalizaci2n del mismo. Pero antes 0ue todo esto( ha# 0ue tener en cuenta una cuestion mu# importante. Esta cuesti2n es el tema de los datos del jue"o. :uchas veces( en aplicaciones sencillas se tiende a hacer las variables "lobales( #a 0ue para evitarnos en"orros de punteros por a0u # por all( # -unciones con %I mil parmetros( se suele tender a soluciones como esa. Pero eso es totalmente inviable en pro#ectos con muchos -icheros -uente( por eso nos deberamos crear un tipo estructurado para almacenar las variables 7"lobales7 de todo el jue"o
t!pedef struct 6 // 8ariables de las 7 t%ucleo9

ue depende el funcionamiento del ,uego.

En C'' crearamos una clase (class C.ucleo)( donde almacenar todos los datos( # esta clase solo tendra variables bsicamente. El por 0u1 evitar poner rutinas 0ue manejen esos datos( es por el mero hecho de 0ue al -inal( acabaramos 7teniendo7 0ue meter todas las -unciones en la misma clase( # eso no es mu# ele"ante( no. /s 0ue una vez ten"amos nuestro nucleo de datos( el main 0uedara al"o como
int main (int argc, char 55 argv) 6 t%ucleo %ucleo9 $nit:uego(;%ucleo, <=2>, ?@@, A@@, &')9 Bucle+rincipal(;%ucleo)9 Cin:uego(;%ucleo)9 7 return @9

*a# 0uien hubiera declarado .ucleo como un puntero directamente # hubiera hecho un malloc( pero -rancamente eso sera buscarse complicaciones del todo innecesarias. En 8nitJue"o() el pro"rama se encar"ara de inicializar el jue"o( 0ue no inicializar la partida( dado 0ue partida # jue"o son dos cosas distintas. +sicamente el es0uema 0ue se"uira la -unci2n sera
void $nit:uego (t%ucleo 5 %ucleo, // 8ariables del programa. int Dinmode, // $ndica si ueremos ue estE en modo ventana. int Didth, // Ancho de la ventana. int height, // Alto de la ventana. int bpp) // %Fmero de bits de profundida del color. 6 // $nicialiGamos la ()* creando el buffer primario de la pantalla.

// Activamos la entrada por teclado ! ratn ba,o ()*. // Cargamos las imagenes del ,uego. // Cargamos las imagenes con las fuentes. // Cargamos las puntuaciones. // H terminamos de inicialiGar el resto de datos importantes para el ,uego. 7

>

#) Iniciando la SDL ! el modo de video: Como pasa con otras tantas libreras # /P8s( la S,5 antes de usarla( debemos inicializarla. En este caso particular la cabecera de la -unci2n sera al"o como
()*I(urface 5 CrearBuffer+antalla (int Dinmode, char 5 title, int Didth, int height, int bpp)

5le"ados a ese punto lo primero 0ue ha# 0ue hacer es llamar a la -unci2n S,5L8nit( a la 0ue le hemos de pasar los sistemas del S,5 0ue 0ueremos activar. .ormalmente activaremos el sistema de video (S,5L8.8@L=8,E;)( el sistema de audio (S,5L8.8@L/B,8;) # el sistema de "esti2n del tiempo (S,5L8.8@L@8:EG). En la a#uda vienen un listado con todos los sistemas disponibles( todos los 0ue 0ueramos activar los tendremos 0ue encadenar con el operador M (or a nivel de bits). S,5L8nit si"uiendo las buenas costumbres del clsico Bni4( devolver $& en caso de -allar. 5a llamada podra 0uedar tal 0ue
if(()*I$nit(()*I$%$<I8$)>JK()*I$%$<IA2)$JK()*I$%$<I<$3>=) 00 CA$*) (alir("%o se puede iniciar la librerLa ()*.Mn", -N)9

Con este -ra"mento de c2di"o inicializamos el S,5( # comprobamos si da -allo ("define CA$* -N). En caso de darlo( llamamos a la -unci2n Salir( pasandole una cadena con un mensaje de error # el c2di"o de salida de la aplicaci2n. El c2di"o de esta -unci2n puede ser al"o como
void (alir (const char 5 mensa,e, int error) 6 // 3andamos el mensa,e de error por pantalla. printf("Os", mensa,e)9 printf("OsMn", ()*I1et>rror())9 // Pacemos ue el ratn estE mostrado ! desactivamos la ()*. ()*I(hoDCursor(()*I>%AB*>)9 ()*IQuit()9 // (alimos devolviendo el cdigo de error. eRit(error)9 7

,espu1s de lo"rar activar la S,5( lo si"uiente 0ue tenemos 0ue conse"uir es crear el bu--er principal de la pantalla. Pero si bien es cierto 0ue conocemos el ancho( el alto # la pro-undidad 0ue va a tomar el bu--er principal( para crearlo bajo S,5 nos -alta saber al"unas caractersticas ms 0ue podra tener. Estas caractersticas se las podemos pasar en una variable -la"s
if(Dinmode) flags 0 (()*IP#(2=CAC>K()*I)J2B*>B2C)9 else flags 0 (()*IP#(2=CAC>K()*I)J2B*>B2CK()*IC2**(C=>>%)9

/l"una de esas caractersticas 0ue puede tener un bu--er en S,5 son ' S,5L*ASBGC/CE N Oue bsicamente es una directiva de pre-erencia( con la 0ue le indicamos 0ue todas las ima"enes 0ue car"emos en un bu--er (0ue S,5 las llama sur-ace( # las llamar1 te4turas)( sea bu--ers 0ue est1n ubicados pre-eriblemente en la memoria de video de la tarjeta( en vez de la memoria ram normal. Esto ser ms rpido D

para la aplicaci2n. ;bviamente si no ha# espacio en la memoria de video( los car"a en la memoria ram normal. ' S,5L,;B+5E+BC N Esta caracterstica hace 0ue ten"amos dos bu--ers de pantalla( uno estar a disposici2n de la tarjeta de video( para pintar su contenido en la pantalla( # en el otro ser donde pintaremos la escena actual. Entonces para actualizar la pantalla con el nuevo contenido( llamaremos a S,5LClip para 0ue la tarjeta tome el bu--er actual 0ue acabamos de pintar( # nosotros nos pon"amos a pintar sobre el 0ue acaba de dejar libre. ' S,5LCB55SCGEE. N Esta caracterstica indica a la S,5 0ue nuestra aplicaci2n ha de ser ejecutada a pantalla completa. Si no la indicamos( la aplicaci2n ser ejecutada en modo ventana. 5o si"uiente 0ue hemos de realizar( es comprobar si el modo de video 0ue 0ueremos activar est soportado. Para ello usaremos S,5L=ideo:ode;P( pasandole el ancho( el alto( la pro-undidad de color # los -la"s. Esta devolver la pro-undidad de color m4ima 0ue soporta para dicho modo de video. Si devuelve cero( es 0ue no lo soporta en absoluto.
if(S()*I8ideo3odeJT(Didth, height, bpp, flags)) (alir("3odo de video no soportado.Mn", -')9

Cinalmente podremos activar el modo de video 0ue tanto deseamos. Para hacerlo usaremos S,5LSet=ideo:ode( pasandole lo mismo 0ue le hemos pasado a S,5L=ideo:ode;P. S,5LSet=ideo:ode nos devuelve un puntero a una estructura de tipo S,5LSur-ace( 0ue en caso de e4istir al")n problema( nos devolver .B55 para indicar 0ue e4iste un error.
Buffer+antalla 0 ()*I(et8ideo3ode(Didth, height, bpp, flags)9 if(Buffer+antalla 00 %2**) (alir("%o se pudo activar el modo de video.Mn", -&)9

Como nota curiosa( si vamos a ejecutar nuestra aplicaci2n en modo ventana( podemos cambiarle el ttulo a esta( con la -unci2n S,5LA:LSetCaption( de la si"uiente -orma
()*I#3I(etCaption("3i peacho de ,uegorlSSS", %2**)9

Cinalmente devolveremos el puntero 0ue nos devolvi2 S,5LSet=ideo:ode( almacenandolo en al"una variable( dentro de la variable .ucleo de la 0ue hablamos al principio. /l"o como S,5LSur-ace Q +u--erPantallaR

$) Iniciando la entrada or teclado ! ratn: ;tro tema "eneralmente importante en cual0uier jue"o es la "esti2n de la entrada del usuario. Para esto normalmente la ma#ora de los jue"os de pc usan el teclado # el rat2n. @ambi1n se podra usar un jo#stick( pero es ms comodo pro"ramar el tema del teclado # el rat2n TLT Como en todo el teclado # el rat2n tiene una serie de datos 0ue tenemos 0ue almacenar en variables. Para el caso del teclado nos bastar con un vector de b#tes. Esta variable para evitarnos problemas la meteremos en la estructura t.ucleo( declarandola
2int? 5 <eclado9

.ada mu# di-cil no( sin embar"o el rat2n tiene muchas ms propiedades( por lo 0ue no estara mal crearnos una estructura 0ue en"lobara todos esos datos( tales como las coordenadas actuales del cursor del rat2n( las coordenadas relativas (0ue son usadas para indicar cuanto ha incrementado o decrementado el valor de las coordenadas( se usa en jue"os <, de primera o tercera persona( por ejemplo)( o el estado de los botones del rat2n( para comprobar si estn pulsados o no. /s 0ue la declaraci2n del nuevo tipo 0uedara tal 0ue
t!pedef struct 6 int R9 //Coordenada absoluta U int !9 //Coordenada absoluta H int rR9 //Coordenada relativa U int r!9 //Coordenada relativa H int lb9 //>stado del boton iG uierdo int cb9 //>stado del boton central int rb9 //>stado del boton derecho int Du9 //$ndica si la rueda del raton se ha movido hacia arriba int Dd9 //$ndica si la rueda del raton se ha movido hacia aba,o 7 t3ouse(tate9

/s 0ue lue"o lo declaramos dentro de t.ucleo como 7t:ouseState GatonR7. 6 entonces nos podemos crear nuestra rutina para inicializar la entrada por teclado # rat2n. Para ello lo primero 0ue haremos dentro de la -unci2n ser llamar a la -unci2n S,5LPumpEvents( 0ue actualiza el estado de la entrada en S,5. Solo 0ue la primera vez 0ue lo llamemos servir para arrancar el sistema de entrada. 5ue"o tendremos 0ue inicializar el teclado( para ello llamaremos a S,5LGetPe#State( 0ue nos devuelve un puntero del tipo BintS. Esta direcci2n devuelta es donde S,5 "uarda el vector donde se almacena el valor de cada tecla. /s 0ue cuando actualizamos la entrada( S,5 comprueba el estado de todas las teclas( almacenandolo en ese vector( para 0ue lue"o nosotros al tener la direcci2n( podamos comprobar lue"o la tecla 0ue deseemos. 6 -inalmente inicializamos la estructura del rat2n. @odo esto junto 0uedara de la si"uiente -orma
void Activar>ntrada (t%ucleo 5 %ucleo) 6 // Activamos el sistema de entrada del ()*. ()*I+ump>vents()9 %ucleo-.<eclado 0 ()*I1etTe!(tate(%2**)9 // $nicialiGamos %ucleo-.=aton.R %ucleo-.=aton.! %ucleo-.=aton.rR %ucleo-.=aton.r! %ucleo-.=aton.lb %ucleo-.=aton.cb la estructura del ratn. 0 @9 0 @9 0 @9 0 @9 0 CA*(>9 0 CA*(>9

%ucleo-.=aton.rb 0 CA*(>9 %ucleo-.=aton.Du 0 CA*(>9 %ucleo-.=aton.Dd 0 CA*(>9

*a# una -unci2n especial 0ue sirve para ocultar el cursor 0ue S,5 pone para el rat2n. Es la -unci2n S,5LSho3Cursor 0ue aparece en la -unci2n Salir. Solo tiene un ar"umento 0ue puede valer S,5L,8S/+5E( para ocultar el cursor del rat2n( o S,5LE./+5E para hacerlo visible.

&?

%) Como cargar te&turas almacenadas en una imagen: 5os jue"os normalmente estn compuestos por ima"enes( 0ue "uardamos en -icheros como los t"a( jp" o bmp. S,5 por si sola solo soporta la car"a de +:P( pero e4iste una e4tensi2n para la S,5( 0ue se llama S,5Lima"e( 0ue sirve para car"ar otros tipos de -icheros 0ue no sean un +:P. /un0ue lo 0ue s es cierto 0ue S,5 permite crear una sur-ace nueva con las dimensiones 0ue 0ueramos( # lue"o modi-icar los pi4eles de dicha sur-ace( con lo 0ue podramos implementar una rutina 0ue lea t"as por ejemplo( # los car"e sobre una sur-ace. ,e todas -ormas para esos casos( es ms recomendable usar S,5Lima"e. Para car"ar una ima"en( primero tendremos 0ue tener una variable sur-ace donde car"arla( por ejemplo 7S,5LSur-ace Q im"CondoR7. 6 lue"o tan solo tendremos 0ue llamar a S,5L5oad+:P. Esta -unci2n recibe como ar"umento la ruta del -ichero bmp 0ue 0ueremos car"ar( # nos devuelve un puntero a una sur-ace creada por S,5. En caso de error devolver .B55. El c2di"o de ejemplo 0uedara del si"uiente modo
%ucleo-.<eRturas.imgCondo 0 ()*I*oadB3+("gfR/fondo.bmp")9 if(%ucleo-.<eRturas.imgCondo 00 %2**) (alir("%o se pudo cargar la imagen fondo.bmp.Mn", -V)9

Car"amos el bmp en una sur-ace( # lue"o comprobamos si realmente se ha creado dicha sur-ace. En caso de 0ue no ha#a sido as( lanzamos un error. 6 en este caso( dada la importancia de la te4tura a car"ar( salimos de la ejecuci2n del jue"o lanzando un error. Pero en los jue"os normalmente ha# te4turas 0ue pintaremos encima de otras( como las naves( # dada la naturaleza de cual0uier mapa de bits( estos son cuadrados. /s 0ue esto sera un problema serio( pero es un problema 0ue se resuelve usando un 7color clave7 para nuestra ima"en. En el ejemplo uso el color rosa cantoso (Vde-ine CCG;S/ ?4??CC??CC). Para asi"nar un color clave a una sur-ace( hemos de usar la -unci2n S,5LSetColorPe#( a la 0ue le pasamos la sur-ace 0ue 0ueremos modi-icar( la constante S,5LSGCC;5;GPE6 cu#a -unci2n es indicar 0ue estamos asi"nando el color clave de esa ima"en( # -inalmente el color 0ue deseamos 0ue actue como color clave. En caso de dar error( devuelve $&.
if(()*I(etColorTe!(%ucleo-.<eRturas.img3alosos, ()*I(=CCJ*J=T>H, CC=J(A) 00 CA$*) (alir("%o se pudo poner el color clave para malosos.bmp.Mn", -V)9

5ue"o cuando termine la ejecuci2n de nuestro pro"rama( o cuando no necesitemos al"una te4tura en particular( S,5 nos o-rece la -unci2n S,5LCreeSur-ace( a la 0ue le pasamos como ar"umento la sur-ace 0ue deseamos liberar de memoria. En este jue"o al ser tan sencillo( todas las te4turas se car"an al principio( # se liberan al -inal. Pero en otro tipo de jue"os cada vez 0ue car"amos un nivel( se"uramente car"aremos al"unas te4turas 0ue necesitemos( # cuando lo -inalicemos( liberaremos las 0ue no necesitemos.
()*ICree(urface(%ucleo-.<eRturas.imgCondo)9

;tro tema importante sobre el tema de las te4turas( viene asociado a la estructura S,5LGect de S,5. S,5LGect tiene solo I campos 4( #( 3( h (3 N ancho # h N altura). WPara 0u1 sirveX WOu1 proposito tiene en la vidaX +ien( muchas veces( sobre todo cuando tenemos animaciones( o tenemos la te4tura de todas las naves en una misma sur-ace( la ima"en tiene 0ue ser manejada con re"iones. 6 eso es lo 0ue representa S,5LGect( re"iones de una sur-ace. En el jue"o tenemos el si"uiente ejemplo &&

//3alo verde %ucleo-.<eRturas.3alososW@X.R %ucleo-.<eRturas.3alososW@X.! %ucleo-.<eRturas.3alososW@X.h %ucleo-.<eRturas.3alososW@X.D //3alo aGul %ucleo-.<eRturas.3alososWNX.R %ucleo-.<eRturas.3alososWNX.! %ucleo-.<eRturas.3alososWNX.h %ucleo-.<eRturas.3alososWNX.D

0 @9 0 @9 0 AV9 0 AV9 0 AV9 0 @9 0 AV9 0 AV9

En una de las variables internas de .ucleo( tenemos el vector :alosos( 0ue est declarado de la si"uiente -orma 7S,5LGect :alososY:/9:/5ZR7. En este caso particular son solo dos naves las 0ue ha# en la misma ima"en( 0ue contiene las naves enemi"as. Por lo 0ue nos creamos dicho vector para almacenar las re"iones 0ue e4isten dentro de dicha ima"en. Esto as puede parecer 0ue no tiene mucho sentido( pero lue"o cobrar sentido ms adelante.

&%

') Como manejar el teclado ! el ratn: /hora e4plicaremos al"o bastante -undamental( como es la interacci2n de la 0ue dispondremos con el teclado # el rat2n. .ormalmente uno actualiza la entrada toda de "olpe( es una de esas 7buenas costumbres7. Para ello es primordial primero llamar a S,5LPumpEvents( para 0ue S,5 actualice el estado total del sistema de la entrada. Con esto #a podremos saber el estado de cual0uier tecla sin tener 0ue llamar a nin"una -unci2n ms( pero normalmente las aplicaciones suelen usar tambi1n el rat2n (aun0ue sea solo para el men)). Por ello tenemos dos -unciones para obtener datos re-eridos al rat2n. Son S,5LGet:ouseState # S,5LGetGelative:ouseState. 5a primera le tenemos 0ue pasar por re-erencia un par de enteros donde almacenar las coordenadas 4 e # del rat2n. /dems nos devolver un entero del tamaFo de un b#te( con el estado actual de los botones del rat2n. 5a se"unda pasandole tambi1n por re-erencia un par de enteros( almacenaremos las coordenadas relativas de la 4 # la #. @odo esto est mu# bien( pero claro( tambi1n nos apetece saber el estado de los botones( #a sabes( para hacer click o disparar estilo Ouake. Para ello tenemos 0ue hacer una cosa la mar de linda( una de esas lindezas por las 0ue el C es un len"uaje tan 0uerido por muchos( pero simplemente lo apuntas( lo entiendes # lo usas para todas las veces 0ue necesites codi-icarlo. 5a si"uiente e4presi2n 7(+otones [ S,5L+B@@;.(S,5L+B@@;.L5EC@)) NN &7( siendo +otones esa variable BintS( donde hemos "uardado el valor devuelto por S,5LGet:ouseState( nos indica para este caso en concreto si el bot2n iz0uierdo est pulsado o no. ;bviamente devuelve cierto( en caso de 0ue est1 pulsado. Cinalmente todo esto junto 0uedara tal 0ue
void ActualiGar>ntrada (t%ucleo 5 %ucleo) 6 2int? Botones9 // ActualiGa la entrada ba,o ()*. ()*I+ump>vents()9 // ActualiGa el estado del ratn. Botones 0 ()*I1et3ouse(tate(;(%ucleo-.=aton.R), ;(%ucleo-.=aton.!))9 ()*I1et=elative3ouse(tate(;(%ucleo-.=aton.rR), ;(%ucleo-.=aton.r!))9 //()*IB2<<J%(U) -. %ucleo-.=aton.lb 0 %ucleo-.=aton.cb 0 %ucleo-.=aton.rb 0 %ucleo-.=aton.Du 0 %ucleo-.=aton.Dd 0 (()*I+=>((>)YY(U-N)) ((Botones ; ()*IB2<<J%(N)) ((Botones ; ()*IB2<<J%(')) ((Botones ; ()*IB2<<J%(&)) ((Botones ; ()*IB2<<J%(V)) ((Botones ; ()*IB2<<J%(Z)) 00 00 00 00 00 N)9 N)9 N)9 N)9 N)9 //()*IB2<<J%I*>C< //()*IB2<<J%I3$))*> //()*IB2<<J%I=$1P< //()*IB2<<J%I#P>>*2+ //()*IB2<<J%I#P>>*)J#% N ' & V Z

6 as tenemos a nuestra disposici2n todos los datos de -orma actualizada( una de esas maravillas de la ciencia. Solo 0ueda comentar como comprobar si una tecla est pulsada o no. Esto es mu# sencillo( solo ha# 0ue ir la variable puntero del teclado( # tratarlo como un vector normal para ver si la tecla n)mero el 0ue sea( vale & ("define <=2> N) o vale ? ("define CA*(> @). Para el jue"o este en cuesti2n codi-i0u1 una -unci2n( 0uizs un tanto 7super-lua7( pero 0ue as "anaba claridad en el c2di"o
int >stado<ecla (t%ucleo 5 %ucleo, int [e!) 6 // )evuelve si una tecla est pulsada o no.

&<

return (%ucleo-.<ecladoW[e!X 00 <=2>)9

&I

() )l bucle rinci al del juego: +ien( ahora viene un tema bastante interesante( # ciertamente crucial. WComo demonios es el bucle principal de un jue"oX +ueno ha# por internet( en particular la p"ina de Jare un pro"ramador de P#ro Studios( 0ue en un artculo su#o pona un ejemplo 7tpico7 de como es un bucle de jue"o. Para este caso no lo us1( por0ue no me acuerdo como era( salvo 0ue haba 0ue escribir mucho TLTB .o( en este ejemplo de jue"o( el bucle es li"eramente mucho ms sencillo( pero en esencia si"ue el es0uema bsico de las cosas. @odo jue"o se divide en dos cosas bsicamente( pintar la pantalla( # ejecutar un ciclo de la l2"ica. 6 no ha# ms. ,e hecho el sonido suele estar relacionados a eventos( 0ue tienen 0ue ver con la l2"ica del jue"o( # por eso se suelen meter dentro de esa parte del c2di"o. 5os "r-icos supuestamente tambi1n estn relacionados con la l2"ica del jue"o. Pero lo estn de una -orma distinta( en cierto modo. El sonido como he comentado( est encadenado a eventos normalmente( cuando iniciamos una -ase( ponemos una banda sonora( cuando disparamos( ponemos el sonido del disparo. Sin embar"o el al"oritmo de renderizar la escena( lo 0ue bsicamente hace es co"er todos los datos( interpretarlos # pintar la escena. .o se hace 0ue si tu activas un bot2n se pinte inmediatamente 0ue la puerta se est abriendo. 5o 0ue se hace es cambiar los datos del jue"o( # a medida 0ue estos cambian( se van re-lejando en el renderizado.
void Bucle+rincipal (t%ucleo 5 %ucleo) 6 2int&' $ntervalo 0 @9 2int&' <iempoAnt 0 @9 do6 //<omamos el tiempo de inicio <iempoAnt 0 )ar<ic[s()9 //+intamos la pantalla +intar+antalla(%ucleo)9 //Calculamos cuanto tiempo ha tardado $ntervalo 0 )ar<ic[s() - <iempoAnt9 //H e,ecutamos la lgica en base al tiempo transcurrido >,ecutar*ogica(%ucleo, $ntervalo)9 7 7Dhile(%ucleo-.+artida.>stado)el:uego S0 >(<(A*)9

6 ah est un bucle principal( del jue"o 0ue sirve de ejemplo para este artculo. Pero como se"uramente hab1is observado( ha# al"o ms 0ue dos sentencias. Primero( el jue"o entra en un bucle( 0ue se ejecutar hasta 0ue el estado del jue"o( indi0ue 0ue ha# 0ue salir. 5ue"o pre"untamos cuantos milise"undos lleva ejecutandose el jue"o( # lo almacenamos en @iempo/nt. ,espu1s renderizamos la escena. El si"uiente paso ser calcular el tiempo en milise"undos 0ue se ha tardado en renderizar la escena( para 0ue -inalmente ejecutemos un ciclo de la l2"ica( pasandole la cantidad de ese intervalo de tiempo ocupado por el renderizado. /hora es cuando t) te pre"untas( Wpara 0u1 cojones ha hecho esoX /s de primeras parece una tontera # "anas de complicarse la vida. Pero la realidad es 0ue pensemos 0ue pasara si tan solo ponemos las sentencias de PintarPantalla # Ejecutar5o"ica. +ien( &K

pintaramos la escena # ejecutaramos la l2"ica( justo como 0ueramos. Pero las cosas son mu# bonitas en la teora( no en la prctica. En la prctica nos puede ocurrir dos supuestos. El primero es 0ue la escena se renderiza mu# rpidamente( # cada -rame tarda una miserable cantidad de tiempo. Claro ahora es cuando piensas( "enial ms -ps (-rames per second( ima"enes por se"undo)( ms calidad para mi jue"o. =ale s( est bien tener una tasa de -ps alta( pero el e-ecto indeseado por este tema( es 0ue tan rpido como se te renderice la escena( ser de rpido a la hora de ejecutar un ciclo de la l2"ica. 6 claro( a priori no sabes cuanto va a tardar en pintarse la escena( # puede haber bajones en los -ps o subidas( con lo 0ue tendras un -instro de jue"orl( 0ue se movera como si -uera chi0uito de la calzada a toda leche( # a trompicones. Claro( esto -rancamente es al"o poco deseable( 0ue se")n empiezas la partida te maten. Pero ha# otro posible e-ecto. Oue el renderizado tarde un huevo # parte del otro( como pasa con el ,oo: <( en un ordenador estandar de cual0uier mortal (aun0ue siempre ha# 0uien cree 0ue todo el mundo tiene una n=idia >S?? de esas tan chulas( obviamente eso es 0ue le sobra el dinero( o 0ue no tiene nin")n escr)pulo a la hora de conse"uir ese dinero a cual0uier precio). El e-ecto inmediato de esto( es 0ue nuestro jue"o no solo ira a pedales "r-icamente( sino 0ue la l2"ica tambi1n. Pero sin embar"o( a pesar de ir a pedales el ,oo: <( eso no impide 0ue el bicho 0ue estaba a tomar por culo (no mucho en el ,oo: < con lo 7"randes7 0ue son las pantallas)( al si"uiente -rame est1 a la mitad de camino( # al si"uiente #a te ha#a sodomizado de mala manera. Ciertamente son e-ectos 0ue desearamos a cual0uier costa evitar. Por eso hemos de calcular cuanto estamos tardando en renderizar la escena actual. Por0ue as sabremos si estamos #endo mu# rpido o mu# lento( # podremos controlar mejor la ejecuci2n de la l2"ica. Si se renderiza mu# mu# rpido( podemos hacer 0ue la l2"ica se ejecute menos veces. 6 si va mu# mu# rpido( podemos ejecutar varias veces la l2"ica( para compensarlo. /hora 0uizs te pre"untes al"o como( pero la l2"ica no puede tardar mucho en ejecutarse( como pasa con el renderizado. Es bastante di-cil 0ue tardemos ms en ejecutar la l2"ica( 0ue en renderizar una escena. .ormalmente( suponiendo 0ue nos va#a -luido "r-icamente el jue"o( la l2"ica suele tardar &? veces menos en ejecutarse( 0ue los "r-icos. Esto ocurre "racias a esos apasionantes micros 0ue ejecuta miles de millones de instrucciones por se"undo( con lo 0ue #a tiene 0ue ser mucho lo 0ue calcules para 0ue a0uello va#a a pedales. Sin embar"o los "r-icos( la cantidad de in-ormaci2n 0ue tiene 0ue manejar la tarjeta( puede lle"ar a ser aberrante( por muchos millones de pol"onos 0ue pueda pintar por se"undo. Por0ue lue"o tiene 0ue mapear las te4turas( # aFadir e-ectos chorras como puede ser el bump mappin"( u otros e-ectos 0ue tienen 0ue ir pi4el por pi4el calculando movidas. /l -inal nos encontramos con 0ue hemos tenido 0ue ejecutar una aberrante cantidad de instrucciones( por parte del microprocesado de la tarjeta "r-ica. En S,5 la -unci2n 0ue nos da el n)mero de milise"undos 0ue ha transcurrido desde el inicio de la aplicaci2n es S,5LGet@icks( 0ue para este caso est en la si"uiente -unci2n
2int&' )ar<ic[s (void) 6 return ()*I1et<ic[s()9 7

6 tambi1n ha# otra -unci2n de control del tiempo 0ue nos puede resultar )til( 0ue es S,5L,ela#( 0ue lo 0ue hace es suspender la ejecuci2n del jue"o durante un determinado tiempo( normalmente medido en milise"undos. Esto puede sernos )til para situaciones del &>

tipo estamos capturando pulsaciones del teclado( # para 0ue de una pulsaci2n no nos meta :85 caracteres( capturamos un caracter # lanzamos un dela#( para 0ue le de tiempo al usuario de despulsar la tecla actual # pulsar la si"uiente. En el jue"o est metida en esta -unci2n
void )ela! (2int&' ms) 6 ()*I)ela!(ms)9 7

&D

*) La gestin del men+: .ormalmente todos los jue"os suelen tener un men)( donde hacer el mono un po0uito. Se"uramente ha# miles de -ormas de implementar un men)( aun0ue para esta ocasi2n lo 0ue #o pens1 0ue sera ms l2"ico # coherente( sera el implementar una m0uina de estados. Bna m0uina de estados( para el 0ue no lo sepa( es un conjunto de estados( 0ue ejecutan un al"oritmo cada uno de ellos( # 0ue dependiendo de 0ue se vuelvan ciertas determinadas condiciones( pasan de un estado a otro. En este caso por ejemplo( nosotros empezamos en el estado 7:en) de .ueva Partida7. Cuando estamos en este men) tanto en la -unci2n de renderizado( como en la de ejecuci2n de la l2"ica( ejecutaremos solo el c2di"o espec-ico para nuestro estado (a veces dos estados pueden compartir justo el msmo c2di"o( como pasa para la l2"ica de la pantalla de ;pciones # la de Puntuaciones( aun0ue usan distinto c2di"o para renderizar la escena por ejemplo). Sin embar"o cuando se cumple al"una condici2n determinada( 0ue normalmente nosotros pro"ramaremos ese n)mero -into de condiciones de cambio( como puede ser por ejemplo hacer click sobre el bot2n de ;pciones( entonces cambiaremos el estado de la m0uina a un nuevo estado( en esta ocasi2n al estado 7Pantalla de ;pciones7. 5as m0uinas de estados se suelen usar sobre todo en inteli"encia arti-icial( para dar un determinado comportamiento -inito( a una entidad espec-ica. Pero en este caso( lo he usado para realizar el men). WPor 0u1X 5a codi-icaci2n clsica para hacer un men)( suele ser pintarlo( ele"ir una opci2n # ejecutar una opci2n se")n esa opci2n. +ueno( no da nin")n problema eso( hasta 0ue 0ueremos volver a llamar al 7:en) de Continuar la partida7. Se podra hacer por el metodo 7clsico7( sin 0ue supusiera muchos problemas( pero por ejemplo en el jue"o( tras morir pasamos a la pantalla para meter el nombre( # lue"o a la de puntuaciones. En la m0uina de estados solo es indicar 0ue al -inalizar el jue"o( pasemos al estado de 7:eter el .ombre7( pero en la concepci2n clsica terminara el jue"o # tendramos 0ue posiblemente montar un pollo del cop2n para poder llamar a la -unci2n para "estionar el tema de dichas secciones del men)( sin 0ue lo ha"a cuando llamamos al men) desde el jue"o. Pero bueno( en esto #a entra dentro de las pre-erencias de cada uno. 6 el caso es 0ue( con la m0uina de estados por ejemplo( podemos separar los al"oritmos de renderizado( de los al"oritmos 0ue comprenden la l2"ica del jue"o (# por consi"uiente la l2"ica del men)). Pero vamos( repito 0ue se"uramente e4istan otras muchas -ormas de hacer el men)( # poder lle"ar a hacer cosas tan chulas como al"unos men)s( en los 0ue das al escape( # si"ue el jue"o su curso (# te dan cerita mientras tratas de cambiar las teclas... 0ue listos).

&S

1,) -estin del intado de la antalla: /hora lle"a un punto ciertamente importante sobre la S,5. El como pintar en el bu--er de pantalla. Como se ha visto antes( hemos creado nuestro "enial bu--er de pantalla( hemos car"ado las te4turas( pero... WPero como pintamos por ejemplo las ima"enes sobre el bu--erX Es mu# sencillo( solo hemos de llamar a la -unci2n S,5L+litSur-ace. Esta -unci2n tiene I parmetros
int ()*IBlit(urface(()*I(urface 5 imgorigen, ()*I=ect 5 areaorigen, ()*I(urface 5 imgdestino, ()*I=ect 5 areadestino)9

Esto traducido es al"o tal 0ue nosotros tenemos una area en la ima"en ori"en( 0ue vamos a pe"ar en un area de la ima"en destino. Por ejemplo( nosotros 0ueremos pe"ar la ima"en del -ondo de la pantalla( sobre el bu--er principal de la pantalla. Justo en este caso da la casualidad de 0ue el area de ori"en es toda la ima"en en s( con lo 0ue cuando 0ueramos 0ue el area sea toda la ima"en en s( podremos pasarle .B55 como parmetro a la -unci2n. ,e -orma similar ocurre con el area destino( en este caso 0ueremos 0ue se pe"ue sobre todo el bu--er de pantalla( con lo 0ue pasaremos .B55 al parmetro areadestino. /s 0ue con esto nos podemos crear una -unci2n para poner te4turas en el bu--er de pantalla
int +oner$magen (t%ucleo 5 %ucleo, ()*I(urface 5 imgsrc, ()*I=ect 5 destino, ()*I=ect 5 origen) 6 // +ega en el area destino del buffer de pantalla, el area origen del // buffer imgsrc. return ()*IBlit(urface(imgsrc, origen, %ucleo-.Buffer+antalla, destino)9 7

6 para poner la ima"en del -ondo la llamaramos de la si"uiente -orma


+oner$magen(%ucleo, %ucleo-.<eRturas.imgCondo, %2**, %2**)9

Sin embar"o para casos como el de pintar una nave como puede ser la del ju"ador( en el bu--er de pantalla( aun0ue vamos a pintar toda la te4tura en este caso( no la vamos a pintar sobre toda la pantalla( eso 0uedara poco propicio. Primero prepararamos un S,5LGect (S,5LGect destinoR)( donde indicaremos el area de destino( # llamaremos a la -unci2n as
+oner$magen(%ucleo, %ucleo-.<eRturas.img(pi[e, ;destino, %2**)9

; tambi1n podemos indicar una re"i2n dentro de la ima"en( como pasar con las te4turas de los malos( los disparos o las -uentes. En estas no 0uerremos pintar toda la ima"en( tan solo una re"i2n( # por ello -ue 0ue nos creamos a la hora de car"ar las te4turas( los vectores 0ue almacenaban las re"iones 0ue haba en cada te4tura( para 0ue lue"o al pintarlas( usemos estos S,5LGect almacenados en el vector( para indicar 0ue parte 0ueremos pintar. Pero tras hacer esto( es posible 0ue no te sal"a nada en pantalla. Eso se debe a 0ue usamos la t1cnica del doble bu--er( como se e4plic2 arriba a la hora de inicializar el S,5. Bna vez ha#amos pintado toda la escena( sin duda al"una 0uerremos mostrarla por pantalla. Para ello hemos de llamar a S,5LClip( pasandole como parmetro el bu--er primario de pantalla

&U

int ActualiGar+antalla (t%ucleo 5 %ucleo) 6 // ActualiGa el buffer de la pantalla. return ()*IClip(%ucleo-.Buffer+antalla)9 7

6 con esto podremos pintar ima"enes en pantalla de -orma bastante e-icaz. Pero claro( no todo el campo es ore"ano( # aun0ue con solo dos -unciones podemos hacer todo un mundo( ese mundo tiene una serie de patrones. Por ello en el jue"o a la hora de pintar la escena tendremos 0ue pensar 0u1 es lo 0ue tenemos 0ue pintar( # como tendremos 0ue hacerlo (con 0ue orden por ejemplo). Por ejemplo en el tema del men) para la pantalla de ' 7:en) de .ueva Partida7 Pintaremos la ima"en donde tenemos el men)( # lue"o comprobaremos una por una( si el cursor del rat2n est dentro de al")n area comprendida por al")n 7bot2n7. Si lo est( pintaremos encima de dicha area( una ima"en con el bot2n cambiado de color (para 0ue se note 0ue est seleccionado). ' 7;pciones7 Pintaremos la ima"en de -ondo 0ue deseemos( # para este caso puse las instrucciones en esta secci2n( por0ue para hacer una secci2n de opciones es mucho curro. /s 0ue el poner las instrucciones simplemente es pintar cadenas de te4to( en una coordenada espec-ica. ' 7Puntuaciones7 Pintamos la ima"en de -ondo 0ue 0ueramos( # vamos pintando las cadenas de te4to( sobre este -ondo( para mostrar las puntuaciones. ' 7:en) de Continuar Partida7 Es como el de 7.ueva Partida7( solo 0ue tras pintar la ima"en del men)( pintaremos encima del bot2n de 7.ueva Partida7( otro bot2n con el te4to cambiado( 0ue pon"a 7Continuar Partida7( # cuando el rat2n est1 encima de los botones( usaremos una ima"en espec-ica como en el men) de 7.ueva Partida7( para 0ue parezca 0ue est seleccionado. ' 7Poner .ombre7 Pintamos un -ondo( # pintamos al")n mensaje de te4to( # lo 0ue llevemos del nombre metido. Pero la parte ms interesante es saber como renderizar una escena del jue"o. Como el c2di"o est ah abajo en un link a un zip( no vo# a ponerlo a0u( por0ue es un tanto lar"o. :s 0ue nada vo# a e4plicar un poco a "randes ras"os como -unciona la parte del c2di"o 0ue pinta una escena del jue"o. Primero pintamos el -ondo del jue"o( # lue"o en mi caso he llamado a la -unci2n 0ue pinta el resto. Se poda haber metido la sentencia de pintar el -ondo dentro de la -unci2n Genderizado( pero bueno... tampoco pasa nada por 0ue est1 -uera. Bna vez dentro de Genderizado en este jue"o tenemos bsicamente cuatro cosas el -ondo( los disparos( las naves( # la inter-az del usuario. /s 0ue tenemos primero 0ue pensar en 0ue orden lo vamos a pintar( #a 0ue lo )ltimo 0ue pintemos saldra por encima del resto. Para este caso( lo pintaremos en este orden primero el -ondo( lue"o todos los disparos 0ue e4istan( lue"o todas las naves( # -inalmente la inter-az. Como el -ondo #a lo tenemos pintado( lo primero a hacer en la -unci2n Genderizado es ir disparo por disparo pintandolo sobre la pantalla. ,espu1s iremos nave por nave pintandola sobre la pantalla. 6 -inalmente pintaremos la inter-az( 0ue en este caso es solo un marcador de puntos # uno de vida.

%?

11) -estin de la lgica del juego: +ien as 0ue bsicamente #a sabemos 0ue es lo 0ue tenemos 0ue hacer en la parte del pintado de la escena. En el tema de la l2"ica lo primero 0ue siempre vamos a hacer es actualizar la entrada del teclado # el rat2n. Esto es -undamental( por0ue con eso es con lo 0ue el ju"ador manejar su nave( por ejemplo. /s 0ue dependiendo de en 0u1 secci2n est1 del men)( har lo si"uiente ' 7:en) de .ueva Partida7 Si hacemos click con el bot2n iz0uierdo del rat2n( entonces comprobaremos si el rat2n est dentro del area de al")n bot2n. En caso a-irmativo cambiaremos el estado del jue"o. Si iniciamos una nueva partida( no solo se cambiar el estado( sino 0ue adems inicializaremos las variables 0ue "estionen el tema de la partida. ' 7;pciones # Puntuaciones7 Si damos a la tecla escape volvemos al men) principal( # dependiendo de si hemos empezado una partida o no( volveremo al men) de nueva partida o al de continuar una empezada. ' 7:en) de Continuar Partida7 Si hacemos click con el bot2n iz0uierdo del rat2n( entonces comprobaremos si el rat2n est dentro del area de al")n bot2n. En caso a-irmativo cambiaremos el estado del jue"o. ' 7Poner .ombre7 /0u comprobaremos si el usuario ha pulsado enter( en ese caso comprobamos 0ue la variable 0ue almacena el nombre del ju"ador no est1 vaca. Si no lo est( tratamos de meterla en las puntuaciones( # cambiamos el estado para ir a la pantalla de puntuaciones. Si pulsa el escape pasamos de meter las puntuaciones( # volvemos directamente al men). Para el resto de teclas( llamamos a la -unci2n Capturar.ombre( donde dependiendo de la tecla 0ue ha#amos pulsado( aFadimos un caracter a la variable nombre( # as el usuario puede meter el nombre por teclado. 5a -unci2n ms importante de las 0ue ha# en estos estados es la de iniciar una nueva partida. En esta cambiaremos el estado del jue"o( diremos 0ue vamos al estado de ju"ar( # 0ue hemos iniciado una nueva partida. @ambi1n inicializaremos el nombre # la puntuaci2n del ju"ador. 5as variables para controlar los temas de la sincronizaci2n( la direcci2n hacia donde se mueven las naves # la lista de disparos( son otras variables de las 0ue depende el jue"o de ejemplo. Cinalmente tendremos 0ue inicializar los datos de la nave del ju"ador( # la de los enemi"os tambi1n. 6 no ha# 0ue olvidar crear una semilla( para la "eneraci2n de los n)meros aleatorios( 0ue se"uramente usaremos en cual0uier jue"o( para dar cierta aleatoriedad (aun0ue esos n)meros tienen de aleatorio bastante poco). /s 0ue en el estado del jue"o( a menos 0ue el usuario pulse la tecla escape (con la 0ue pasaremos al estado del men) de continuar partida)( se"uiremos la si"uiente dinmica. Para poder controlar 0ue la l2"ica se ejecute un determinado n)mero de veces (en este caso es %K por se"uido( lo 0ue sera cada I? milise"undos)( el intervalo 0ue hemos recibido por los parmetros lo aFadimos a una variable llamada @iempo/cumulado. Cuando esta ten"a un valor ma#or o i"ual 0ue el tiempo mnimo( 0ue en este caso es I? ms( entonces ejecutaremos un ciclo de la l2"ica del jue"o. ,e hecho la ejecutaremos tantas veces como nos de la divisi2n entera de @iempo/cumulado entre el tiempo mnimo (SK ! I? N % ciclos). Para ello cada vez 0ue terminemos un ciclo( le restaremos I? ms a @iempo/cumulado.
%ucleo-.+artida.<iempoAcumulado /0 $ntervalo9 if(%ucleo-.+artida.<iempoAcumulado .0 <3+3$%) 6 Dhile(%ucleo-.+artida.<iempoAcumulado .0 <3+3$%)

%&

$nterpretar>ntrada(%ucleo)9 >,ecutar$A(%ucleo)9 %ucleo-.+artida.<iempoAcumulado -0 <3+3$%9

7 7

5a )ltima sentencia( donde @iempo/cumulado pasa a valer cero( podramos saltarnosla( # de hecho as el control sera ms e4acto( por0ue los ms 0ue ha#an sobrado de la resta de los ciclos( podran ir acumulandose # podra ser 0ue perdieramos al")n ciclo. Pero para este jue"o perder al")n ciclo de vez en cuando tampoco va a hacer 0ue se ejecute mucho ms lento( tan solo ms o menos I? ms ms lento cada cierto tiempo. El "rueso de la l2"ica( lo -orman dos -unciones 8nterpretarEntrada # Ejecutar8/. En la primera( veremos el estado de la entrada( # en base a ello( decidiremos 0ue es lo 0ue nos est pidiendo el ju"ador 0ue ha"amos. 6 la se"unda ejecuta el comportamiento de todas las entidades del jue"o. Esto es un tema curioso( por0ue en terminos 7absolutos7( no solo los enemi"os tienen una 8/( desde el punto de vista de lo 0ue representa la 8/ en la pro"ramaci2n de jue"os. 5a 8/( ms 0ue un comportamiento inteli"ente( es un patr2n de-inido de comportamiento( por0ue rara es la 8/ de jue"o 0ue tiene una memoria( #a 0ue eso consumira muchos recursos. Por ello un disparo de un arma tiene una 8/( como por ejemplo el cohete 0ue lanzas en el Ouake. Por ello cada vez 0ue ejecutamos la -unci2n para realizar un ciclo de la l2"ica de todas las 8/( esto bsicamente si"ni-ica( realizar un ciclo en el comportamiento de todas las entidades del jue"o. 8nterpretarEntrada bsicamente lo 0ue hace es comprobar el estado de las teclas -lecha iz0uierda( -lecha derecha( # espacio. Si pulsamos la -lecha iz0uierda( la nave tendr 0ue moverse a la iz0uierda (decrementar el valor en su eje 4)( # si pulsamos la -lecha derecha( la nave se desplazar hacia la derecha (incrementando el valor en su eje 4). Pero para este jue"o hemos puesto al"unas restricciones( de cual es el valor m4imo en el eje 4( sobretodo para 0ue no se sal"a de la pantalla la nave( 0ue sera todo un desastre. /s 0ue si adems pulsamos la tecla espacio( la nave "enerar un disparo( llamando a la -unci2n Crear,isparo( 0ue crea un nuevo nodo de tipo t,isparo( en la lista doblemente enlazada 0ue se usa en el jue"o. Ejecutar8/ es una -unci2n li"eramente ms elaborada. En ella lo primero 0ue tenemos 0ue hacer es ejecutar un ciclo de comportamiento de todas las entidades de tipo disparo. Para ello iremos nodo por nodo de la lista de disparos( comprobando primero si han colisionado con al"o( en ese caso in-lin"imos el daFo 0ue produce el disparo( # destruimos el disparo. Suponiendo 0ue no colisione con nada( hacemos avanzar el disparo hacia la direcci2n contraria del bando 0ue lo ha lanzado. 6 si se sale de los limites mnimo # m4imo 0ue le indicamos para el eje #( destruimos el disparo. @ras esto comprobamos 0ue el ju"ador no est1 muerto( en caso de estarlo se -inaliza el jue"o. Pero si si"ue vivo miramos a ver si si"ue viva al"una nave. En el caso de 0ue est1n todas muertas( pasamos al si"uiente nivel( llamando a Si"uiente.ivel( 0ue cambiar los valores de al"unas variables( # volver a inicializar el contenido de la ma#ora. 6 ahora vamos a comprobar nave por nave enemi"a( siempre 0ue esta est1 viva lo si"uiente. Primero avanzaremos un paso en la direcci2n actual( # decidiremos de -orma aleatoria si dispara o no. En este caso en concreto la e4presi2n base para disparar o no( viene dad por lo si"uiente (rand() \ &??) devuelve un n)mero entre ? # UU( # en una variable de la nave llamada -rec( tenemos el porcentaje m4imo para 0ue la nave dispare( %%

esto 0uiere decir( 0ue si el n)mero devuelto es menor o i"ual 0ue -rec( entonces la nave disparar. Pero esto no termina ah. @enemos 0ue volver a recorrer el vector de naves enemi"as( comprobando de entre las vivas( si al"una ha sobrepasado los limites mnimo (si se diri"e a la iz0uierda) o m4imo (si se diri"e a la derecha) del eje 4. Si e4iste al"una nave 0ue cumpla eso( tendremos 0ue hacer 0ue todas las naves vivas avancen un paso hacia abajo. 6 comprobar de paso si al"una nave ha lle"ado hasta el -ondo de la pantalla( con lo 0ue si"ni-icara el -in de la partida. *a# un detalle 0ue todava no he e4plicado( # es 0ue una de las variables para controlar los ciclos de ejecuci2n Cuenta/ct # Cuenta:a4( son empleadas para determinar cada cuantos ciclos las naves enemi"as ejecutan su 8/. Cuenta:a4 indica cuando ocurre eso( empieza siendo cada %K ciclos de l2"ica( # si sobrevive el ju"ador %K 7-ases7( este se ve reducido a &( con lo 0ue en cada ciclo se ejecutara la 8/. Cuenta/ct es el contador 0ue lleva el n)mero de ciclos 0ue ha pasado desde la )ltima vez 0ue se ejecut2 la 8/( cuando vale i"ual o ms 0ue Cuenta:a4( se ejecuta la /8. 6 tras terminar de ejecutar la 8/( Cuenta/ct pasa a valer ?( para empezar de nuevo con la cuenta de ciclos de l2"ica.

%<

12) )l tema de intar te&to en la antalla: 6a como nota -inal( una -unci2n bastante )til( sobre todo para el men) es la -unci2n PintarCadena. Esta -unci2n pinta en el bu--er principal una serie de ima"enes con transparencia( en las 0ue -ijate t)( casualmente ha# una letra del abecedario. Pero primero tenemos 0ue tener en una ima"en todos los caracteres 0ue vamos a usar en el jue"o( medirlos( # constuir automticamente con una lista de los anchos de cada caracter( un vector de areas para saber donde esta cada ima"en. Entonces en la -unci2n PintarCadena( le pasamos una coordenada (4( #)( # una cadena de caracteres. =amos uno por uno( analizando 0ue caracter es # calculando en 0ue posici2n del vector de areas( est el area 0ue corresponde con la ima"en del caracter actual. Bna vez tenemos esa posici2n( pintamos esa re"i2n con el caracter dibujado( en una re"i2n destino del bu--er de pantalla( -ormado dicho S,5LGect( por las (4( #) 0ue nos pasaron en los parmetros( # el ancho # alto de la re"i2n( 0ue hemos obtenido del vector de areas. Cinalmente tras pintarla( aFadimos a la 4 el ancho actual del caracter 0ue acabamos de pintar( para 0ue no se pinten todos encima de la misma posici2n. 6 as es como se sola hacer para dibujar te4to en un jue"o( pero por suerte para todos a0uellos 0ue no 0uieran morirse de asco( midiendo letra por letra como me pas2 a m... S,5 tiene otra e4tensi2n 0ue se llama S,5Ltt-( 0ue sirve para manejar las -uentes true$ t#pe( 0ue vienen con el sistema operativo( o con tu propio jue"o. Con lo 0ue nos ahorraremos mucho trabajo( # tendremos a nuestra disposici2n muchsimas prestaciones. Pero siempre 0ueda el m1todo clsico( en caso de no tener nada ms 0ue el propio in"enio.

%I

You might also like