Ver la versión completa : Haciendo un pong...
Hola, pues eso, estoy probando de hacer un pong... pero me he quedado atascado, concretamente no se como hacer para mover la pala. Porque la pala se mueve pero no se borra la anterior osea en mas de moverse parece que se alargue. Pego el código para ver si alguien me ayuda. Gracias y Salu2.
#include <SDL/SDL.h>
#include <stdio.h>
#include <stdlib.h>
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define SCREEN_DEPTH 16
int main()
{
SDL_Surface *screen;
SDL_Event event;
SDL_Surface *fondo, *red, *pala1, *pala2, *pelota;
SDL_Rect flocalizacion, rlocalizacion, p1localizacion, p2localizacion, blocalizacion;
int done=0;
/* Inicializar SDL */
SDL_Init(SDL_INIT_VIDEO);
/* Inicializar pantalla */
screen=SDL_SetVideoMode (SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, SDL_SWSURFACE);
// cargamos el fondo (fondo.bmp)
fondo=SDL_LoadBMP("fondo.bmp");
//localizacion del fondo
flocalizacion.x=0;
flocalizacion.y=0;
flocalizacion.w=fondo->w;
flocalizacion.h=fondo->h;
SDL_BlitSurface(fondo, NULL, screen, &flocalizacion);
//cargamos la red del centro (red.bmp)
red=SDL_LoadBMP("red.bmp");
//localizacion de la red
rlocalizacion.x=155;
rlocalizacion.y=0;
rlocalizacion.w=red->w;
rlocalizacion.h=red->h;
SDL_BlitSurface(red, NULL, screen, &rlocalizacion);
//cargamos la pala 1 (pala1.bmp)
pala1=SDL_LoadBMP("pala1.bmp");
//localizacion de la pala1
p1localizacion.x=10;
p1localizacion.y=80;
p1localizacion.w=pala1->w;
p1localizacion.h=pala1->h;
SDL_BlitSurface(pala1, NULL, screen, &p1localizacion);
//cargamos la pala 2 (pala2.bmp)
pala2=SDL_LoadBMP("pala2.bmp");
//localizacion de la pala2
p2localizacion.x=295;
p2localizacion.y=80;
p2localizacion.w=pala2->w;
p2localizacion.h=pala2->h;
SDL_BlitSurface(pala2, NULL, screen, &p2localizacion);
//cargamos la pelota (pelota.bmp)
pelota=SDL_LoadBMP("pelota.bmp");
//localizacion de la pelota
blocalizacion.x=150;
blocalizacion.y=110;
blocalizacion.w=pelota->w;
blocalizacion.h=pelota->h;
SDL_BlitSurface(pelota, NULL, screen, &blocalizacion);
while(!done)
{
//buscamos los eventos
while(SDL_PollEvent(&event)) {
//si se ha pulsado una tecla
if (event.type==SDL_KEYDOWN) {
//miramos que tecla es
switch(event.key.keysym.sym){
case SDLK_ESCAPE: done=1;
break;
case SDLK_UP:
if (p1localizacion.y>0){
p1localizacion.y-=15;
SDL_BlitSurface(pala1, NULL, screen, &p1localizacion);
printf("\nvalor de %i\n", p1localizacion.y);
}
break;
}
}
}
/* Actualizar la pantalla (doble buffer) */
SDL_Flip(screen);
}
}
Haz el blit del fondo en cada vuelta para limpiar la pantalla.
Has de pensar que esto es como un collage, a cada frame has de "montar" todo el tinglado y "pegar" el nuevo fotograma sobre el anterior, de modo que si no vuelves a poner el fondo aun se ve de fondo el anterior frame.
Me explico con el ano.
eso me ha parecido a mi tb...
Lo que tu haces es lo siguiente
00 - Inicializar
Fondo
01 - Dibuja
pala
02 - Dibuja+1i
pala+1
...
Lo que debes hacer es
00 - Inicializa
fondo
pala
01 - Dibuja
Fondo
pala
02 - Dibuja+1
Fondo
pala+1
....
Creo que se ha entendido bien, no?
Asias a los dos, ya funciona he puesto el SDL_BlitSurface para cada cosa al final del bucle justo despues del SDL_Flip(screen).
Porque dibujar todo el fondo cuando con la porcion q ocupaba la pala en el frame anterior va sobrado?, al menos así lo hacía yo :).
Saludos
Pues eso, como te dice Kabanya no tienes que dibujar todo el fondo sino solo la parte donde estaba la pala en el momento anterior. Se nota muchísimo la diferencia de rendimiento: dibujar en la pantalla es lo más lento con diferencia en cualquier juego.
Por otro lado, diría que te falta una llamada al reloj, ¿no? Una llamada tipo "duerme durante 33ms". Así 1.- dejas que la consola haga otras cosas que también son necesarias como tocar una músiquilla de fondo 2.- permites sincronizar el juego, sobretodo lo echarás de menos cuando quieras controlar la velocidad de la pelota. Si quieres un juego a 30FPS, necesitarás dormir 1000ms/30FPS=33ms. Pero creo recordar que SDL tiene una llamada tipo clock->frame(30) que ya duerme el tiempo necesario para mantener unos FPS. Hazla en el bucle principal justo después de hacer un flip() de la pantalla.
Y como se haria eso que decis... sorry soy nuevo en esto de SDL. Esque estoy usando imagenes BMP. Salu2.
^MiSaTo^
17/07/2007, 15:52
Pues eso, como te dice Kabanya no tienes que dibujar todo el fondo sino solo la parte donde estaba la pala en el momento anterior. Se nota muchísimo la diferencia de rendimiento: dibujar en la pantalla es lo más lento con diferencia en cualquier juego.
Por otro lado, diría que te falta una llamada al reloj, ¿no? Una llamada tipo "duerme durante 33ms". Así 1.- dejas que la consola haga otras cosas que también son necesarias como tocar una músiquilla de fondo 2.- permites sincronizar el juego, sobretodo lo echarás de menos cuando quieras controlar la velocidad de la pelota. Si quieres un juego a 30FPS, necesitarás dormir 1000ms/30FPS=33ms. Pero creo recordar que SDL tiene una llamada tipo clock->frame(30) que ya duerme el tiempo necesario para mantener unos FPS. Hazla en el bucle principal justo después de hacer un flip() de la pantalla.
¿Puedes explicarme a mi también lo del reloj?
¿Es decir para qué sirve exactamente? Esque no lo he entendido del todo. Thanks
tal y como lo explica significa que no tiene poque andar continuamente dibujando en la pantalla y leyendo las entradas. Parando 33ms se consigue un buen framerate, y ademas sobra tiempo ejecutar otras cosas como puede ser ejecutar la IA del adversario, la musica u otros procesos necesarios...
tal y como lo explica significa que no tiene poque andar continuamente dibujando en la pantalla y leyendo las entradas. Parando 33ms se consigue un buen framerate, y ademas sobra tiempo ejecutar otras cosas como puede ser ejecutar la IA del adversario, la musica u otros procesos necesarios...
Ok voy a probar, pero en principio era para hacer mas que un hello world :).
Como dice Jurk, el reloj sirve para no estar continuamente haciendo cosas si no necesitas dibujar tanto, y la consola puede aprovechar ese tiempo para otras funciones como la música, o también para sincronizar los objetos del juego.
Es evidente para qué sirve dejar a la consola libre para que haga otras cosas: tocar la música, acceder a disco o simplemente bajar la frecuencia de funcionamiento para ahorrar batería. Siempre que puedas, deja descansar la consola y ella te lo agradecerá.
¿Y para qué sirve sincronizar? Verás que los emuladores intentan funcionar a 60 FPS. Si funcionan a menos, van lentos, y si funcionan a más se ven acelerados. Imagina que quieres que la pelota del Pong se mueva 100 pixeles cada segundo y en cada pasada por el bucle la mueves 5. Así, necesitas pasar 20 veces y solo 20 por el bucle principal cada segundo. Lo malo es que tal como está no hay forma de controlarlo: a lo mejor pasa 20, a lo mejor 50, si alguna vez tiene que hacer mucha cosa como acceder a disco solo pasará 30 y el resto del tiempo 50... Si no quieres volverte loco por la sincronización, lo mejor es usar relojes para controlarla.
En DIV/Fenix el comando es set_fps(30), y luego dentro de la llamada frame del bucle principal se encarga de esperar el tiempo necesario para ofrecer 30FPS de forma transparente al programador. Lo hace con relojes, claro. Con pygame (una fina capa de Python sobre SDL) el comando es pygame.time.clock.tick(30) y descansa el tiempo necesario para conseguir 30FPS.
¿Y en SDL para C a pelo?
¡Ay, no existe un comando parecido! Puedes mirar las fuentes de la librería Pygame para que veas cómo lo han implementado ellos (http://www.seul.org/viewcvs/viewcvs.cgi/trunk/src/time.c?rev=1022&root=PyGame&view=markup función clock_tick())
Básicamente es calcular el tiempo necesario para descansar en función del tiempo de la última llamada al reloj y llamar a SDL_Delay(). En pseudocódigo:
void tick(int fps){
int diff, delay;
diff=SDL_GetTicks()-last_clock;
delay=1000/fps-diff;
SDL_Delay(delay);
last_clock=SDL_GetTicks();
}
Y hay que llamar a esta función en el bucle principal justo después de la llamada a Flip()
En cuanto a dibujar solo la parte de la pantalla que ha cambiado, se nota muchísimo en el rendimiento del juego. Quizá en un juego tan pequeño como Pong no se note demasiado, pero en cuanto hagas algo más complejo con mucho movimiento por pantalla verás la bajada de rendimiento. Una de las razones de que los programas en Fénix tengan que funcionan a esas frecuencias tan revolucionadas que se beben las pilas es porque no tienen ninguna forma de pintar solo la parte de la pantalla que ha cambiado, pero tú estás trabajando en C y ahí tienes todo el poder :)
Créeme, en cuanto pongas a la pelota moviéndose por la pantalla te darás cuenta de que necesitas algo así. Y si te preocupan las pilas de la GP2X, también.
Por otro lado, diría que te falta una llamada al reloj, ¿no? Una llamada tipo "duerme durante 33ms". Así 1.- dejas que la consola haga otras cosas que también son necesarias
te complicas la vida optimizando que solo se actualice el trozo de fondo de la pala, porque es mas leento, y luego limitas la velocidad artificialmente? curioso :) bueno esta bien que limites a 30fps para estar estable, pero para conseguir 30fps no te compliques la vida los vas a conseguir optimices o no ;) (si dijeras conseguir 60fps eso si habria que optimizar)
Aiken
< - >
CEn cuanto a dibujar solo la parte de la pantalla que ha cambiado, se nota muchísimo en el rendimiento del juego. Quizá en un juego tan pequeño como Pong no se note demasiado, pero en cuanto hagas algo más complejo con mucho movimiento por pantalla verás la bajada de rendimiento.
si hay mucho movimiento en la pantalla tendras que redibujar a trozos casi toda la pantalla, aun peor que redibujarla entera de golpe.
No se, te veo muy convencido y casi me convences pero no del todo.
A parte de eso me parece recordar que las funciones de SDL ya hacen automaticamente un Update solo de lo que ha cambiado, asi que que lo hagas tu a mano no tiene mucho sentido. Creo que las funcones se llama Update_Rects o algo asi, aunque creo que la flip tambien las llamaba automaticamente (no estoy seguro).
Aiken
< - >
¿Y en SDL para C a pelo?
Básicamente es calcular el tiempo necesario para descansar en función del tiempo de la última llamada al reloj y llamar a SDL_Delay(). En pseudocódigo:
void tick(int fps){
int diff, delay;
diff=SDL_GetTicks()-last_clock;
delay=1000/fps-diff;
SDL_Delay(delay);
last_clock=SDL_GetTicks();
}
Y hay que llamar a esta función en el bucle principal justo después de la llamada a Flip()
Y en cuanto a la funcion que propones, mide Ticks es decir ciclos del procesador no mide tiempo. Asi que como dependiendo de la velocidad del procesador hara mas o menos ticks en 1segundo, pues la funcion no sirve de nada. Pues a diferentes velocidades marcara FPS diferentes (no los 30fps que tu quieres) :(
Aiken
< - >
En DIV/Fenix el comando es set_fps(30), y luego dentro de la llamada frame del bucle principal se encarga de esperar el tiempo necesario para ofrecer 30FPS de forma transparente al programador. Lo hace con relojes, claro.
la gp2x no tiene relojes.
Aiken
Y en cuanto a la funcion que propones, mide Ticks es decir ciclos del procesador no mide tiempo. Asi que como dependiendo de la velocidad del procesador hara mas o menos ticks en 1segundo, pues la funcion no sirve de nada. Pues a diferentes velocidades marcara FPS diferentes (no los 30fps que tu quieres) :(
Aiken
< - >
la gp2x no tiene relojes.
Aiken
Tu mismo te respondes, majete... si sabes la frecuencia del micro, y si sabes los tics, puedes calcular el tiempo... de este modo puedes utilizar los tics como timers...
¿solo por curiosidad, Aiken, tu no sabes nada sobre programar microcontroladores en ensamblador, verdad? Porque segun el cristal que le enganches, va mas rapido o mas lento... pero sabiendo el cristal que tienes, puedes utilizar un contador de tics (paradoja!! llamado timer en los microcontroladores...) para que sea un reloj...
Creo que la GP2X si que tiene reloj, lo que pasaba es que no había pila, y por lo tanto, no se podía guardar la hora.
Pero una vez encendida si que está disponible y si que se podría hacer la medición de tiempo.
Había por ahí un tema sobre el reloj para hacer un juego en que contara el tiempo que ha pasado con la consola apagada.
te complicas la vida optimizando que solo se actualice el trozo de fondo de la pala, porque es mas leento, y luego limitas la velocidad artificialmente?
Sí. La limitación de velocidad artificial tiene dos motivos fundamentales: sincronizar las imágenes del juego, y bajar la velocidad a la que funciona. La sincronización de las imágenes es vital y verás que los emuladores usan técnicas como la limitación de FPS y el frameskip para mantenerla. Si quieres pasar 60 veces por segundo por el bucle principal del juego, y solo 60, tienes que usar temporizadores.
La otra razón para limitar la velocidad pero a la vez optimizar el código es poder funcionar a una menor frecuencia. Los juegos de Fenix funcionan por encima de los 233Mhz porque no optimizan el dibujado en la pantalla. El emulador de Megadrive puede funcionar por debajo de 150Mhz. ¿Qué es lo que prefieres como usuario gastapilas?
si hay mucho movimiento en la pantalla tendras que redibujar a trozos casi toda la pantalla, aun peor que redibujarla entera de golpe.
En esto tienes toda la razón y se me entiende muy mal. Cuando digo "mucho movimiento", entiéndeme en términos de velocidad. Si quieres mover una pelota 100 pixeles cada segundo, puedes hacerlo de dos maneras: 50FPS moviendo 2 pixeles por images, con un movimiento muy suave, o 10FPS moviendo 10 pixeles por images, donde verás los saltos bruscos. Optimizando el código para que solo dibuje lo que ha cambiado podrás aumentar los FPS, y los movimientos serán más suaves.
A parte de eso me parece recordar que las funciones de SDL ya hacen automaticamente un Update solo de lo que ha cambiado,
No, no lo hacen. Hay dos funciones en SDL: SDL_Flip(), que dibuja toda la pantalla, y SDL_UpdateRects(), que dibuja solo las partes que han cambiado... ¡pero tienes que decirle qué partes son!
Esta optimización no es fácil de hacer y hay que diseñar el juego desde el principio pensado en UpdateRects() y no en Flip() No se lo recomiendo a los que empiecen en SDL, pero en algún momento tendrán que aprender a utilizarlo.
Y en cuanto a la funcion que propones, mide Ticks es decir ciclos del procesador no mide tiempo.
Según la documentación de SDL, la función SDL_GetTicks() devuelve el número de milisegundos que ha pasado desde que se inición el sistema SDL. Así que mide milisegundos, no ticks de procesador (sí, el nombre está mal escogido)
la gp2x no tiene relojes.
La Gp2x sí tiene un reloj que funciona por defecto a 200MHz :) Linux usa ese reloj para llevar otro más "human friendly", al que podrás acceder si ejecutas 'date' con el telnet o la shell. Lo que sucede es que el reloj deja de funcionar cuando la consola se apaga, pero tener, tiene. Y los programadores pueden (y lo hacen) usarlo para temporizar.
Esta optimización no es fácil de hacer y hay que diseñar el juego desde el principio pensado en UpdateRects() y no en Flip() No se lo recomiendo a los que empiecen en SDL, pero en algún momento tendrán que aprender a utilizarlo.
Ahi es donde queria llegar, no creo que sea una buena recomendacion (por no ser necesaria) para un usuario que esta empezando a programar.
Según la documentación de SDL, la función SDL_GetTicks() devuelve el número de milisegundos que ha pasado desde que se inición el sistema SDL. Así que mide milisegundos, no ticks de procesador (sí, el nombre está mal escogido)
puede ser, quizas dependiendo en que sistemas la funcion coja ticks o milisegundos, de hecho seria facil de comprobar, haz la misma prueba a 60Mhz y a 250Mhz y a ver si va igual de rapido habiendo limitado a 30fps.
¿solo por curiosidad, Aiken, tu no sabes nada sobre programar microcontroladores en ensamblador, verdad? Porque segun el cristal que le enganches, va mas rapido o mas lento... pero sabiendo el cristal que tienes, puedes utilizar un contador de tics (paradoja!! llamado timer en los microcontroladores...) para que sea un reloj...
PD. No hay que saber de programar microcontroladores para saber que la mayoria de los micros aceptan varias frecuencias.
PD2. Y si no sabes programarlos en ensamblador, ya no vale? Tu no sabes nada de que se puede programar microcontroladores en Basic, en C, y algun otro lenguaje de mas alto nivel no? :D
Aiken
La limitacion de FPS y el frameskip no tienen absolutamente nada que ver :confused: el frameskip es, como el nombre indica para que el emulador se salte frames sin pintar, que tiene poco que ver con sincronizar
Definiremos "sincronizar" como pasar 60 veces por segundo por el bucle principal del juego, y solo 60. Ni una más, ni una menos. Nota que no es igual a 60FPS si no dibuja en todas las pasadas.
El frameskip es precisamente esto último: con frameskip el emulador ejecuta el bucle principal, calcula las nuevas posiciones de los objetos móviles, lo hace todo... excepto dibujar en la pantalla. Así mantienes la sincronización del juego y el juego no se ve lento, sino que los gráficos se dibujan a saltos: ¡el monstruito que estaba delante de repente está detrás! Lo gracioso es que la velocidad es adecuada (tiempo sincronizado), pero ha dado un salto en el espacio. Los juegos empiezan a ir lentos ("pasan menos de 60 veces por el bucle principal") cuando el frameskip no es suficiente para mantener la velocidad.
La limitación de FPS es el efecto contrario: la máquina va sobrada para hacer 60FPS, pero no puede hacerlos todos al principio del segundo y que le sobren milisegundos al final: se tienen que hacer paradas entre imagen e imagen.
La sincronizazión puedes observarla en los (muy) antiguos juegos de MSDOS, que no utilizaban relojes y estaban pensados para una frecuencia determinada. Para jugar actualmente a ellos tienes que utilizar programas que ralenticen el ordenador.
Puede hacer un programa sin considerar el reloj, y para empezar es lo más sencillo. ¿Qué consecuencias tendrá? Que no será portable. Es decir, que le funcionará bien en el ordenador, pero lentísimo en la GP2X, o bien en la GP2X y muy rápido en el ordenador. Pero si no lo importa la portabilidad en su primer programa, y está dispuesto a un esfuerzo adicional en depuración (no puede depurar en su ordenador), pues adelante con el aprendizaje. Pero en su siguiente programa... ¡que se facilite la vida con los relojes!
Buff hay que ver lo complicado que es todo... De momento tengo las funciones esas que pintan al final del bucle principal, sólo asi he conseguido lo que quería... porque lo demas no lo acabo de entender, ahora estoy pensando en mover la pelotita alguna idea?
joan88, en tu código justo detrás del while(){} de los eventos y dentro del bucle principal haz algo así:
pelotalocalizacion.x+=velx;
pelotalocalizacion.y+=vely;
/* Aquí los ifs para controlar rebotes de la pelota y salidas de pista */
SDL_BlitSurface(pelota, NULL, screen, &pelotalocalizacion);
Por supuesto, tendrás que cargar el gráfico de la pelota e iniciar las velocidades y la posición al centro de la pantalla. Hazlo justo después de cargar los gráficos de las palas.
¿Y qué valor de velx y vely pones? Como no usas relojes (y con Aiken hemos convenido que mejor no los uses... por esta vez), tendrás que ponerlos a ojo y hacer pruebas hasta que des con la velocidad que te satisfaga. Como consejo, elige un valor aleatorio para vely entre -MAXVELY y +MAXVELY, y para velx escoge al azar VELX ó -VELX
Buff hay que ver lo complicado que es todo... De momento tengo las funciones esas que pintan al final del bucle principal, sólo asi he conseguido lo que quería... porque lo demas no lo acabo de entender, ahora estoy pensando en mover la pelotita alguna idea?
jaja, ves como era innecesario meterle al pobre chaval algo que pertenece al capitulo optimizar, capitulo al que casi nadie llega y aun asi programa sus jueguillos :D jeje
Aiken
< - >
Como no usas relojes (y con Aiken hemos convenido que mejor no los uses... por esta vez)
Eso eso, que si no lo aprovamos nosotros no se puede hacer :D jeje no agobiemos al pobre con esas cosas.
una vez que consiga hacer moverse la pelota, es bastante sencillo y hasta intiutivo hacer que mantenga la velocidad constante, incluso sin saber lo que es un timer o reloj ni nada, simplemente "esperar" aque vuelva a tocar pintar ;)
Aiken
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.