"Ningún juego dura tanto hasta que te lo terminas como el que te programas tú mismo,
ninguno te absorbe tanto y ninguno te produce tanta satisfacción cuando lo has acabado"
Programacion de videojuegos
Inicio Curso de programación de juegos 13. Pantalla completa y cambio de resolucion
Domingo 22 de Octubre del 2017

Menu principal
Colaborar (con PayPal)

Para continuar con el trabajo de esta Web y poder pagar el hosting, viene bien la ayuda que sea. Gracias a todos.

Importe: 

Ultimas descargas
19.Jan

Clase que permite dibujar texto en OpenGL con mucha facilidad.Usa FreeType2.Para ver que hace y c...


13. Pantalla completa y cambio de resolucion Imprimir Correo electrónico
Videojuegos - Curso de Programación de juegos
Escrito por Vicengetorix   
En este capitulo vamos a tocar un tema al que todos los programadores de OpenGL en Windows llegan, tarde o temprano. Cambiar el programa a pantalla completa o al reves, y cambiar la resolucion son temas puramente de programacion Windows. Al terminar este capitulo ya no tendremos dudas al respecto.

La programacion Windows es, cuando menos, especial. En todo caso, para nuestros propositos no necesitamos mas que saber hacer ciertas cosas, y una vez hechas dedicarnos a lo nuestro, los juegos.
Una primera aproximacion antes de meternos en el codigo, de lo que haremos y las funciones que usaremos.
El metodo para que nuestro programa se ejecute a pantalla completa es basicamente sencillo.
Primero necesitaremos saber el tamaño de la pantalla, la resolucion, para luego ajustar la posicion y el tamaño de la ventana para que cubra toda la ventana. Antes tendremos que haber cambiado los parametros de nuestra ventana para que no tenga borde alguno.
El proceso contrario es mas facil. Solo tendremos que poner borde a la ventana y restablecer el tamaño y posicion anterior.
Tambien veremos como preguntar a la tarjeta grafica por las resoluciones que admite (puede que el monitor no las admita). En nuestro programa las listaremos (advierto que suelen ser muuuuchas, en mi tarjeta 800 y pico).
El cambio de resolucion lo haremos al poner modo de pantalla completo, ya que en modo ventana no tiene sentido cambiar resolucion.

Las funciones windows que usaremos principalmente, para el tema de la resolucion, son EnumDisplaySettings(...) para recuperar datos de la tarjeta grafica y ChangeDisplaySettings(...) para cambiar la resolucion de pantalla.
Lo datos de resolucion (los que recuperamos en una y los que ponemos en otra) se guardaran en una variable del tipo DEVMODE, que no es mas que una estructura con todos los valores necesarios para definir un modo grafico o resolucion. Los miembros de DEVMODE que nos pueden interesar son:

  • dmPelsWidth: El ancho de la pantalla en pixels.
  • dmPelsHeight: El alto de la pantalla en pixels.
  • dmBitsPerPel: Bits por pixel (numero de colores por pixel).
  • dmDisplayFrequency: Frecuencia de barrido.
  • dmFields: Usado al cambiar resolucion. Indica que campos cambiamos.
Veremos tambien alguna funcion mas de Windows para el tema de las ventanas.

Empezamos sin mas preambulos a ver lo que cambiamos del codigo.
Muy importante: empezamos a partir del codigo del capitulo 10. Teclado, ratón y cámara.

Comenzamos como siempre con las definiciones globales.

Primero definimos mas indicadores para controlar que una tecla pulsada haga solo una vez su tarea por pulsacion (como haciamos con la tecla F para ver o no los FPS).

1
2
3
4
5
6
7
8
9
//Indicadores de si hemos levantado las teclas.
// Tecla F
bool LevantaTeclaF=true;
// Tecla U
bool LevantaTeclaU=true;
// Tecla P
bool LevantaTeclaP=true;
// Tecla 8
bool LevantaTecla8=true;

