consol08: Presentacion

Transcripción

consol08: Presentacion
Desarrollo de videojuegos
con MotorJ
02/20/08
Alejandro Valenzuela Roca, LIDSOL, UNAM
Desarrollo de videojuegos con MotorJ
Alejandro Valenzuela Roca
Laboratorio de Investigación y Desarrollo de Software Libre, UNAM
¿Qué es?
MotorJ es una colección de bibliotecas
que contienen funciones útiles para
el desarrollo de videojuegos.
Está desarrollado en lenguaje C y C++ y se emplea
ampliamente OpenGL para gráficos y Simple
DirectMedia Layer para las ventanas y el sonido.
Es un proyecto siendo desarrollado en LIDSOL,
UNAM, pensado para facilitar el desarrollo de
videojuegos.
2
Desarrollo de videojuegos con MotorJ
Introducción
¿Cómo puede usarse?
MotorJ es Software Libre bajo la licencia GNU
LGPL v3, por lo que es utilizable para desarrollos
tanto libres como cerrados, pero poniendo énfasis
en los desarrollos libres. MotorJ puede utilizarse
tanto en GNU/Linux como en FreeBSD y Windows y
uno de los objetivos es que sea posible realizar
desarrollos en todos los sistemas soportados sin
tener que modificar el código.
3
Requerimientos
Computadora decente, 800+
MHz, 256 MB RAM
Tarjeta de video decente, Intel
Medio Acelerada (Media
Accelerator), que tenga soporte
para al menos lo básico de
OpenGL
Programación en un lenguaje
estructurado y POO (Cuando
menos lo básico)
Lo básico de vectores y álgebra
vectorial, física de prepa.
Perseverancia
Curiosidad
Recomendaciones
Buena computadora, 1.5+ Ghz
[No Celeron], 512+ MB RAM
Tarjeta de video NVidia con
driver NVidiot (privativo >:( )
o
Tarjeta de video ATI con un
driver que sirva.
C++ con clase(s), OpenGL,
Shaders, optimización, etc.
Geometría analítica en el
espacio, álgebra lineal,
cinemática, dinámica, cálculos,
programación, inteligencia
artificial, etcéteras. Las mates
rulan.
Necedad
No dormir (no mucho..)
4
Software adicional
GIMP
Audacity
Vorbis tools
Blender?
Editor de textos favorito
( Anjuta, Emacs, vim,
etc.)
Calculadora científica,
preferiblemente que
maneje vectores, aún
más preferible en físico
5
Motor de videojuegos básico
6
El funcionamiento de un motor de videojuegos básico
Se establecen los valores
iniciales del juego (vidas,
mundo, etc).
Se inicia el ciclo de simulación,
el cual se repetirá hasta que el
usuario quiera salir:
Leer controles
Procesar como afectan al
juego
Realizar una actualización
del mundo interno del juego
– mover objetos, verificar
colisiones, etc.
Representar el mundo
interno del videojuego
mediante instrucciones de
“algún” API gráfico.
Si el usuario desea salir,
7
terminar el programa.
El funcionamiento de MotorJ
Pero antes...
8
El funcionamiento de MotorJ (¿Qué es un “universo”?)
Un universo en MotorJ es un objeto que se deriva de la clase
Universe y que contiene las siguientes funciones:
Initialize, inicializar, donde se establecen las características
iniciales del universo (variables, etc.). Aquí podría haber una
manera de abrir save-states.
ProcessEvent, procesar evento, donde se procesan las
interacciones del usuario con los controles.
Update, actualizar, donde se modifica el estado actual del universo
de acuerdo a lo que se obtuvo en el procedimiento anterior. En
este paso se realizan todos los cálculos físicos, de colisiones y se
actualizan también.
Redraw, redibujar, donde se representa gráficamente el estado
actual del universo.
Cleanup, limpiar, donde se libera la memoria ocupada por la
mayor parte del universo para ser re-inicializado posteriormente.
Aquí podría haber una manera de guardar save-states.
9
Más sobre los “universos”
En otras palabras...
Un universo contiene la manera como el juego responde a los
controles del usuario, controla como funciona la física en su interior,
así como las reacciones de las entidades inteligentes (enemigos,
NPCs, etc.) y también debe tener un inicio y un fin.
Pueden haber tantos universos como se necesiten.
¿Por qué no llamarles “mundos” en vez de “universos”?
La idea es que un mismo universo pueda cargar muchos escenarios
(“mundos”) distintos, pero que todos ellos contengan las mismas
reglas físicas, y que cuando se necesiten modificar las reglas físicas
(o que incluso no existan, como en un menú), se utilice otro universo.
El programa podría cambiar entre varios universos distintos según el
transcurso del juego lo requiera.
De esta manera es más fácil mantener por separado situaciones del
juego muy distintas entre sí, y queda más legible el código, además de
evitar problemas de nombramiento de variables, horrorosas variables
globales, etc.
10
El funcionamiento de MotorJ (Ahora sí..)
11
Configuración del entorno de programación
12
Configurando el entorno
Para desarrollar con MotorJ, es necesario tener
instaladas todas las bibliotecas de las cuales
depende. Estas son:
libsdl
libsdl-image
libsdl-mixer
libsdl-net
libsdl-ttf
Estas bibliotecas se encuentran disponibles en los
manejadores de paquetes de la mayoría de las
distros
13
Configurando el entorno (paquetes en Synaptic)
14
Configurando el entorno (obteniendo el código fuente)
Para crear un proyecto basado en MotorJ, es
necesario bajar el código fuente del repositorio de
LIDSOL mediante SubVersion
SubVersion también se encuentra en los sistemas
de paquetes de la mayoría de las distros; una vez
instalado, se debe crear un directorio donde se
pondrá una copia local del repositorio. Para obtener
el código fuente, hay que abrir una terminal, navegar
hasta el directorio creado en el paso anterior y
ejecutar la siguiente orden:
svn checkout http://svn.lidsol.net/motorj
15
Configurando el entorno (obteniendo el código fuente - svn)
Nota: aparecerán muchos más mensajes en la terminal.
Se acortó la lista para propósitos de la imagen.
16
Creando un nuevo proyecto basado en MotorJ
Una vez obtenido el código fuente, desde la misma
terminal donde se efectuó el svn checkout, se
cambia al directorio motorj/trunk/pc_sdl y se ejecuta
el siguiente script:
./create_new_pc_project.sh
El script nos pedirá un lugar para el nuevo proyecto,
debemos proporcionarle una ruta completa a un
directorio que se encuentre dentro de un sistema de
archivos nativo de GNU/Linux (o FreeBSD). El
directorio será creado si no existía.
17
Creando un nuevo proyecto (create_new_pc_project.sh)
18
Compilando el demo
19
Corriendo el demo
20
Creando un nuevo proyecto (¿Qué hay dentro del proyecto default?)
El proyecto “default” creado por el script contiene
varios subdirectorios y archivos:
bin, donde aparecerá el binario al ser
compilado
pixmaps, donde se establecen los
íconos
src, donde está alojado el código
fuente
Makefile, licencia, archivo TODO (por
hacer), README y un ícono y el
proyecto .dev para DevC++ (para
compilar en Windows)
Para compilar el proyecto, se debe escribir el
comando make en una terminal ubicada en este
directorio.
21
Creando un nuevo proyecto (¿Qué hay dentro de src?)
Dentro del directorio src se encuentra el código fuente
para un demo con dos universos, menu-universe y
game-universe.
universes.cpp contiene la información de qué
universo será puesto a funcionar primero (por
defecto, MenuUniverse), así como la creación
de todos los universos del programa.
main-universe se encarga de configurar
OpenGL e inicializar todos los sistemas
básicos del juego (sonido, red, etc) y hacer
funcionar los demás universos.
menu-universe está pensado para mostrar al usuario un menú
donde sea introducido al juego y pueda configurar distintas
opciones del mismo.
game-universe está pensado para ser la parte principal del
videojuego, donde el usuario interactúe y se divierta con él.
22
¿Qué hay dentro de menu-universe?
Dentro del directorio menu-universe hay dos archivos:
menu-universe.h
menu-universe.h contiene la declaración (la forma, el esqueleto)
de lo que es el universo del menú, descendiente de la clase
Universe.
Esto quiere decir que es la particularización de la
clase Universe, con variables adicionales que no
están en Universe.
También se usa para incluir otras bibliotecas y otras
clases que sean útiles al juego, por ejemplo, la
biblioteca para cargar imágenes como texturas y la
biblioteca para renderizar texto como una textura.
menu-universe.cpp
contiene la implementación de lo que está descrito en menuuniverse.h, es decir, el código que realiza las operaciones
descritas por Universe más otras propias de menu-universe.
23
El menu-universe default
MenuUniverse es
class MenuUniverse : public Universe descendiente de Universe
{
public:
Estas funciones y
void Init(void);
variables fueron
void ProcessEvent(s_event & event); especificadas por
Universe como funciones
void Update(float t_trans);
virtuales (es decir,
void Redraw(void);
funciones que NO están
implementadas en
void Cleanup(void);
Universe pero DEBEN
bool mustCleanup;
ser implementadas por
sus descendientes).
bool mustInit;
private:
Estas variables no están en Universe;
son particulares a MenuUniverse y se
cam_ctl camera;
usarán para controlar algunos detalles
float rot_x, rot_y, rot_z;
de la animación (NO necesitan ser
privadas, pero en este caso es
};
conveniente).
24
El menu-universe default: Init()
void MenuUniverse::Init()
{
la cámara y sus parámetros internos en
init_cam_ctl_def(&camera);Inicializar
la posición y orientación default
mjEnableSimpleLighting();
Inicializar la iluminación con parámetros bastante
sencillos
rot_x = rot_y = rot_z = 0;
Poner la rotación de “algo” (un cubo, pero desde
aquí no se “ve”) en ceros (¡¡ceros, joven!!)
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
Usar el parámetro de material “color difuso” en las caras al verlas de frente y por detrás
glEnable(GL_COLOR_MATERIAL);
glColor3f(1,0,0);
}
Se establece el rojo como color actual
(R,G,B), cada uno de 0 a 1.
Las superficies tendrán color (al
habilitar la iluminación, se desactiva
el utilizar el color actual para la
superficie; esta función junto con la
anterior lo vuelve a activar)
25
El menu-universe default: ProcessEvent()
void MenuUniverse::ProcessEvent(s_event & event)
{
if (event.quit) Este evento es recibido cuando se quiere cerrar el programa, ya sea con
el botón de “cerrar” de la ventana, o con kill.
{
SetNextUniverse(NULL); Para activar la secuencia de terminación de MotorJ,
se manda NULL como el “siguiente Universo”.
}
switch(event.key_down){
Cuando se presiona una tecla, se recibe un
número distinto a 0 en key_down. El número
case 'a':{
camera.dsc_ctl.fwd = 1; corresponde a carácteres en minúsculas para las
letras y a números que no son carácteres para
}break;
teclas especiales (F1, flechas, etc.)
//...
}
switch(event.key_up){
En el menu-universe default se controla una
case 'a':{
cámara usando las flechas y las teclas “A” y Z”.
camera.dsc_ctl.fwd = 0; Aquí se está desactivando el movimiento
“avanzar” de la cámara, cuando se suelta la
}break;
tecla “A”.
//...
}
}
26
El menu-universe default: Update(t_elapsed)
void MenuUniverse::Update(float t_elapsed)
Se utiliza la rutina automática de actualización
{
de la cámara (la cámara volteará y se moverá
update_cam_auto(&camera); de acuerdo al comportamiento establecido).
rot_x += 0.02*t_elapsed;
rot_y += 0.04*t_elapsed;
rot_z += 0.01*t_elapsed;
Se incrementan los ángulos de rotación de
“algo” (un cubo) para simular movimiento. Nótese
que el incremento es dependiente del tiempo
utilizado en el frame anterior (si la máquina va a
pocos fps, se verá menos fluido pero el cubo girará
a la misma velocidad que en una máquina rápida
que pueda proporcionar más fps).
if (rot_x > 360) rot_x -= 360;
if (rot_y > 360) rot_y -= 360;
if (rot_z > 360) rot_z -= 360;
Se limitan los ángulos de 0 a 360°.
OpenGL maneja de manera nativa los grados,
no es necesario convertir a radianes.
}
27
El menu-universe default: Redraw()
void MenuUniverse::Redraw(void)
{
Se reinicia la pila de transformaciones de OpenGL
glLoadIdentity(); (¡¡Ceros, joven!!)
gluLookAt(camera.pos.x, camera.pos.y, camera.pos.z,
camera.pos.x+camera.dir.x, camera.pos.y+camera.dir.y, camera.pos.z+camera.dir.z,
camera.dirv.x, camera.dirv.y, camera.dirv.z); Se simula la perspectiva de la cámara
}
mjDefLightPos(); Se simula el posicionamiento de la luz default
“retroceden” 5 unidades con respecto a la posición
glTranslatef( 0, 0, -5); Se
actual. Todo lo que siga aparecerá recorrido 5 unidades
glRotatef(rot_x, 1,0,0); hacia adelante en “Z”.
glRotatef(rot_y, 0,1,0); Se realizan las rotaciones (todo lo que aparezca después
cada glRotatef estará rotado el número de grados
glRotatef(rot_z, 0,0,1); de
especificados alrededor del vector)
mjSolidCube(); Se dibuja el cubo. Voilá
Ninguna variable debe modificarse en este
procedimiento, para eso están ProcessEvent y Update
28
Cambiando entre universos
Cleanup() está vacío puesto que no hicimos ninguna asociación
de memoria de manera dinámica y no es necesario liberarla.
En Cleanup() también podríamos reiniciar algunas variables de
OpenGL (por ejemplo, glEnable(GL_COLOR_MATERIAL); )
pero para no hacer esto más complicado, lo dejaremos así y
simplemente tendremos en cuenta que sus efectos se
aplicarán también a game-universe cuando cambiemos.
Por omisión, el game-universe está “vacío” y listo para usarse.
Aprovecharemos game-universe para elaborar ahí nuestro juego
de video.
Pondremos un pedazo de código para cambiar de menu-universe
a game-universe.
29
El proceso de creación de universos: universes.cpp
Universe * create_universes()
{
Universe * result;
Se crean los universos (ambas variables son de
tipo Universe * y están declaradas más arriba)
menu = new MenuUniverse(); Los tipos MenuUniverse y GameUniverse se
game = new GameUniverse(); pueden usar porque en universes.h se
incluyeron menu-universe.h y game-universe.h
menu->Init(); Se realiza la inicialización
del primer universo
Se agregan los universos a
lista de universos (Nota:
list_append(0, (void **) &menu, &App.universes); la
el formato de lista es
list_append(1, (void **) &game, &App.universes); candidato a ser reemplazado próximamente).
game-universe será
accesible con el número 1
result = menu;
result->SetNextUniverse(result);
Se establece el menú como el primer
universo a ejecutar
return result;
}
30
Cambiando de un universo a otro
void MenuUniverse::ProcessEvent(s_event & event)
{
//
switch(event.key_down){
case RETURN:{
Universe * gameUni;
gameUni = (Universe *) list_find_type(1, 0, &App.universes)->data;
SetNextUniverse(gameUni);
}break;
case 'a':{
camera.dsc_ctl.fwd = 1;
}break;
//...
}
//...
}
31
Funcionamiento de un juego (desde un punto de vista abstracto)
Un juego de video, desde un punto de vista abstracto, debe
funcionar en este orden:
Recibir comandos del usuario
Usar esas órdenes para modificar variables
Actualizar las entidades “inteligentes” dentro del juego
(moverlas, verificar reglas de interacción, tomar decisiones)
Verificar cómo afectan las modificaciones y las condiciones
actuales en la simulación física
Verificar si se ha cumplido alguna de las reglas de juego
(condiciones de salida, etc.)
Mostrar los resultados y repetir el ciclo
32
Propuesta de mini-juego de video
Juego estilo Mario64
Personaje humanoide que camine sobre un terreno desigual
El terreno es relativamente grande
Se aparece en un extremo y se debe llegar al otro
El terreno es volcánico con charcos de lava
Hay malignos cubos que perseguirán al personaje si se acerca
demasiado a ellos
Hay gravedad
El humanoide puede saltar
Texturas y música acordes al juego
33
Ajuste de la propuesta de mini-juego al funcionamiento (abstracto)
de un juego
Recibir comandos del usuario (Las flechas desplazarán al
humanoide)
Usar esas órdenes para modificar variables (La posición del
humanoide será modificada con respecto a la perspectiva de la
cámara)
Actualizar las entidades “inteligentes” dentro del juego (Los
enemigos se moverán de manera aleatoria hasta que se
encuentren cerca del humanoide; en ese caso irán en dirección al
humanoide)
Verificar cómo afectan las modificaciones y las condiciones
actuales en la simulación física (Detección de colisiones contra el
piso: en caso de no haber colisión contra el piso, simular
gravedad; prueba de altura para determinar si el humanoide ha
tocado los lagos de lava)
Verificar si se ha cumplido alguna de las reglas de juego (Distancia
entre el humanoide y el lugar del fin del juego)
34
Mostrar los resultados y repetir el ciclo
¡Genial! Y ahora ¿Qué?..
¿Cómo lograremos hacer todo eso?
Veamos qué hay en el directorio “motorj”..
35
¿Qué contiene MotorJ?
app.h: Contiene la clase mjApplication. Esta clase proporciona varios datos sobre la
aplicación actual (resolución, soporte de sonido, soporte de red, etc.)
cam_ctl.h: Contiene la estructura (pronto clase) cam_ctl, que implementa una
cámara con dos modos de operación – manual y seguidora-, y su funcionamiento.
collisions.h: Contiene funciones para calcular colisiones – esfera vs esfera, triángulo
vs rayo, caja orientada vs caja orientada, caja alineada vs caja alineada, etc.
data_structs.h: Contiene la implementación de una lista genérica. También es
candidata a volverse clase, para facilitar su manejo.
events.h: Contiene la estructura “evento” y la definición de varias teclas “especiales”
(RETURN, LEFT_KEY, etc.).
glContexts.h: Intento (todavía infructuoso) de poder usar OpenGL directamente en
varios hilos. No está lista, no usar.
lanjobot.h: Humanoide formado por cubos. Con 30+ ángulos configurables, puede
tomar casi cualquier pose humana imaginable.
musicplayer-class.h y networkctl-class.h: Clases que controlan la música de fondo y
la red (PERO solo en la versión para Nintendo DS – no usar).
objects.h: Contiene instrucciones para trazar varios objetos. Entre ellos un cubo
sólido y uno de marco de alambre.
ovejota.h: Permite interpretar archivos en formato .obj para emplearlos como
modelos 3d.
36
¿Qué contiene MotorJ? (continuación)
sound.h: Permite cargar, a través de SDL_Mixer, archivos .ogg y .wav para usar
como música de fondo y efectos de sonido. Podría cambiar sustancialmente dentro
de poco tiempo.
stenciltricks.h: Realiza trucos visuales con el buffer de stencil, p.ej., como una
“cortinilla” de caricatura (de hecho es el único efecto que contiene en este
momento, pero se planea incorporarle más).
support.h: Contiene las estructuras de datos más usadas, así como todas las
funciones de geometría analítica básicas. Casi todos los demás archivos lo
incluyen; podría ser considerado como la base de MotorJ
textrender.h: Convierte de texto a una textura fácilmente utilizable por OpenGL,
usando SDL_TTF.
textures.h: Carga imágenes en casi cualquier formato para emplearse como
texturas en OpenGL, usando SDL_Image.
sound.h: Permite utilizar archivos .ogg y .wav como música de fondo y efectos de
sonido, respectivamente, usando SDL_Mixer.
universe-class.h: Establece la “forma” general de los universos.
*.cpp: Contienen la implementación. Si hay algún bug o quieren ver como se hizo,
es una buena idea consultarlos y modificarlos.
La documentación está en proceso; la página oficial es
http://wiki.lidsol.net/wiki/index.php?title=Motorj_doc_index
37
Contacto
•
•
•
•
•
•
•
LIDSOL
http://www.lidsol.org
Blog
http://mexinetica.com/~lanjoe9/bloginetica
Email
Lanjoe9 at mexinetica . com
Alejandro Valenzuela Roca
38

Documentos relacionados