Foros - Stratos

Programadores => Programación gráfica => Mensaje iniciado por: kittie4man en 22 de Enero de 2008, 01:19:04 PM

Título: [SDL | C++] Problema con clases y SDL_BlitSurface
Publicado por: kittie4man en 22 de Enero de 2008, 01:19:04 PM
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
Título: [SDL | C++] Problema con clases y SDL_BlitSurface
Publicado por: Martinez en 22 de Enero de 2008, 03:11:26 PM
He entendido que haces lo de la superfice para evitar el parpadeo, para eso usa el SDL_DOUBLEBUF.  (http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fSetVideoMode) y cuando termines de dibujar usa la funcion SDL_Flip (http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fFlip).

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
Título: [SDL | C++] Problema con clases y SDL_BlitSurface
Publicado por: kittie4man en 23 de Enero de 2008, 06:03:16 AM
Cita de: "Martinez"He entendido que haces lo de la superfice para evitar el parpadeo, para eso usa el SDL_DOUBLEBUF.  (http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fSetVideoMode) y cuando termines de dibujar usa la funcion SDL_Flip (http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fFlip).

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
Título: [SDL | C++] Problema con clases y SDL_BlitSurface
Publicado por: Martinez en 23 de Enero de 2008, 11:21:08 AM
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.
Título: [SDL | C++] Problema con clases y SDL_BlitSurface
Publicado por: kittie4man en 23 de Enero de 2008, 02:09:39 PM
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
Título: [SDL | C++] Problema con clases y SDL_BlitSurface
Publicado por: Martinez en 23 de Enero de 2008, 03:11:28 PM
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.
Título: [SDL | C++] Problema con clases y SDL_BlitSurface
Publicado por: kittie4man en 23 de Enero de 2008, 05:36:57 PM
Es cierto, horrible lo mio, algo tan basico en el manejo de punteros y me lo como =S

Gracias nuevamente.