Despues tres indicadores mas.
Para saber si estamos a pantalla completa o no, para saber si estamos presentando la lista de resoluciones de la tarjeta en pantalla o no, y para saber si hemos terminado de escribir la lista completa de resoluciones de la tarjeta grafica o no.

1
2
3
4
5
6
7
8
9
10
// Indicador para saber si estamos 
// a pantalla completa o no.
bool PantallaCompleta = false;
// Indicador de si presentamos
// lista de resoluciones en pantalla
// o no.
bool imprime_res=false;
// Indicador de si hemos terminado
// la lista de resoluciones o no.
bool fin_res=false;

Luego dos indices. Uno sera el que usemos de indice para listar las resoluciones posibles de la tarjeta y otro auxiliar para el mismo tema.

1
2
3
4
5
// Indice de las resoluciones de
// la tarjeta grafica.
int indice_res=0;
// Indice auxiliar de resoluciones.
int indice_res2=0;

Por ultimo un array para almacenar una cadena de texto que pondremos en pantalla cuando estemos a pantalla completa. El mensaje que contendra sera distinto si mantenemos la resolucion, la cambiamos o falla la funcion que cambia la resolucion.

1
2
3
4
// Texto a presentar con pantalla 
// completa. Luego lo cambiaremos
// segun el programa.
char TextoPC[200]="";

Tras las definiciones globales, definimos un funcion para la carga de texturas, la misma funcion que hemos usado en el juego Rompe Ladrillos de los capitulos 11 y 12.
Incluyo el codigo, por si acaso alguien se los ha saltado, de la funcion CargaTextura(...).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//--------------------------------------------------------------
///// Funcion para cargar y crear texturas
void CargaTextura(GLuint *textura, const char *fichero)
{
// Objeto de Corona para cargar imagenes
corona::Image* imagen=NULL;
// Usamos de nuevo el objeto imagen para cargar otra imagen
// de disco.
imagen = corona::OpenImage(fichero, corona::PF_R8G8B8A8);
// Si no retorna un puntero valido, normalmente por que no
// encuentra la imagen en disco o el fichero esta mal, termino
// la funcion. Si ocurre esto, luego no pintara nada.
if (!imagen) return;
 
// Genero una textura y cargo su identificativo en tex2
glGenTextures(1, textura);
// Le indico a OpenGL que voy a trabajar con la textura tex2
glBindTexture(GL_TEXTURE_2D, *textura);
 
// Cargo la textura normal sin generar mipmaps en OpenGL desde
// la imagen de corona.
// Parametros: 1- Es de 2D; 2- Es para mipmaps manuales y no lo
// usamos; 3- Tipo de imagen (de nuevo RBGA); 4- Ancho de la imagen;
// 5- Alto de la imagen; 6- Borde. Nosotros 0; 7- El formato de nuevo
// 8- Tipo en que almacena la textura; 9- Puntero a los pixels de
// la imagen.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
imagen->getWidth(), imagen->getHeight(),
0, GL_RGBA, GL_UNSIGNED_BYTE, imagen->getPixels());
 
// Indico como va a visualizarse los pixels en caso de encajar
// una imagen grande en un cuadrado pequeño.
// GL_LINEAR interpola y se ve bien.
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
// Indico como va a visualizarse los pixels en caso de encajar
// una imagen pequeña en un cuadrado grande.
// GL_LINEAR interpola y se ve bien.
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
 
// Das dos funciones siguientes son por si quiero repetir la
// imagen dentro del cuadrado. Tendria que cambiar las
// coordenadas de textura. En este ejemplo no seran necesarias.
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_REPEAT);
 
// El modo en que se aplica la textura sobre el color del poligono.
// DECAL y REPLACE simplemente de pone el dibujo. MODULATE y BLEND
// se podrian usar con iluminacion una y para transparencia otra.
glTexEnvf(GL_TEXTURE_2D,GL_TEXTURE_ENV_MODE,GL_DECAL);
 
