Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





[SDL | C++] Problema con clases y SDL_BlitSurface

Iniciado por kittie4man, 22 de Enero de 2008, 01:19:04 PM

« anterior - próximo »

kittie4man

HOla a todos!!

Mi nombre es JuanPablo y soy nuevo en el foro pese a que lo conozco desde hace años.

Mi problema es el siguiente:
Tengo un pequeño proyecto de un juego en el cual arme clases para hacer lo que seria el engine donde tengo una clase cEngine que lo que hace (entre otras cosas) es iniciar el SDL, cargar imagenes, las muestra, etc.

El tema es que, cuando inicio el SDL creo una surface de buffer que la voy a usar para despues pegarla en la pantalla así:
[Todo lo que quiera mostrar] --> [surface de buffer] --> [surface principal]
La superficie la creo de esta manera:

sBuffDisplay = SDL_CreateRGBSurface(SDL_HWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0x00000000, 0x00000000, 0x00000000, 0x000000ff);

Por otra parte, tengo un método que lo que hace es mostrar la imagen que le paso como surface, el método es este:

void cEngine::apply_surface( int x, int y, SDL_Surface *source){
  SDL_Rect offset;

  offset.x = x;
  offset.y = y;
  offset.h = source->h;
  offset.w = source->w;

  SDL_BlitSurface( source, NULL, sBuffDisplay, &offset );
}


Este es el mas sencillo, ya que tengo otros que aceptan otros parametros:

void apply_surface( int x, int y, SDL_Surface *source);
void apply_surface( int x, int y, SDL_Surface *source, int px, int py, int ph, int pw);
void apply_surface( int x, int y, SDL_Surface *source, SDL_Rect srcRect);
void apply_surface( SDL_Surface *source, SDL_Rect srcRect, SDL_Rect dstRect);


Ahora bien, desde la clase principal cGame (del archivo game.cpp y game.h) creo el objeto del engine (el objeto cGame lo creo en el main de la aplicación):

cEngine engine;

y mas adelante lo uso para mostrar una imagen:

engine.apply_surface( 0, 0, sBackground);

Esto funciona perfectamente mostrando la imagen como corresponde.
Ahora si yo hago esto mismo que hice en la clase cGame desde otra clase (cMouse, cMenuItem, etc) no muestra la imagen pero tampoco tira error.
Estoy un poco mariado en cuanto a que porque se puede deber ya que el método que muestra la imagen (apply_surface) es uno solo, y el render, flip y el clear también se hace por medio de la clase cEngine.

No tengo problemas con el parpadeo, ni con ninguna otra cosa, solo esto que me surgió a raíz de que empecé a acomodar el engine ahora que tengo el juego en una versión bastante estable con menú y todo eso; me faltan algunas cosas para hacer pero el juego en si esta bastante terminado.

Por otro lado, me queda la duda si eso de crear una surface de buffer es correcto para usar el doblebuffer o el SDL lo hace automaticamente?

También me gustaría un poco de info (ya que busque y no encontré lo que necesitaba) acerca de OpenGL para hacer los blits ya que, según leí es mejor, mas rápido y no es difícil. Si alguien tiene y puede pasarme una mini-guia para hacer algo básico como crear un cuadrado, ponerle la textura (imagen) y mostrarlo estaría re agradecido.

Espero que alguien me entienda y me pueda dar una respuesta.

Salu2
JuanPablo

Martinez

He entendido que haces lo de la superfice para evitar el parpadeo, para eso usa el SDL_DOUBLEBUF. y cuando termines de dibujar usa la funcion SDL_Flip.

Posiblemente el problema este en como llames a cEngine en el resto de los objetos. Debes utilizar un puntero a la instancia de cEngine que creaste en cGame.

Sobre tutoriales de OpenGL: http://nehe.gamedev.net/http://nehe.gamedev.net/

En esos tutoriales no se muestra a usarlo con SDL lo unico que tienes que hacer es poner esto:
// Atributos de OpenGL
   SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
   SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
   SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
   SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
   SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16);

   SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

// Creamos la ventana
if((pWindow=SDL_SetVideoMode(ancho,alto,bpp, SDL_OPENGL))==NULL)
throw std::exception("No se puede crear la ventana");


Y despues del render poner SDL_GL_SwapBuffers() en vez de SDL_Flip. Mira la funcion SDL_GL_SetAttribute  en SDL wiki para mas informacion, ya que hay mas parametos. Por cierto esta configuracion me la he inventado, no se cual es la mas optima

kittie4man

Cita de: "Martinez"He entendido que haces lo de la superfice para evitar el parpadeo, para eso usa el SDL_DOUBLEBUF. y cuando termines de dibujar usa la funcion SDL_Flip.

Posiblemente el problema este en como llames a cEngine en el resto de los objetos. Debes utilizar un puntero a la instancia de cEngine que creaste en cGame.

Sobre tutoriales de OpenGL: http://nehe.gamedev.net/http://nehe.gamedev.net/

En esos tutoriales no se muestra a usarlo con SDL lo unico que tienes que hacer es poner esto:
// Atributos de OpenGL
   SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
   SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
   SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
   SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
   SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16);

   SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