// Como ya he cargado la imagen en la textura con glTexImage2D
// en la memoria grafica, puedo liberar la memoria.
if(imagen!=NULL) delete imagen;
} // Fin CargaTextura
 

Luego, como es logico, usaremos esta funcion para cargar texturas en la funcion IniciaGL().

1
2
3
4
// Cargo texturas con la funcion de 
// una forma mas comoda.
CargaTextura(&tex1,"dibus//mad.png");
CargaTextura(&tex2,"dibus//usw.png");

El logo de UnSitioWeb es el de siempre, la otra textura, la que usaremos para dos de los cubos es nueva en el ejemplo, una textura de tablones de madera. Poned vosotros la que querais, la de anteriores capitulos u otra vuestra (recordad que tiene que ser de tamaño 8, 16, 32, 64, 128, 256, 512, ...). Ademas las texturas estan en un subdirectorio llamado "dibus".

La funcion ProcesaMensajes(...) tiene cosas incluidas al recibir el mensaje WM_DESTROY, al terminar el programa.
Esto es un olvido. Un olvido mio ya que nunca antes hemos cerrado OpenGL correctamente y tendriamos que haberlo hecho en el capitulo 3. Siempre es buen momento para hacerlo. Hay va el codigo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Destruccion de el epacio de 
// dibujo de OpenGL.
// Si hay un Contexto de Render de
// OpenGL es que hay que destruirlo.
if(OGLrc)
{
// Hago que el OGLrc no sea ya
// el que esta en uso.
wglMakeCurrent(NULL,NULL);
// Lo borro
wglDeleteContext(OGLrc);
// Lo hago NULL (por si acaso)
OGLrc=NULL;
}
// Libero el Dispositivo de Contexto
// de la Ventana.
ReleaseDC(IdVentana,DevContex);

Inmediatamente despues de este codigo hay una linea que es nueva y sobre el tema de las resoluciones.
Antes de cerrar el programa definitivamente debemos asegurarnos de que dejamos la resolucion de pantalla como estaba, y para eso es esta funcion con estos parametros. Deja la resolucion tal y como esta grabada en el registro de Windows (nosotros habremos cambiado la resolucion pero no el registro).

1
2
3
4
// Dejo la resolucion como esta
// en el registro de Windows, como
// estaba antes.
ChangeDisplaySettings(NULL,0);

Tras lo anterior llegamos a la parte importante del tema del capitulo.
Crearemos dos funciones.
La primera se llamara ImprimeResoluciones(). Sera la encargada de escribir en pantalla la resoucion actual en pantalla y la lista de resoluciones posible de nuestra tarjeta grafica.
El funcionamiento de esta funcion, junto con las variables globales pertinentes, sera imprimir la lista de resoluciones llamando a la funcion EnumDisplaySettings con un indice como segundo parametro, empezando de el indice 0. Iremos aumentando este indice hasta que la funcion retorne un valor false, lo que querra decir que ya no tiene mas resoluciones posibles, que ha terminado la lista.
La funcion se define asi.


1
2
3
4
5
6
// Funcion que consulta e imprime la lista de 
// resoluciones en pantalla a partir del valor
// del indice de resoluciones (indice_res) hasta
// donde quepa en pantalla.
void ImprimeResoluciones()
{

Primero definimos una variable del tipo DEVMODE y luego una variable para la posicion en el eje Y de las resoluciones que vayamos escribiendo.

1
2
3
4
5
6
7
8
9
// Variable del tipo DEVMODE. Es una
// estructura con los datos necesarios
// de un modo grafico.
DEVMODE dv;
// Defino una variable para ir escribiendo
// las resoluciones que encontremos en pantalla
// de abajo a arriba. La inicializo con la
// altura de la ventana menos 10 pixels.
int pos_y = rect.bottom-10;

Luego consulto la resolucion actual y escribo en pantalla los datos de esta resolucion que nos interesan.

1
2
3
4
5
6
7
8
9
10
11
// Funcion que pregunta a la tarjeta grafica sus datos.
// Con el parametro ENUM_CURRENT_SETTINGS devuelve los
// datos del modo grafico activo (la resolucion que
// tenemos puesta ahora). Deja los datos en una estructura
// del tipo DEVMODE (&dv).
EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&dv);
 
// Pongo en pantalla (arriba) la resolucion actual.
Escribe(10, 15,"Resolucion actual: %i x %i, %i bpp, %i hercios",
dv.dmPelsWidth, dv.dmPelsHeight,
dv.dmBitsPerPel, dv.dmDisplayFrequency);

Como habeis visto, la resolucion actual nos la dara la funcion EnumDisplaySettings pero con ENUM_CURRENT_SETTINGS como parametro, en vez de un indice para listar las resoluciones posibles. Los datos los retorna en la estructura del tipo DEVMODE como dijimos al principio del capitulo. El primer parametro de esta funcion es el dispositivo de display, pero como solo tenemos uno (solo una pantalla) no hay duda. NULL es para que coja el que tiene por defecto.

Luego preparo la variable "indice_res2", que es auxiliar con el valor de "indice_res", que contiene el primer indice a partir del cual imprimir resoluciones.

1
2
3
4
//// Pongo el valor del indice de resoluciones (indice_res)
//// en el indice auxiliar (indice_res2) para no modificar
//// el valor original.
indice_res2=indice_res;

Despues, en un bucle while vamos escribiendo en pantalla los datos de las resoluciones empezando por abajo (variable "pos_y") hasta que lleguemos a la parte de arriba de la ventana.
Dentro del bucle iremos preguntando a la tarjeta y si nos responde escribiremos los datos que nos devuelve. Si no responde es que hemos terminado con la lista, en cuyo caso tambien terminaremos el bucle.
El codigo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Se ejecutara mientras la coordenada Y en la que voy 
// a escribir sea mayor de 65 pixels (dejamos espacio
// arriba para escribir la resolucion actual).
while( pos_y > 65 )
{
// La funcion "EnumDisplaySettings", si
// el segundo parametro es un numero, empezando
// por 0; lo toma como un indice de las resoluciones
// que es capaz de soportar. Si la llamas con 0 como
// segundo parametro retornara la resolucion numero 0
// que es capaz de soportar, con un 1 la numero 1, y
// asi sucesivamente. Nosotros la llamaremos con
// indice_res2 como parametro muchas veces sucesivas
// e incrementando indice_res2, y asi obtendremos en
// dv, en cada pasada los datos de resolucion para
// todos los indices y formaramos la lista a imprimir.
// Si retorna false es que ha fallado, normalmente
// porque la lista ha terminado.
if(EnumDisplaySettings(NULL,indice_res2,&dv))
{
// Pongo en pantalla los datos de la resolucion
// del indice en curso.
Escribe(10, pos_y,"%i: %i x %i, %i bpp, %i hercios",
indice_res2,
dv.dmPelsWidth, dv.dmPelsHeight,
dv.dmBitsPerPel, dv.dmDisplayFrequency);
// Muevo la posicion Y donde escribire la
// proxima pasada.
pos_y-=15;
// Incremento el indice de resoluciones para
// que apunte a la proxima resolucion de la tarjeta.
indice_res2++;
}
// Si falla EnumDisplaySettings es porque
// ha terminado la lista.
else
{
// Asi que pongo el indicador de fin de
// lista a true...
fin_res=true;
// ... y salgo del bucle.
break;
}
}

Tras esto solo queda terminar esta funcion.

1
} // Fin ImprimeResoluciones

(Si no ponemos el corchete no compila).

La segunda funcion que creamos y, como la anterior usaremos luego deste la funcion Pinta() se llamara CambiaPantalla(...) y acepta un parametro que sera true o false (false por defecto) segun el cual, ademas de cambiar a pantalla completa cambiara la resolucion. En nuestro programa lo haremos con una resolucion de 800x600 y 16 bits de color por pixel. Esta es una resolucion muy normal que aceptan las tarjetas graficas. Si es tu resolucion normal de trabajo cambialo por otra que tu tarjeta y monitor acepte.
Cuando se ejecute esta funcion lo que hara sera cambiar a pantalla completa si estamos en modo ventana y a modo ventana si estamos a pantalla completa.
La definicion es asi.