// Creamos la ventana
if((pWindow=SDL_SetVideoMode(ancho,alto,bpp, SDL_OPENGL))==NULL)
throw std::exception("No se puede crear la ventana");


Y despues del render poner SDL_GL_SwapBuffers() en vez de SDL_Flip. Mira la funcion SDL_GL_SetAttribute  en SDL wiki para mas informacion, ya que hay mas parametos. Por cierto esta configuracion me la he inventado, no se cual es la mas optima

Ante todo, muchas gracias por la respuesta.

Con respecto al doblebuffer lo que queria saber es si hace falta crear una surface de "buffer", como hago yo, o el SDL lo hace automaticamente cuando hago flip. Osea, en el caso de que lo hiciera automaticamente yo pintaria TODAS las imagenes en la surface principal y despues hago flip y listo.

Lo de los punteros no entiendo como haria ya que no tengo referencia a la clase cGame desde las otras clases y en caso de incluir su .h daria errores de linkeo al redefinir ciertas cosas.. no?.
Donde me tira error es en el SDL_BlitSurface del metodo de la clase cEngine que pinta en la surface de buffer.
Lo tengo armado asi

game.cpp: incluye game.h
game.h: incluye menuItem.h y engine.h (entre otros)

menuItem.cpp: incluye menuItem.h
menuItem.h: incluye engine.h

Espero que se entienda.
Tal vez mi problema pasa por un tema de OOP, un error al crear las clases, clases mal pensadas, relaciones mal pensadas, etc.

Con respecto a lo de OpenGL, gracias, ya conocia eso y me sirvio.

Salu2
Juan Pablo

Martinez

Al poner el flag doble buffer es como tener 2 superficies en una. Al dibujar en la superfice dibujas en la parte que no se muestra,y al hacer el flip se intercambian. No tienes que crear superfices intermedias. Al hacer flip, no solo intercambia los bufferes ademas actualiza toda la pantalla.

Citarpintaria TODAS las imagenes en la surface principal y despues hago flip y listo.

Exacto.


Sobre la poo:




cEngine::cEngine()
{
     sBuffDisplay =SDL_CreateRGBSurface(....);
}


Si creas el buffer en el constructor del engine cada vez que hagas cEngine engine se creara una superfice diferente. Como arreglarlo:

en cGame cuando crees el resto de elemetos hazlo asi:

cMenuItem menu_item(&engine);

y el constructor de clase deberia ser:

cMenuitem(cEngine *pEngine);

Como es logico deberas guardar en puntero en una variable de la clase. Hay mas formas, como que todos los metodos y atributos de cEngine sean estaticos.

kittie4man

Gracias Martinez, voy a sacar la creación y utilización de la surface esa que tengo como buffer.

Lo que hice fue crear una método GetInstance asi cada objeto que requiera el objeto cEngine primero comprueba si tiene una instancia creada, sino la tiene crea uno nuevo.

engine.h:

class cEngine{
  private:
     static cEngine *instance;
     ...

  public:
     static cEngine* GetInstance ();
     ...
};


engine.cpp

cEngine* cEngine::instance = NULL;

cEngine* cEngine::GetInstance () {
        if ( instance == NULL ) {
              instance = new cEngine();
        }
        return instance;
}


Cuando creo el objeto cEngine lo hago asi:
menuItem.h

#include "engine.h"
class cMenuItem{
  private:
     ...
     cEngine* engine;
  public:
     cMenuItem(int px, int py, std::string pTxt);
     ...
};


menuItem.cpp

cMenuItem::cMenuItem(int px, int py, std::string pTxt){
  ...
  engine = cEngine::GetInstance();
}


Esto funciono de maravilla. No se si es lo mejor pero me pareció una solución mas limpia. Ahora solo resta hacer delete en el destructor de cada clase y listo.

Salu2 y muchas gracias por tu ayuda
Juan Pablo

Martinez

Es una forma valida, Ogre y CEGUI la usan, al metodo GetInstance le llaman getSingleton. Una cosa NO HAGAS delete engine en todos los destructores de las clases. Te pongo un ejemplo:

1 - cGame crea cMenuItem

2- cMenuItem llama a cEngine::GetInstance por lo que se crea la instancia.

3 - cGame borra cMenuItem. cMenuItem borra la instancia de engine con delete engine en su destructor. Pero cEngine no lo sabe y tiene un puntero apuntando a una zona de memoria que ha sido liberada!!!

4- cGame crea otro objeto llamado cBoton.

5- cBoton hace lo siguiente cEngine::GetInstance()->apply_surface

6- CRASH!!!!!!!!!!!!!!!

Para borrar la instancia de cEngine crea otro metodo estatico Delete y lo llamas al salir del programa. Recuerda que los punteros apuntan a zona de memoria, y aunque esta se libere estos siguen apuntando a esa zona de memoria.

kittie4man

Es cierto, horrible lo mio, algo tan basico en el manejo de punteros y me lo como =S

Gracias nuevamente.






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.