1
2
3
4
5
6
7
// Funcion que cambia de programa en ventana a pantalla completa 
// y al reves. Con el parametro res a true, cuando cambia a
// pantalla completa lo hace, ademas, ambiando la resolucion
// a 800 x 600 x 16. Sin parametro, res coje el valor por defecto,
// que es false.
void CambiaPantalla(bool res=false)
{

Primero, como en la funcion anterior, necesitaremos una estructura de tipo DEVMODE para los datos de resolucion.

1
2
3
4
// Variable del tipo DEVMODE. Es una
// estructura con los datos necesarios
// de un modo grafico.
DEVMODE dv;

Vemos el caso de tener que pasar a pantalla complata (si no lo estabamos entonces es lo que queremos hacer).

1
2
3
4
5
// Si este indicador es false, es que antes no 
// estaba a pantalla completa y por tanto hay
// que poner pantalla completa.
if(!PantallaCompleta)
{

Para poner pantalla completa nos enteramos de que tamaño es la pantalla, quitamos es borde a la ventana y la situamos en la coordenada 0,0 con el mismo ancho y alto que la pantalla (logico).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Uso esta funcion para recuperar los datos 
// de la resolucion actual de pantalla en dv.
EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&dv);
// Modifico el estilo de la ventana para que no tenga
// borde (el parametro style de la funcion "CreateWindowEx"
SetWindowLong(IdVentana,GWL_STYLE,WS_POPUP);
// Cambio la posicion y el tamaño de la ventana para
// que se ajuste a la pantalla. La esquina superior
// izquierda en las coordenadas "0,0". La esquina inferior
// derecha en "dv.dmPelsWidth,dv.dmPelsHeight" (tamaño
// X e Y de la resolucion actual de pantalla, en la
// estructura dv de tipo DEVMODE).
// HWND_TOP inica que ponga la ventana encima de las demas.
// SWP_FRAMECHANGED indica que el borde ha cambiado.
SetWindowPos( IdVentana,HWND_TOP,
0, 0,
dv.dmPelsWidth, dv.dmPelsHeight,
SWP_FRAMECHANGED );
// Ponemos el mensaje de no haber cambiado
// la resolucion en TextoPC (luego se escribira
// en pantalla.
sprintf(TextoPC,"Resolución sin cambiar");

Con esto tengo la ventana a pantalla completa pero no he contemplado si hay o no que cambiar de resolucion, para lo cual incluyo una clausula if (dentro del if en el que estamos) que compruebe el parametro de la funcion.
Si tiene que cambiar la resolucion pondremos los datos que queremos en los campos de la estructura dv (tipo DEVMODE) y en el campo dmFields indicaremos los datos que vamos a modificar. Tras esto cambiaremos resolucion con la funcion ChangeDisplaySettings pasandole la direccion de dv (tipo DEVMODE) como primer parametro y CDS_FULLSCREEN como segundo.
Si todo va bien, cambio de nuevo el tamaño de la ventana para acomodarlo al nuevo tamaño de pantalla.
Veamos el codigo de esto.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// En el caso de que res (el parametro
// de esta funcion) se true es que
// queremos, ademas, cambiar la resolucion
// de pantalla.
if(res)
{
// Pongo en los parametros correspondientes
// de la estructura dv (tipo DEVMODE) el
// ancho y alto que queremos.
dv.dmPelsWidth=800;
dv.dmPelsHeight=600;
// Pongo los bit por pixel
// Con menos de 16, OpenGL no va.
dv.dmBitsPerPel=16;
// Podria cambiar la frecuencia tambien
// pero ¿para que?.
// Indico en la estructura los parametros que
// quiero modificar...
dv.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
// ... y cambio la resolucion y a pantalla completa.
if(DISP_CHANGE_SUCCESSFUL==ChangeDisplaySettings(&dv,CDS_FULLSCREEN))
{
// Si el cambio va bien...
// Pongo el mensaje que escribire en pantalla.
sprintf(TextoPC,"A 800x600x16");
// Indico el tamaño que debe tener la ventana
// para adecuarse a la pantalla con la nueva
// resolucion.
SetWindowPos(IdVentana,HWND_TOP, 0,0,800,600,SWP_FRAMECHANGED);
}
// Si el cambio fallase, el programa
// se habria quedado a pantalla completa
// pero sin cambiar resolucion.
// Pongo el mensaje para saber que ha fallado.
else
sprintf(TextoPC,"Falla cambio");
}

En este momento tendriamos a pantalla completa el programa (con o sin cambio de resolucion).
Deberiamos ahora cerrar el corchete del if y poner un else, el caso de que estuvieramos antes a pantalla completa y tuvieramos ahora que volver al programa en una ventana.

1
2
3
4
5
}
//// En el caso de que haya que pasar de
//// pantalla completa a verse en ventana.
else
{

Pondremos de nuevo a la ventana el mismo borde que tenia cuando la creamos, volveriamos a colocarla en la misma posicion del principio (se podria haber guardado la posicion de cuando se paso a pantalla completa para restituirla ahora al mismo sitio, a mi no me apetecia) y con el mismo tamaño del principio, se retorna a la resoucion antigua por si acaso estaba cambiada de la misma forma que hemos visto al cerrar el programa, y por fin redibujariamos todas las ventanas del Windows ya que si no lo hicieramos se quedarian en negro pendientes de ser redibujadas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Cambio en estilo de la ventana al
// original de cuando creamos la ventana.
SetWindowLong( IdVentana,GWL_STYLE,
WS_POPUPWINDOW|WS_CAPTION|WS_MINIMIZEBOX);
// Pongo la ventana donde al principio y
// con el tamaño original.
SetWindowPos( IdVentana,HWND_TOP,
100, 100,
AnchoVentana, AltoVentana,
SWP_FRAMECHANGED);
// Dejo la resolucion original, por si estaba cambiada.
ChangeDisplaySettings(NULL,0);
// Redibujo toda la pantalla, desde el tapiz
// de windows hasta la ultima ventana.
// Con esto evito que el resto (las ventanas que
// no son de nuestro programa) se queden en negro.
RedrawWindow(NULL,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE );
// Quito el mensaje de pantalla completa.
// En realidad no haria falta pero por si acaso.
sprintf(TextoPC,"");

Cerramos el else y cambiamos el indicador "PantallaCompleta" para dejarlo indicando el estado actual del porgrama (pantalla completa si o no)

1
2
3
4
}
//// Cambio el indicador de pantalla completa al
//// contrario de como estaba.
PantallaCompleta=!PantallaCompleta;

Por ultimo, en esta funcion, tendremos que adecuar OpenGL al nuevo tamaño de visualizacion.
Recordad que en IniciaGL() definiamos el viewport y la perspectiva segun el tamaño de la ventana, por tanto ahora tendremos que hacer lo mismo. Este codigo sirve pues para los dos casos, reducir el tamaño de ventana o ampliarlo. Si no cambiaramos el viewport veriamos partes de la ventana en negro solo, o solo parte de nuestra escena. Si no cambiasemos la proyeccion se veria la escena achatada o lo contrario.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Tras esto debemos recalcular el area cliente de 
// la ventana como al empezar y ajustar el viewport
// de OpenGL y poner la prespectiva otra vez en base
// a las nuevas dimensiones.
 
// Extraemos el area cliente de nuestra ventana en rect.
GetClientRect(IdVentana, &rect);
// Le digo a OpenGL que voy a cambiar la matriz de proyeccion
glMatrixMode(GL_PROJECTION);
// Usamos rect (el area cliente o espacio util dentro de los
// bordes de nuestra ventana) para definir los limites del
// espacio que va a usar OpenGL para dibujar (viewport). Asi
// ajustamos nuestra ventana y OpenGL.
glViewport(rect.left,rect.top,rect.right,rect.bottom);
// Le digo a OpenGL que use proyeccion perspectiva. Uso el ancho
// y alto de mi viewport para calcular el segundo parametro
gluPerspective(50.0f,(float)rect.right/(float)rect.bottom,0.5f,50.0f);
// Dejo la matriz de modelo de nuevo.
glMatrixMode(GL_MODELVIEW);
 
// Nos aseguramos de ver nuestra ventana.
ShowWindow(IdVentana, SW_SHOW);
// La actualizo para que muestre lo que tenga que mostrar.
UpdateWindow(IdVentana);

Hemos terminado esta funcion.

1
} // Fin CambiaPanalla.

Solo queda encajar estas funciones en la funcion Pinta() y rematar el asunto.

Primero, organizamos el tema para que presionando la tecla "U" cambie a pantalla completa o vuelva a ventana. Lo hacemos como siempre para que solo lo haga una vez por pulsacion.
Recordad que el parametro por defecto de la funcion CambiaPantalla es false.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// De la misma manera que antes, controlo que
// solo se ejecuta una vez lo que hace la tecla U.
if(LevantaTeclaU && Teclas['U'])
{
// Llamo a la funcion de cambiar pantalla.
// Si pone pantalla completa, lo hara sin
// cambiar resolucion (sin parametros la funcion).
// Si esta a pantalla completa, volvera a
// verse en una ventana mas pequeña.
CambiaPantalla();
 
// El resto del codigo para control de la
// tecla U
LevantaTeclaU=false;
}
else if( !Teclas['U']) LevantaTeclaU=true;

Luego hacemos lo mismo con la tecla "8" para pantalla completa con cambio de resolucion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// De la misma manera que antes, controlo que
// solo se ejecuta una vez lo que hace la tecla 8.
if(LevantaTecla8 && Teclas['8'])
{
// Llamo a la funcion de cambiar pantalla.
// Si pone pantalla completa, lo hara cambiando
// la resolucion (con parametro true).
// Si esta a pantalla completa, volvera a
// verse en una ventana mas pequeña.
CambiaPantalla(true);
 
// El resto del codigo para control de la
// tecla 8
LevantaTecla8=false;
}
else if( !Teclas['8']) LevantaTecla8=true;

Si estamos en pantalla completa escribimos en pantalla la resolucion, si no la hemos cambiado o si ha fallado el cambio ("TextoPC" vaya).

1
2
3
// Si esta a pantalla completa escribe TextoPC en 
// su lugar.
if(PantallaCompleta) Escribe(rect.right-200,40,TextoPC);

Luego nos ocupamos de la lista de resoluciones en la tecla "P".
Al presionar esta tecla cambiaremos un indicador en base al cual escribiremos en pantalla la lista de resoluciones o no ("imprime_res"). Si el indicador se pone a true en esta pulsacion es que se va a pintar la lista, a si que actualiza el indice para que imprima la lista a partir del indice (resolucion posible de la tarjeta) ultimo que imprimio la vez anterior que la lista estuvo en pantalla. De esta forma cada vez que presionemos "P", ademas de aparecer la lista, apareceran los siguientes modos graficos soportados. Si se llega al final de la lista se reinician indicadores e indices para volver a empezar la proxima vez.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// De la misma manera que antes, controlo que
// solo se ejecuta una vez lo que hace la tecla U.
if(LevantaTeclaP && Teclas['P'])
{
// Cambio el indicador para que imprima
// o no imprima en pantalla la lista de
// resoluciones.
imprime_res=!imprime_res;
// Si tiene que imprimir, actualizo
// el indice de resoluciones igualandolo
// con el auxiliar. Asi lo dejo en el lugar
// donde termino de imprimir anteriormente
// y por lo tanto imprimira los siguientes
// que le quepan en pantalla.
if(imprime_res) indice_res=indice_res2;
// Si hemos llegado al final de la lista
// de resoluciones.
if(fin_res)
{
// Preparo el indicador para empezar
// la lista la proxima vez que se presione
// la tecla P.
fin_res=false;
// Inicializo el indice de resoluciones y
// el indice auxiliar para empezar la
// lista.
indice_res=0;
indice_res2=0;
}
 
// El resto del codigo para control de la
// tecla p
LevantaTeclaP=false;
}
else if( !Teclas['P']) LevantaTeclaP=true;

Despues, si el indicador lo dice, imprimimos la lista.

1
2
3
4
// Si toca imprimir la lista de resoluciones
// llamo a la funcion que lo hara, en base al
// indice de resoluciones.
if(imprime_res) ImprimeResoluciones();

Ya solo nos queda cambiar la leyenda que escribimos con las teclas a que atiende el programa.
Aprovechamos para posicionarla de forma relativa al borde de la ventana y asi mantener el aspecto de la lista con cualquier tamaño.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Escribo la leyenda de lo que se puede hacer en nuestro 
// programa. La situo segun el tamaño de la ventana, sea
// cual sea.
Escribe(rect.right-300,rect.bottom-180,
"8: Cambiar a Pantalla completa y de resolución");
Escribe(rect.right-300,rect.bottom-160,
"U: Cambiar entre Pantalla completa y normal");
Escribe(rect.right-300,rect.bottom-140,
"P: Ver resoluciones de la tarjeta");
Escribe(rect.right-300,rect.bottom-120,
"Click izquierdo presionado para mover");
Escribe(rect.right-300,rect.bottom-100,
"con el raton (rueda para distancia)");
Escribe(rect.right-300,rect.bottom-80,
"F - Ver FPS");
Escribe(rect.right-300,rect.bottom-60,
"A Z - Modificar FPS");
Escribe(rect.right-300,rect.bottom-40,
"Q W - Rotar cubos");
Escribe(rect.right-300,rect.bottom-20,
"Arriba Abajo - Acercar y alejar");

Ya hemos terminado de modificar el programa para incluir la posibilidad de pantalla completa y cambio de resolucion.
Compilamos y, si hay suerte, algo asi es lo que veremos despues de presionar la tecla "8".



Notad que cuando la lista de resoluciones esta en pantalla, el programa se ralentiza. Esto es por que estamos preguntando a la tarjeta grafica muchas veces por frame y eso lleva su tiempo. Esta operacion normalmente se hace solo al principio del programa o cuando se cambia la visualizacion pero no en el bucle principal cada frame como nosotros estamos haciendo.   

Codigo completo: usw13.cpp




¡Sólo los usuarios registrados pueden escribir comentarios!
+/- Comentarios
Buscar
gabriel lopez  - ayuda   |200.7.224.xxx |18-03-2010 06:42:42
no puedo correr el juego me sale este
error


Compiling...
Cpp1.cpp
c:\documents and
settings\gabriel\escritorio\juego\ cpp1.cpp(49) : fatal error
C1083: Cannot open include file: 'corona/include/corona.h': No such file or
directory
Error executing cl.exe.

Cpp1.exe - 1 error(s), 0
warning(s)
Compiling...
Cpp1.cpp
c:\documents and
settings\gabriel\escritorio\juego\ cpp1.cpp(49) : fatal error
C1083: Cannot open include file: 'corona/include/corona.h': No such file or
directory
Error executing cl.exe.

Cpp1.exe - 1 error(s), 0 warning(s)
Vicengetorix   |194.179.126.xxx |18-03-2010 20:57:38
Este error es simplemente que no encuentra el fichero "corona.h" en
disco.
Solo hace falta que hagas coincidir el path del fichero en la clausula
"#include" correspondiente del programa con su situacion real en
disco.
Si el path es relativo debe serlo respecto al directorio donde tengas el
proyecto.

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

 


Banner
Spanish Chinese (Simplified) English French German Japanese Portuguese Russian