Sie sind auf Seite 1von 56

PROGRAMACIN GRAFICA EN 3D

Que es XNA?

Xna es un Api que se integra en el framework de .NET para facilitar el desarrollo de videojuegos. Esta facilidad viene asociada a que proporciona tareas comunes en el desarrollo de los videojuegos como el renderizado, captura de controles, etc. ya implementadas y optimizadas para ser ejecutadas sobre el Common Language Runtime. Actualmente XNA esta slo disponible para los lenguajes C# y Visual Basic, si bien Microsoft tiene intencin de extenderlo a el resto de lenguajes soportados por el Visual Studio. Como nota ms destacada se puede decir que un videojuego desarrollado en XNA podra funcionar en cualquier sistema operativo que tenga una implementacin del .NET framework. Este hecho hace que sea relativamente sencillo desarrollar videojuegos no solo para PCs con Windows sino que tambin se pueden desarrollar para XBOX 360 y Zune. En los siguientes apartados se ira construyendo un pequeo videojuego en 3D paso por paso utilizando las utilidades que nos ofrece XNA.

Curso de Graduacin

Pgina 1

PROGRAMACIN GRAFICA EN 3D

Entorno XNA
Primero Inicialicemos XNA

Figura 1 Acceso directo a XNA

Para comenzar, inicialicemos XNA Game Studio Express y conozcamos el entorno de la aplicacin (figura 2). En el rea central, organizada en tabs o pginas, observar una pgina con el nombre de Start Page donde aparecen 4 cuadros. El primero Recent Projects muestra los ltimos proyectos trabajados y dos vnculos para abrir y crear proyectos. El segundo Getting Started es un cuadro de ayuda para utilizar Visual C# Express y conectarse a la comunidad C#. El tercer cuadro Visual C# Express Headlines se presenta comentarios del diseador web de Microsoft. El ltimo cuadro y el ms grande presenta los ttulos de las publicaciones realizadas por Microsoft relacionadas con C# Express.

Curso de Graduacin

Pgina 2

PROGRAMACIN GRAFICA EN 3D

Figura 2 Entorno de trabajo XNA

En el rea derecha encuentra el Solution Explorer o Explorador de contenido de proyectos y en el rea izquierda encontrar una barra donde se encuentra el Toolbox la caja de herramientas; proyecto. As que procedamos a construir un proyecto. Vamos a la barra de men, ingresamos en File, New Project (figura 3). Un proyecto es la organizacin ms grande que podremos tener en el entorno de programacin de Visual estudio. En un proyecto, usualmente definiremos nuestra aplicacin (para el caso nuestro un juego) y todos los contenidos necesarios para su funcionamiento (cdigo fuente, imgenes, sonidos, videos, etc). ahora no observamos nada en ellos, pero podremos ver su funcionalidad ms adelante cuando trabajemos sobre un

Curso de Graduacin

Pgina 3

PROGRAMACIN GRAFICA EN 3D
Fig ura 3 Crear un nuevo proyecto XNA

Al iniciar un nuevo proyecto podemos ver la siguiente ventana (figura 4).

Fig ura 4 Opciones de nuevo proyecto XNA

Esta ventana contiene todos los tipos de proyectos que se pueden realizar con XNA y visual estudio. Para nuestro caso estamos interesados en realizar un proyecto del tipo de videojuegos Windows Game (XNA), esto significa que el entorno de trabajo se va a preparar para que podamos desarrollar un videojuego cargando las libreras apropiadas. Cabe anotar que el titulo Windows Game no se limita solo a juegos para el sistema operativo Windows; mas adelante veremos que un juego Windows Game puede ser usado tambin por ejemplo en la Xbox 360 o en una computadora de bolsillo (PDA)
5

si se realizan los ajustes necesarios. Sin embargo si te


Pgina 4

Curso de Graduacin

PROGRAMACIN GRAFICA EN 3D
fijas, ya hay tambin en la lista, un proyecto de tipo Xbox 360 Game, por lo que si esa consola es nuestra meta para el juego a desarrollar, deberamos usar esa plantilla de una ves. Selecciona el icono Windows Game y colocale un nombre al proyecto, en este caso lo nombraremos WindowsGame1 el nombre por defecto, pero t puedes usar el nombre que quieras. Observar que en el rea central se crea una nueva pgina con el nombre de Game1.cs (figura 5).

Fig ura 5 Proyecto Windows Game

Si ejecutamos nuestro proyecto (F5) o en la barra de Men, debug start debuggin, aparecer una ventana con el nombre del proyecto y un fondo azul (figura 6). All visualizar el video juego, y validar su funcionamiento a medida que lo vamos desarrollando. Podemos cerrar y continuar. Ejecutar un proyecto, equivale a iniciar el juego como si ya estuviera listo. La ventaja de ejecutar un proyecto en visual estudio (mientras lo programamos), radica en que podemos probar los resultados del cdigo que vamos escribiendo. Para ejecutar un proyecto vasta con presionar la tecla (F5) o dar clic en el botn con forma de play que hay en la barra de tareas. Al presionar este botn XNA revisa las sentencias de cdigo escritas para corroborar si estn bien escritas, y luego si las ejecuta. En caso de encontrar errores avisara y abortara la ejecucin del programa. En caso de encontrar errores en el cdigo, se despliega una lista con los
Curso de Graduacin Pgina 5

PROGRAMACIN GRAFICA EN 3D
mismos. La experiencia con Visual Estudio, te dar la prctica que necesitas para saber que hacer con cada mensaje de error, de momento, si sigues todos los pasos al pie de la letra, no deberas recibir errores por el cdigo que escribas. Sin embargo ten presente que hay otro tipo de errores que te pueden salir cuando ejecutes el juego, relacionados con el ambiente de trabajo en el que te encuentres, por ejemplo te podran salir errores por razones como las siguientes: La tarjeta de video no cumple los requisitos mnimos para XNA. No tienes permisos de administrador en la maquina en la que trabajas y esto le impide a XNA por ejemplo leer/escribir en algn disco.

Fig ura 6 Ejecutar un proyecto Windows Game en blanco

Vamos a mirar ahora el entorno de trabajo de nuestro proyecto. En el Explorador de Contenido (figura 7) se nos muestran los elementos de nuestro proyecto WindowsGame1, el diagrama en forma de carpetas nos resulta natural. En cada carpeta que se crea, podremos almacenar los activos (ya sea cdigo, imgenes, sonido o video) que vamos a utilizar en nuestro juego. Por defecto, XNA crea una plantilla de juego en blanco que nos servir de gua para programar nuestro juego (que es la que estamos viendo ahora). Estos contenidos bsicos pueden ser revisados con este explorador.
Curso de Graduacin Pgina 6

PROGRAMACIN GRAFICA EN 3D

Fig ura 7 Explorador de contenido

4. Cdigo generado Ahora comenzamos a relacionarnos con el cdigo del proyecto. Ve al Explorador de Contenido y dale doble clic sobre el archivo Game1.cs. Aparecer una nueva pgina con el cdigo del proyecto. El namespace WindowsGame1, es el espacio de programacin de nuestro proyecto y guardar todas las clases que desarrollemos. Antes de continuar vamos a hacer algunas precisiones sobre los trminos que vamos a utilizar de aqu en adelante. C# es un lenguaje de programacin de alto nivel, lo que significa que fue diseado para facilitarle el desarrollo a los programadores; esto es posible debido a que implementa la filosofa de Programacin Orientada a Objetos o POO. La programacin orientada a objetos, le permite al programador entre otras ventajas, realizar una abstraccin de la realidad por medio de unas estructuras que organizan el cdigo y le permiten usarlo de manera ms eficiente y fcil que los lenguajes no orientados a objetos. Las estructuras bsicas de la orientacin a objetos en C# son: El namespace. La Clase
Pgina 7

Curso de Graduacin

PROGRAMACIN GRAFICA EN 3D
El mtodo

Los Namespaces: Podemos hacer una analoga de un namespace con una carpeta de archivos, de esta manera nuestro cdigo se puede separar en carpetas para conservar el orden. De hecho, cada namespace efectivamente corresponder a una carpeta de Windows en nuestros archivos del proyecto; no obstante esto no es estrictamente necesario pues uno podra darles nombres diferentes a los de las carpetas, pero por motivos de orden y facilidad se recomienda mantener los nombres iguales. Las Clases: Es la siguiente estructura en nuestra organizacin. Una clase se puede asociar como su nombre lo indica, con una categora; por ejemplo una clase podran ser los Carros y otra las motos y todo el cdigo relacionado con los carros ira en la clase de los carros as como el de las motos. Siguiendo con nuestra analoga, si los namespaces son las carpetas que contienen En otras nuestros archivos del proyecto, las clases son efectivamente, los archivos que se colocan dentro de cada carpeta. palabras los namespaces pueden contener muchas clases y las clases no son ms que archivos que tienen en su interior el cdigo correspondiente a esa clase. Los mtodos: Finalmente, tenemos los mtodos, los cuales representan todos aquellos servicios que una clase puede ofrecernos; para el ejemplo de la clase carro, podramos tener mtodos como acelerar, frenar, pitar y cualquier otro servicio que pueda prestarnos el carro. En otras palabras los mtodos se corresponden a fragmentos cdigo escrito que hay en cada clase (una vez mas, por razones de orden, es que el cdigo se divide en mtodos). Estos conocimientos bsicos nos sern suficientes por ahora para

continuar explorando XNA. Posteriormente iremos precisando ms las definiciones y los conceptos de programacin.
Curso de Graduacin Pgina 8

PROGRAMACIN GRAFICA EN 3D

Regresando al proyecto en blanco que tenamos abierto, en el explorador de soluciones podemos ver la clase Program (figura 11) (note que pertenece al namespace WindowsGame1) Esta clase contiene el mtodo denominado main (figura 11) que es el punto de inicio de cualquier proyecto. Sencillo hasta el momento, este mtodo lo que hace es permitir que nuestro juego ejecute todo lo que escribamos en la clase Game1. La clase program que cre automticamente XNA cuando le dijimos que queramos hacer un Windows Game, es la clase en la que se ejecutar el juego; es decir, esta clase har uso del cdigo que se encuentre en todas las dems clases. El mtodo main, representa en cualquier tipo de programa (no solo en los juegos) el punto de partida de una aplicacin. Es decir que a partir de este mtodo se empiezan a leer las instrucciones, por lo que este es un mtodo de carcter obligatorio y debe respetarse su nombre. Ampliando nuestras definiciones iniciales, entonces podemos decir que los mtodos son agrupaciones de cdigo a las cuales se les ha puesto un nombre para poder ser usadas en muchos lugares diferentes de nuestro programa. Puede identificar un mtodo por que tiene un nombre luego abre y cierra parntesis y luego abre llaves, posteriormente hay cdigo del mtodo como tal, y finaliza con llaves nuevamente, por ejemplo: miMetodo( ){ // Cdigo del mtodo } Las dems partes de un mtodo (indicadores de acceso [que van antes del nombre], parmetros [que van dentro de los parntesis] y valor de retorno [que va despus del indicador de acceso]) sern discutidas posteriormente.
6

Curso de Graduacin

Pgina 9

PROGRAMACIN GRAFICA EN 3D

Figura 11 El mtodo main

Si continuamos explorando nuestro entorno de trabajo, notaremos que en la parte superior (figura 12), aparecen dos campos con listas desplegables; la primera despliega las clases con su namespace al que pertenecen y la segunda los mtodos de la clase seleccionada en el primer campo. El uso de estas listas es una tcnica que nos permitir movilizarnos con mayor facilidad a medida que el proyecto crezca.

Listas de navegacin de clases y mtodos

Figura 12

Por ahora puedo asegurar que entendemos que una clase es una agrupacin de mtodos, as pues, una clase agrupa un conjunto de mtodos (servicios) que podremos pedirle a ella que realice. Cabe anotar que una clase tambin pude contener variables a las cuales denominaremos atributos. Por ejemplo:

MiClase{ // Atributos miMetodo( ){


Curso de Graduacin Pgina 10

PROGRAMACIN GRAFICA EN 3D
// Cdigo del mtodo } OtroMetodo( ){ // Cdigo del mtodo } } Los atributos constituyen caractersticas comunes a los objetos de una clase, es decir, para la clase Carro de la que hablamos previamente, podramos definir atributos como color, matricula, kilometraje, modelo, etc. Es decir que cada carro que construyamos con esa clase tendra que definir valores propios para esos atributos. El concepto de objeto entonces aparece ante nosotros como la instancia (el carro que carro tipo Carro. construimos) a partir de una clase. Es decir que cada que construyamos (instanciemos) con la clase Carro, es un objeto del

Volvamos nuevamente al Explorador de contenido y dale clic derecho sobre la clase Game1.cs y selecciona Ver cdigo (View code). El cdigo que aparece es el de la clase Game1 que es hija de la clase Microsoft.Xna.Framework.Game es decir hereda la estructura que explicaremos a continuacin la cual es necesaria para crear un videojuego en XNA. Cuando hablamos de que una clase hereda de otra nos referimos aun concepto poderoso de la programacin orientada a objetos, el cual esta relacionado con la reutilizacin de cdigo. Para nuestro ejemplo de la clase carro, imaginemos que tenemos una clase mas general como Vehiculo que tiene atributos y mtodos que cualquier tipo de vehiculo debera tener; por ejemplo atributos como color, velocidad mxima, modelo, tipo de motor, etc y mtodos como encender,
Pgina 11
7

Curso de Graduacin

PROGRAMACIN GRAFICA EN 3D
apagar, acelerar, frenar. De esta manera nuestra clase Carro podra heredar (ser hija de) la clase Vehiculo y de esta manera no tendra que definir ni los atributos ni los mtodos de su padre pues por ser hija de el ya los tiene, as pues otras clases que se nos ocurran, como Lancha y Moto podran heredar tambin de Vehiculo. La sintaxis de herencia es muy sencilla en C#; basta con colocar el nombre de la clase hija y seguido de dos puntos : el nombre de la clase padre. MiClase : ClasePadre{ // Atributos mtodo( ){ // Cdigo del mtodo } }

Retomando nuestro cdigo, la clase Game1 (uno puede cambiar este nombre hacer a gusto propio) es la clase principal que XNA nos crea los mtodos necesarios para que un Windows Game se muestre en la pantalla; inicialmente automticamente. En ella se encuentran

solo coloca el cdigo necesario para crear una ventana vaca de color azul. La primera parte del cdigo (figura 13) es la implementacin de varias libreras que se van a usar en el desarrollo del cdigo para el juego. Las libreras se refieren a cdigo que los creadores de XNA ya han creado y lo han puesto a disposicin de nosotros para evitarnos mucho trabajo innecesario; de esta manera ya existen por ejemplo libreras que le indican a la pantalla cuando y que pintar, segn los parmetros que nosotros le demos. Para implementar una librera en XNA se utiliza la palabra clave using y el nombre de la librera que
8

siguiendo la estructura de namespaces en la

se encuentre. Esto pone a nuestra disposicin, una funcionalidad


Pgina 12

Curso de Graduacin

PROGRAMACIN GRAFICA EN 3D
deseada.

Figura 13 Libreras de un Windows Game

Como vemos la clase Game1 tiene un constructor y otros mtodos (ver figura 14). El constructor, no es ms que otro mtodo, cuya caracterstica principal es que posee el mismo nombre que el de la clase. Como veremos mas adelante, el constructor, tiene la caracterstica de permitir que la clase sea instanciada (es decir permite crear los objetos de esa clase, para el ejemplo de la clase Carro, es el mtodo que no entrega carros nuevos cuando se lo pedimos). Ahora, una instancia de una clase, es decir un objeto, es una entidad propia e independiente, que puede prestar todos los servicios (mtodos) de la clase que instancia.

Curso de Graduacin

Pgina 13

PROGRAMACIN GRAFICA EN 3D

Figura 14 Estructura de la clase Game1

De los mtodos que hay en la clase Game1 vamos a ir hablando poco a poco en lo corrido de estos tutoriales, sin embargo vamos a introducir los 2 ms importantes. El mtodo Update es llamado una y otra vez mientras el juego se ejecuta. Aqu sucede toda la accin. Desde l se manejan los movimientos del jugador, entradas del control, etc., actualizando todos los cambios que se realizan en el juego. El mtodo Draw es el corazn y el alma del juego. Su funcin es colocar todas las imgenes en la pantalla, algunas de las cosas que el hace por nosotros son: Se asegura que el dispositivo de grfico sea vlido. Limpia el fondo de la pantalla con el color azul que vimos anteriormente. Le dice a la tarjeta de video que se va a dibujar una escena. Le dice a cada uno de los componentes que heredan el mtodo que dibujen. Le dice a la tarjeta de video cuando ha terminado de dibujar la escena. Le dice a la tarjeta de video que muestre los resultados.

TEMA COMPLEMENTARIO La sintaxis del lenguaje C# es otro tema que vamos a explorar sobre la marcha, no obstante es importante que hagamos algunas precisiones antes de
Curso de Graduacin Pgina 14

PROGRAMACIN GRAFICA EN 3D
empezar a programar. Palabras reservadas Como todo lenguaje de programacin (as como cualquier lenguaje en general), C# se compone de un conjunto de palabras (denominadas palabras reservadas) que conforman el lenguaje con el que le vamos a decir al computador que queremos que haga. El hecho de que sean reservadas, significa que son exclusivas del lenguaje y no las podremos usar como nombre de nuestras variables o clases. Las palabras reservadas de C# son:
abstract as base bool break byte case catch char checked class const continue decimal default delegate event explicit extern false finally fixed float for foreach goto if implicit in int interface internal new null object operator out override params private protected public readonly ref return sbyte sealed short struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual volatile void do double else enum is lock long namespace sizeof stackalloc static string while get set where

El significado y uso de ellas ser estudiado mas adelante,

por

el

momento podramos reflexionar sobre el hecho de que un leguaje se componga de tan pocas palabras (a diferencia de un lenguaje humano como el espaol que tiene millones de palabras). Hemos resaltado en negrilla las palabras reservadas que usaremos mas comnmente durante el desarrollo de nuestros tutoriales.

Operadores.
Curso de Graduacin Pgina 15

PROGRAMACIN GRAFICA EN 3D
C# proporciona un amplio conjunto de operadores, que son smbolos que especifican las operaciones que se deben realizar en una expresin. C# dispone de los operadores aritmticos y lgicos habituales, y de una gran variedad de otros operadores, como se muestra en la siguiente tabla. El uso de los operadores ser estudiado mas adelante, no obstante es importante conocer de la existencia de estos operadores y de las palabras reservadas para que no se nos hagan extraas en prximas lecciones. Hemos resaltado con negrilla los operadores que usaremos ms comnmente durante el desarrollo de nuestros tutoriales.

Tipos de datos Son las construcciones que se han creado en los lenguajes de programacin para almacenar valores especficos. Algunos tipos de datos son comunes en todos los lenguajes de programacin, vamos a ver los mas comunes de c#. Numricos: como su nombre lo indica, representa nmeros. Datos enteros: permiten guardar nmeros enteros (sin decimales),

sus palabras reservadas son int, long y short cada una define si rango, y el de la segunda es el doble de grande, puede tomar valores positivos y negativos. int valor = 5454; long valor2 = 34432223; Datos no enteros: Permiten guardar datos en formato decimal en

diferentes rangos segn la palabra clave que se utilice (double, float, ) float valor = 5.53f; // observe que en este caso hay que colocar una f al final double valor2 = 8.345f; Bolanos: Representan datos que tienen solo 2 valores posibles. bool: puede tomar 2 valores, verdadero (true) o falso (false), su
Pgina 16

palabra reservada es bool.


Curso de Graduacin

PROGRAMACIN GRAFICA EN 3D

bool valor = true; De texto: Almacenan datos en formato no numricos es decir, cadenas de caracteres. string: Permite almacenar cadenas de caracteres en formato

universal (unicode), su palabra reservada es string, las cadenas de texto se encierran entre comillas dobles. string cadena = Esta es una cadena de texto; char: permite almacenar un nico carcter unicode. Su palabra

reservada es char, y los caracteres se escriben entre comillas sencillas. char letra = a;

VertexPositionColor XNA
Con VertexPositionColor, lo que hacemos es pasarle las posiciones y colores den nuestros puntos, y luego mediante VertexDeclaration le decimos al XNA y que lo dibuje. Primero, debemos crear las dos variables, VertexPositionColor y VertexDeclaration:
VertexDeclaration _vertexDeclaration; VertexPositionColor[] _verticestriangulo;

Bien, ahora necesitamos iniciarlas:


_verticestriangulo = new VertexPositionColor[3]; _vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);

Como hacemos un triangulo, solo necesitamos 3 puntos. Luego, el _vertex declaration, creamos un nuevo pasndole dentro el GraphicsDevice, y el tipo de vrtices que estamos haciendo, en este caso VertexPositionColor. Ahora necesitamos crear los puntos:
_verticestriangulo[0].Position = new Vector3(1, 0, 0); _verticestriangulo[0].Color = Color.Red; _verticestriangulo[1].Position = new Vector3(0, 1, 0); _verticestriangulo[1].Color = Color.Green;

Curso de Graduacin

Pgina 17

PROGRAMACIN GRAFICA EN 3D

_verticestriangulo[2].Position = new Vector3(-1, 0, 0); _verticestriangulo[2].Color = Color.Yellow;

Nosotros tomamos esos puntos por esta razn:

Como ven en la imagen, Los dos puntos de abajo se pocisionan en -1, 0 y 1, 0. si alguno se pusiera en 0.5, el triangulo seria distinto, si le pusiramos 0, quedara un triangulo rectngulo, mismo caso si pusiramos en 1,1 el punto de arriba. Si no nosotros quisiramos hacer el triangulo mas grande, deberamos aumentar estos valores. Bien, ahora que ya tenemos todos los puntos creados, debemos dibujarlos, pero antes, necesitamos un shader simple, pero necesario. Lo que hace este shader es revolvernos el color que le ingresamos arriba. Veamos:
float4x4 World; float4x4 View; float4x4 Projection; struct VertexShaderInput { float4 Position : POSITION0; float4 Color : COLOR0; }; struct VertexShaderOutput { float4 Position : POSITION0; float4 Color : COLOR0; }; VertexShaderOutput VertexShaderFunction(VertexShaderInput input) { VertexShaderOutput output; float4 worldPosition = mul(input.Position, World); float4 viewPosition = mul(worldPosition, View); output.Position = mul(viewPosition, Projection);

Curso de Graduacin

Pgina 18

PROGRAMACIN GRAFICA EN 3D
output.Color = input.Color; return output; } float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0 { float4 Color = input.Color; } return Color;

technique Technique1 { pass Pass1 { VertexShader = compile vs_2_0 VertexShaderFunction(); PixelShader = compile ps_2_0 PixelShaderFunction(); } }

Bsicamente, lo que hacemos es tomar el color que ingresamos y mostrarlo. Ustees dirn que por que no usar BasicEffect y dejar de complicarnos. Bueno, se puede usar. Pero nuestro triangulo seria todo blanco, y no es lo que buscamos. Una vez echo eso. Creen una variable Effect y cargenlo:
Effect _effect;

Inicienlo:
_effect = Content.Load<Effect>("Effect");

Ahora en el metodo Draw, debemos colocar esto:


_effect.Parameters["World"].SetValue(Matrix.CreateScale(4)); _effect.Parameters["View"].SetValue(Matrix.CreateLookAt(new Vector3(0,0,-10), new Vector3(0,1,0), Vector3.Up)); _effect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, graphics.GraphicsDevice.Viewport.AspectRatio, 0.01f, 1000.0f ); _effect.Begin(); foreach (EffectPass pass in _effect.CurrentTechnique.Passes) { pass.Begin(); graphics.GraphicsDevice.VertexDeclaration = _vertexDeclaration; graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, _verticestriangulo, 0, 1); pass.End(); } _effect.End();

Primero, lo que hacemos es pasarle los datos efectos, luego, usando un foreach recorremos lo efectos dentro de efecto, y se lo aplicamos a triangulo. Luego, lo dibujamos mediante DrawUserPrimitives. Si arranca el juego, veran el triangulo

Curso de Graduacin

Pgina 19

PROGRAMACIN GRAFICA EN 3D Cargando un Modelo 3D


Les comentare como cargar un objeto 3D texturizado. La creacin del juegos 3D es uno de los aspectos mas difciles, ya que no solo hay que saber como usar XNA, sino tambin aprender a modelar en 3D y matemticas. Parece mucho, pero siempre se puede conseguir a un amigo para trabajar en conjunto. Para el modelado en 3D se puede usar los siguientes programas: Autodesk 3D Studio Max Autodesk Maya zBrush Cinema 4D
//Variables Model Pantera; float AspectoRatio = 640.0f / 480.0f; Matrix MatrizProyeccion; Matrix MatrizVista; //Inicamos la Variable Pantera = Content.Load<Model>("Pantera"); //Luego en el metodo Draw colocamos esto Vector3 Camara = new Vector3(0, 100, 250); //Camara MatrizProyeccion = Matrix.CreatePerspectiveFieldOfView (MathHelper.ToRadians(45.0f), AspectoRatio, 1.0f, 10000.0f); //La proyeccion MatrizVista = Matrix.CreateLookAt(Camara, new Vector3(0,0,0), Vector3.Down); //Hacia donde debe mirar foreach (ModelMesh mesh in Pantera.Meshes) //Carga del modelo { foreach (BasicEffect effect in mesh.Effects) { //Cualquier tipo de efecto sobre el modelo debe ser puesto aca //Luz Normal effect.EnableDefaultLighting(); //Calcular la matriz del mundo effect.World = mesh.ParentBone.Transform * Matrix.CreateScale(5); //Asignar la matriz de Vista effect.View = MatrizVista; //Asignar la matriz de proyeccin effect.Projection = MatrizProyeccion; } //Dibujar el modelo con los efectos mesh.Draw(); }

Algo importante que cabe destacar, el XNA, soporta solo dos tipos de extenciones en los objetos, .X y .FBX. Para exportar a .X deben buscar algun exportador. Para .FBX, 3D Studio Max y Maya ya traen la capacidad de exportar a .FBX. Nosotros, siempre trabajremos con .FBX.

Curso de Graduacin

Pgina 20

PROGRAMACIN GRAFICA EN 3D
Bueno, como ven no es tan difcil. Ahora si presionan F6, pueden ver que el compilador tira un error, algo como esto: MissingAsset("Una ruta"), esto se debe a que cuando uno exporta un modelo con alguna textura, el exportador coloca la ruta de la foto de la computadora en donde se encuentra. Para arreglar esto se debe seguir estos paso: 1) En el momento que se estas exportando a FBX, hay que habilitar la opcin que dice Convert ASCII code, esto es para poder ver las configuracin del objeto por dentro. 2) Una vez agregado a la carpeta Content, deben hacer doble clic en el modelo. Podrn ver todo el cdigo del modelo 3) Vallan a Edit/Find And Remplace/Quick Find, y ponen para buscar RelativeFilename. Deben reemplazar la ruta que tienen ah por la suya en donde se encuentra la textura. Esto lo deben hacer en todas los lugares en donde diga RealtiveFilename. Una vez echo esto, hagan F6, y vern que no hay ningn error, y luego F5 y vern una pantalla como esta:

Esto se aplica igual que modelos sin textura, solo que no requerir entrar adentro del objeto y cambiar la ruta de la imagen ni nada.
Ejemplo de lo anterior en la carpeta ejemplos de CD en el archivo CarganModelo3DXNA

Curso de Graduacin

Pgina 21

PROGRAMACIN GRAFICA EN 3D
Rotacin, escala, posiciones en 3D XNA
Para poder controlar un poco el mundo con las cosas 3D, rotaciones, poner a escala y las posiciones. Les colocare todas las variables que usaremos y que nos ayudara para, en este caso, controlar las posiciones del nuestro mundo 3D:

Matrix.CreateRotationX //Esto nos sirve para rotar nuestro modelo desde el eje X Matrix.CreateRotationZ //Esto nos sirve para rotar nuestro modelo desde el eje Z Matrix.CreateRotationY //Esto nos sirve para rotar nuestro modelo desde el eje Y Matrix.CreateScale //Con esto podremos aumentar y reducir el tamao de nuestro objeto Matrix.CreateTranslation //Con esto podemos mover nuestro objeto como nos plazca en cualquier posicin

Con estas cinco variables nosotros podemos controlar nuestro mundo 3D. Ahora veremos como utilizar estas variables: La variables deben ser colocadas dentro de nuestro mtodo Draw de nuestro modelo 3D, mas exactamente en la parte de effect.World, ah almacena todos los cambio de nuestro modelo con el mundo 3D.
foreach (ModelMesh mesh in casa.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.EnableDefaultLighting(); effect.World = //Aqu deben ser colocadas nuestras variables effect.View = View; effect.Projection = Projection; } mesh.Draw(); }

Bien, veremos un ejemplo de cada uno:


effect.World = Matrix.CreateRotationX(MathHelper.ToRadians(90)) ;

Aqu lo que hemos echo es rotar nuestro objeto 90 grados en el eje X, el numero que debe ser colocado all dentro debe estar en Radianes, por eso utilizamos MathHelper para que nos convierta en grados a radianes. Esto se aplica exactamente para la rotacin en Z e Y.
effect.World = Matrix.CreateScale(5,5,5);

Curso de Graduacin

Pgina 22

PROGRAMACIN GRAFICA EN 3D

Aqu haremos mas grande nuestro modelo, lo que se debe colocar adentro es, cuantos mas grande queremos hacer en eje X, Y y Z. Tambin podramos usar una variable Vector3 para almacenar nuestro tamao.
effect.World = Matrix.CreateTranslation(new Vector3(50,0,60);

Con esto nosotros podremos calcular la posicin de nuestro objeto, all dentro, como ven, deben colocar una variable Vector3. Bien, ahora, si nosotros quisiramos aumentar el tamao y moverlo el objeto a alguna pocision en particular debemos hacer esto:
effect.World = Matrix.CreateTranslation(150,0,90) * Matrix.CreateScale(5,9,5):

Como ven, si quisiramos agregar alguna propiedad a nuestro objeto, solo lo debemos hacer agregando un * entre cada propiedad.
El ejemplo de rotacin y traslacin esta en el archivo RotarEscalaMover3DXNA

Cmara en Primera Persona XNA


Para crear una cmara, es algo medio complejo, mas que nada por las matemticas que esto recurre, de hecho, tomando el ejemplo que encontr en Internet hay un padre cosas que no entiendo, aun as, seguir adelante:
//Variables Vector3 cameraPosition; Vector3 cameraTarget; float cameraYaw; float cameraPitch; MouseState mStateCashe; Matrix Projection; Luego en el mtodo initialize: //En ininitalize Mouse.SetPosition(graphics.GraphicsDevice.Viewport.Width / 2, graphics.GraphicsDevice.Viewport.Height / 2); mStateCashe = Mouse.GetState(); Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper. ToRadians(45f), graphics.GraphicsDevice.Viewport.AspectRatio, 1f, 1000f); Despus en el Update: //Update KeyboardState kState = Keyboard.GetState();

Curso de Graduacin

Pgina 23

PROGRAMACIN GRAFICA EN 3D
MouseState mState = Mouse.GetState(); float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; Vector3 moveVector = Vector3.Zero; if (kState.IsKeyDown(Keys.W)) moveVector.Z += 3.0f * deltaTime; if (kState.IsKeyDown(Keys.S)) moveVector.Z -= 3.0f * deltaTime; if (kState.IsKeyDown(Keys.A)) moveVector.X += 3.0f * deltaTime; if (kState.IsKeyDown(Keys.D)) moveVector.X -= 3.0f * deltaTime; float mouseX = mState.X - mStateCashe.X; float mouseY = mState.Y - mStateCashe.Y; cameraPitch += (mouseY * 0.4f) * deltaTime; cameraYaw -= (mouseX * 0.4f) * deltaTime; cameraPitch = MathHelper.Clamp(cameraPitch, MathHelper.ToRadians(-60.9f), MathHelper.ToRadians(60.9f)); Mouse.SetPosition(graphics.GraphicsDevice.Viewport.Width / 2, graphics.GraphicsDevice.Viewport.Height / 2); Matrix cameraViewRotationMatrix = Matrix.CreateRotationX(cameraPitch) * Matrix.CreateRotationY(cameraYaw); Matrix cameraMoveRotationMatrix = Matrix.CreateRotationY(cameraYaw); Vector3 transformedCameraReference = Vector3.Transform(Vector3.UnitZ, cameraViewRotationMatrix); cameraPosition += Vector3.Transform(moveVector, cameraMoveRotationMatrix); cameraTarget = transformedCameraReference + cameraPosition;

Bueno, mucho cdigo, bsicamente, lo que hacemos all arriba es configurar las pocisiones del Mouse y las rotaciones. No hay mucho que explicar. Luego en el draw, en el modelo 3D que estn dibujando, dentro de effect.view deben colocar esto:
effect.View = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up); El ejemplo de manejo de cmaras esta en CamaraPrimeraPersonaXNA

cmara en Tercera Persona XNA


Primero, debemos cargar dos modelos 3D, usaremos uno que seria nuestro personaje y otro objeto cualquiera solo para que se note que nuestro modelo se mueve y la cmara sigue al modelo. Para nuestro modelo personaje necesitaremos estas variables, adems de la variable Model :
//Variables Quaternion panterarot; //Quaternion: Quaternion lo que hace es almacenarnos todas las rotaciones que tenga nuestro modelo, luego vern que podemos usarla mediante Matrix Vector3 posicionpantera; Vector3 velpantera; Vector3 cameraposition; Vector3 cameratarget;

Curso de Graduacin

Pgina 24

PROGRAMACIN GRAFICA EN 3D
float camerayaw; MouseState mouse1; Matrix lokatcamara; Matrix Projection; Bien, ahora en el mtodo Initialize: posicionpantera = new Vector3(20, 0, 0); Mouse.SetPosition(graphics.GraphicsDevice.Viewport.Width / 2, graphics.GraphicsDevice.Viewport.Height / 2); mStateCashe = Mouse.GetState(); velpantera = new Vector3(0, 0, 0); Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper. ToRadians(45f), graphics.GraphicsDevice.Viewport.AspectRatio, 1f, 10000f); Lo que hacemos bsicamente all es arranca toda las variables y setear el Mouse al medio. KeyboardState kState = Keyboard.GetState(); MouseState mState = Mouse.GetState(); Mouse.SetPosition(graphics.GraphicsDevice.Viewport.Width / 2, graphics.GraphicsDevice.Viewport.Height / 2); float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; if (kState.IsKeyDown(Keys.W)) velpantera.Z += 1.0f * deltaTime; else if (kState.IsKeyDown(Keys.S)) velpantera.Z -= 1.0f * deltaTime; else velpantera = Vector3.Zero; posicionpantera += Vector3.Transform(velpantera, Matrix.CreateFromQuaternion(panterarot)); panterarot = Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.ToRadians(cameraYaw)); cameraposition = Vector3.Transform(new Vector3(0,10,-10), Matrix.CreateFromQuaternion(panterarot)); cameratarget = Vector3.Transform(new Vector3(0, 10, 10), Matrix.CreateFromQuaternion(panterarot)); float mouseX = mState.X - mStateCashe.X; cameraYaw -= (mouseX * 5f) * deltaTime; lokatcamara = Matrix.CreateFromQuaternion(panterarot) * Matrix.CreateScale(1f, 1f, 1f) * Matrix.CreateTranslation(posicionpantera);

Bien, ac esta la lgica de nuestra cmara, en las primeras tres lneas asignamos el teclado, otro Mouse, y lo seteamos en el centro, esto da como resultado un Mouse centrado, fjense que si borran un Mouse.SetPosition, y mueven el Mouse, este no para nunca de rotar. En deltatime le asignamos el tiempo de juego. Luego viene una sentencia if, que la usamos para aumentar la velocidad de nuestro modelo, y as moverlo. Las siguientes variables que usamos son:
panterarot = Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.ToRadians(cameraYaw)) ;

Aqu arrancamos la variable Quaternion, lo que hacemos aqu es asignar a Quaternion todas las rotaciones, que, en este caso, solo tendr las rotaciones del eje X. Como primer parmetro recibe Vector3.Up, esto siempre es as, y lo siguiente son las rotaciones convertidas en radianes, y recibe como parmetro adentro el movimiento del Mouse.

Curso de Graduacin

Pgina 25

PROGRAMACIN GRAFICA EN 3D
posicionpantera += Vector3.Transform(velpantera, Matrix.CreateFromQuaternion(panterarot)); Aqu lo que hacemos es calcular que la posicin de nuestro modelo, ser igual dependiendo a la rotacin de nuestro modelo, o sea, el modelo se mover hacia delante dependiendo de la rotacin de nuestro modelo y velocidad del mismo.
cameraposition = Vector3.Transform(new Vector3(0,10,-10), Matrix.CreateFromQuaternion(panterarot)); cameratarget = Vector3.Transform(new Vector3(0, 10, 10), Matrix.CreateFromQuaternion(panterarot));

Aqu calculamos la posicin de nuestro modelo y hacia que punta esta mirando, segn mi experimentos, el punto Y del de cameratarget debe ser igual a punto Y de camera position, y el punto Z debe ser el opuesto al del punto Z de cameraposition. Bien, con esto ya hecho, ya solo nos falta implementarlo en nuestro View, que debe ser as:
Matrix.CreateLookAt(posicionpantera+ cameraposition, posicionpantera + cameratarget, Vector3.Up);

Eso es todo, y as la cmara andar de diez. Espero que les all servido, les dejo el link de cdigo de muestra:
El ejemplo de manejo de cmaras esta en CamaraTerceraPersonaXNA

Creacin del Proyecto Bsico

Paso1: Arrancar el visual studio Paso2: Crear el proyecto XNA para videojuegos(PC). Establecindole las propiedades que se deseen como el nombre del juego Paso3: Probar que ya tenemos configurado el entorno bsico.

XNA utiliza un Pipeline de proceso para el 3D como se puede ver en

Curso de Graduacin

Pgina 26

PROGRAMACIN GRAFICA EN 3D

Segn este, los puntos correspondientes a los modelos creados se colocan en un sistema de coordenadas cartesiano en 3D con el origen en (0,0,0). Posteriormente se le aplican las transformaciones propias al modelo (Traslaciones, rotaciones y escalados). El resultado de este paso se le aplican las transformaciones para adecuarlos al punto de vista que se esta usan y final mente se realiza la proyeccin de estos objetos para que queden como los vamos a ver en la pantalla. Como se puede observar este Pipeline es muy similar al de OpenGL. El motivo es que utiliza el mismo Pipeline pero ocultando determinadas cuestiones para configurar un entorno de ms alto nivel, que facilita la programacin.

Aadir los modelos 3D que vamos a usar durante este tutoral


Aadirlos modelos a la carpeta Content de la solucin. Esto se consigue sin ms que arrastrar la carpeta Models la carpeta Content de la solucin. Seleccionar todos los archivos con una extensin diferente a fbx. Clic en el botn derecho del ratn y excluirlos de la solucin Este ltimo paso no borra los ficheros, slo hace que el visual studio no los tenga en cuenta.

Pintando el terreno
En el siguiente paso en el tutorial ser establecer el terreno de nuestro mundo virtual. Para ello el primero de los pasos es definir el punto de vista desde el que visualizaremos la escena (debajo de la variable SpriteBatch) Vector3 cameraPosition = new Vector3(0.0f, 60.0f, 160.0f); // colocamos la cmara Vector3 cameraLookAt = new Vector3(0.0f, 50.0f, 0.0f); // seleccionamos a que punto
Curso de Graduacin Pgina 27

PROGRAMACIN GRAFICA EN 3D
esta mirando. Matrix cameraProjectionMatrix; // Determina el volument de proyeccin ( tipo, tamao, ...) Matrix cameraViewMatrix; // determina el tipo de visin de la cmara Adems, Tendremos que cargas la cmara en el LoadContent con los siguientes parmetros: cameraViewMatrix = Matrix.CreateLookAt( cameraPosition, cameraLookAt, Vector3.Up); cameraProjectionMatrix = Matrix.CreatePerspectiveFieldOfView( MathHelper.ToRadians(45.0f), graphics.GraphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f); La primera de las matrices va a determinar que esta viendo la cmara y el aspecto final de la escena. La segunda es el paso que determinar el aspecto final en pantalla tras la proyeccin con un volumen de proyeccin. Ahora definiremos la variable que contendr el modelo del terreno que vamos a pintar. Para ello se debe definir las siguientes lneas para las variables del terreno : Model terrainModel; Vector3 terrainPosition = Vector3.Zero; A continuacin es necesario cargar dicho modelo en el mtodo LoadContent. Lo que se efectuar no es otra cosa que cargar modelos desarrollados previamente en un entorno de desarrollo en 3D para ser usados. En concreto en este primer modelo se va a cargar el contenido que simula un conjunto de montaas. terrainModel = Content.Load<Model>("Models\\terrain"); As se nota como con una sola instruccin se realizar la carga de un modelo guardado en la variable para poder usarlo posteriormente. La nica limitacin es que dicho modelo debe estar referenciado dentro de los contenidos de la solucin. Ahora pasaremos a pintar este elemento para ello definimos un nuevo mtodo despus del mtodo Draw. El mtodo que vamos a realizar pinta los modelos y establece las propiedades de cada uno de los efectos bsicos de un Modelo.
Curso de Graduacin Pgina 28

PROGRAMACIN GRAFICA EN 3D
void DrawModel(Model model, Vector3 modelPosition) { foreach (ModelMesh mesh in model.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.EnableDefaultLighting(); effect.PreferPerPixelLighting = true; effect.World = Matrix.CreateTranslation(modelPosition); effect.Projection = cameraProjectionMatrix; effect.View = cameraViewMatrix; } mesh.Draw(); } } Los modelos en 3D estn compuestos de una o ms mayas que representan las partes del modelo. Estos a su vez estn compuestos de los distintos efectos bsicos. Por ejemplo, un modelo bsico de tanque estara compuesto de dos mayas. La primera representara el cuerpo y la segunda la torreta que se mueve por separado. A su vez estas estaran compuestos de distintos efectos bsicos para darle forma, color y textura.

Sobre cada elemento se pueden ejecutar distintos tipos de transformaciones, sombreados y cambios segn la cmara. Pero para este ejemplo nos limitaremos a utilizar el por defecto que ofrece XNA Framework. As ya tendremos todas las mayas y datos del modelo listos para pintarlas en nuestro recin creado mundo. Finalmente, slo queda pintarlo mediante el mtodo que hemos creado anteriormente. Para ellos aadimos la siguiente lnea en el mtodo Draw. DrawModel(terrainModel, terrainPosition); Prueba a ver como va debera aparecer algo similar a esta figura
Curso de Graduacin Pgina 29

PROGRAMACIN GRAFICA EN 3D

Creando el primer GameObject


Como deberas saber XNA es un framework que trabaja con el lenguaje C#. Este lenguaje es orientado a objetos y, en contraposicin del hecho en el tutorial de OpenGL, se va a utilizar esta caracterstica para simplificar el desarrollo. Para ello, en este punto vamos a crear una clase que va a guardar todas las propiedades necesarias para representar cualquier modelo. Lo primero es que aadiremos una nueva clase a nuestra solucin. Le llamamos a esta clase GameObject y al abrir la clase copiamos las referencias del programa principal (los using para tener los objetos de XNA). Esta clase contendr todos los atributos necesarios para representar un objeto de nuestro juego, como por ejemplo las naves, misiles, terreno, etc. En el nuevo fichero con las referencias copiadas vamos a definir 4 variables ( model, scale, rotation, position) y los inicializamos a los valores correspondientes como se muestra en el cdigo siguiente:

namespace <NOMBRE DEL JUEGO> { class GameObject { public Model model = null; public Vector3 position = Vector3.Zero; public Vector3 rotation = Vector3.Zero; public float scale = 1.0f; } }
Curso de Graduacin Pgina 30

PROGRAMACIN GRAFICA EN 3D
Vale vamos entonces a hacer unos cambios para representar el terreno con esta nueva clase. Lo primero sustituimos la declaracin de variables del terreno por GameObject terrain = new GameObject(); y en el LoadContent cambiamos la carga por terrain.model = Content.Load<Model>( "Models\\terrain"); De la misma forma define un nuevo objeto "missileLauncherBase" que va a ser la base de la torreta de misiles y carga el contenido en "LoadContent". Esta vez pon la escala de la torreta a 0.2f . missileLauncherBase.model = Content.Load<Model>( "Models\\launcher_base"); missileLauncherBase.scale = 0.2f; Ahora y para tratar con el nuevo objeto sustituimos el mtodo DrawModel por void DrawGameObject(GameObject gameobject) { foreach (ModelMesh mesh in gameobject.model.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.EnableDefaultLighting(); effect.PreferPerPixelLighting = true; effect.World = Matrix.CreateFromYawPitchRoll( gameobject.rotation.Y, gameobject.rotation.X, gameobject.rotation.Z) * Matrix.CreateScale(gameobject.scale) * Matrix.CreateTranslation(gameobject.position); effect.Projection = cameraProjectionMatrix; effect.View = cameraViewMatrix; } mesh.Draw();
Curso de Graduacin Pgina 31

PROGRAMACIN GRAFICA EN 3D
} } Como se puede ver utiliza los atributos guardados en el objeto. Adems, se crea la matriz de transformacin mediante la multiplicacin de los atributos y las utilidades que nos da XNA para este fin -- recordar que el orden es importante (Rotacin* Scalado*Traslacin). Comentar que en este caso le vamos a pasar la rotacin en grados en lugar de radianes ya que, la utilidad del framework nos convierte directamente los ngulos a su correspondiente seno y coseno. Adems combina la rotacin en las 3 direcciones para dar una nica matriz. Ya slo falta cambiar en el mtodo Draw el pintado. Borra la lnea para pintar el terreno que hemos escrito previamente y en su lugar usa la nueva funcin con los dos objetos (terrain y missileLaucherBase). Bueno toca probar a ver cmo est quedando.

Aadiendo el control
Bueno hasta el momento hemos creado el terreno y puesto la base para nuestra torreta de misiles. En esta seccin vamos a aadir la cabeza y los controles mediante el teclado. Para aadir la cabeza del Lanzamisiles crea un nuevo GameObject igual que el creado para la base ( en la parte de las variables). Recuerda cargar su modelo en LoadContent. Adems, aade el siguiente cdigo para colocar la cabeza en su sitio. missleLaucherHead.position = missileLaucherBase.position + new Vector3(0.0f,20.0f,0.0f); Llegado a este punto es el momento de aadir el siguiente cdigo en la funcin Update,
Curso de Graduacin Pgina 32

PROGRAMACIN GRAFICA EN 3D
que har la lectura del teclado cada vez que se realice el pintado. Cada pulsacin aadir medio radian de giro en cada eje. KeyboardState keyboardState = Keyboard.GetState(); if(keyboardState.IsKeyDown(Keys.Left)) { missileLauncherHead.rotation.Y += 0.05f; } if(keyboardState.IsKeyDown(Keys.Right)) { missileLauncherHead.rotation.Y -= 0.05f; } if(keyboardState.IsKeyDown(Keys.Up)) { missileLauncherHead.rotation.X += 0.05f; } if(keyboardState.IsKeyDown(Keys.Down)) { missileLauncherHead.rotation.X -= 0.05f; } Adems aade el siguiente cdigo para limitar el giro de la cabeza al rea de juego. missileLauncherHead.rotation.Y = MathHelper.Clamp( missileLauncherHead.rotation.Y, -MathHelper.PiOver4, MathHelper.PiOver4); missileLauncherHead.rotation.X = MathHelper.Clamp( missileLauncherHead.rotation.X, 0, MathHelper.PiOver4); la funcin Clamp limita el valor que puede tomar una variable a los dos valores extremos que se marcan en este caso Pi/4 y -Pi/4 Solo queda aadir la lnea para pintar en Draw y probar que todo va bien.

Disparando los misiles


Lo primero que vamos a hacer es modificar el GameObject y le vamos a aadir lo siguiente: public Vector3 velocity = Vector3.Zero; public bool alive = false;
Curso de Graduacin Pgina 33

PROGRAMACIN GRAFICA EN 3D
Estas lneas son para representar los objetos que pueden cambiar de posicin. La primera representa la velocidad del objeto y la segunda si se muestra en pantalla. Una vez hecho esto vamos a crear un arrary de misiles en el archivo de Juego principal. Aade un contador y pon el nmero mximo a 20 con las siguientes lneas const int numMissiles = 20; GameObject[] missiles; Una vez hecho esto hay que cargar el contenido de cada uno de los misiles en LoadContent ponindoles una escala de 3. KeyboardState previousKeyboardState; Ahora habr que aadir ms controles para disparar. En concreto vamos a definir una variable KeyboardState para almacenar el estado previo. El almacenar el estado anterior es para saber si se ha disparado ms de un misil. El siguiente paso es cambiar la funcin Update para aadir el control de disparo. Para ello aadimos el siguiente cdigo al final de Update if(keyboardState.IsKeyDown(Keys.Space) && previousKeyboardState.IsKeyUp(Keys.Space)) { FireMissile(); } previousKeyboardState = keyboardState; Como ves llamada falta por escribir la funcin Firemissile(), el cdigo de este es el siguiente void FireMissile() { foreach (GameObject missile in missiles) { if (!missile.alive) { missile.velocity = GetMissileMuzzleVelocity(); missile.position = GetMissileMuzzlePosition(); missile.rotation = missileLauncherHead.rotation; missile.alive = true; break; }
Curso de Graduacin Pgina 34

PROGRAMACIN GRAFICA EN 3D
} } Adelo al fichero principal del Juego. Adems, como se puede ver hay que definir dos nuevas funciones. Para ello debemos aadir dos nuevas constantes: const float launcherHeadMuzzleOffset = 20.0f; const float missilePower = 20.0f; Estas constantes indican donde posicionar el misil disparado al comienzo y la velocidad del mismo. Ahora ya se puede definir los mtodos como sigue: Vector3 GetMissileMuzzleVelocity() { Matrix rotationMatrix = Matrix.CreateFromYawPitchRoll( missileLauncherHead.rotation.Y, missileLauncherHead.rotation.X, 0); return Vector3.Normalize( Vector3.Transform(Vector3.Forward, rotationMatrix)) * missilePower; } Vector3 GetMissileMuzzlePosition() { return missileLauncherHead.position + (Vector3.Normalize( GetMissileMuzzleVelocity()) * launcherHeadMuzzleOffset); } En la primera de las funciones se puede ver como se utilizan 3 operaciones sobre matrices de manera consecutiva. Vector3.Forward() nos devuelve una matriz Identidad que contiene la direccin haca adelante en el mundo 3D. Esta matriz se utiliza en combinacin con Transform para adaptar la rotacin de esta para que coincida con una rotacin pero siempre hacia adelante. Finalmente se normaliza el vector resultante ( valores entre 0 y 1) ya que muchas veces el resultado de las transformaciones puede ser un vector cuyo modulo es mayor que 1. Finalmente en la funcin se multiplica por la velocidad del misil para obtener un vector con ese valor de modulo. La segunda de las funciones tiene como objeto calcular el punto donde aparece el misil un poco alejado de la cabeza lanzadora.
Curso de Graduacin Pgina 35

PROGRAMACIN GRAFICA EN 3D
Con estos nuevos mtodos ya podemos calcular la velocidad de los misiles en la pantalla y que se realice en pintado un poco por delante de la torreta con la orientacin correcta. Vale nos queda actualizar la posicin de esos misiles para ello aadimos la siguiente funcin: void UpdateMissiles() { foreach (GameObject missile in missiles) { if (missile.alive) { missile.position += missile.velocity; if (missile.position.Z < -6000.0f) { missile.alive = false; } } } } y la llamamos desde el mtodo Update. Ya slo nos queda pintar los missiles. Para ello en el mtodo Draw aadimos un bucle que recorra los missiles y pinte los objetos con el mtodo DrawObject. foreach (GameObject missile in missiles) { if (missile.alive) { DrawGameObject(missile); } } Listo prueba a lanzar unos cuantos misiles

Necesitamos algo a lo que disparar


Lo primero que vamos a hacer es posicionar algunas naves enemigas para tener algn objetivo para nuestros misiles recin creados. Para ello definimos las siguientes variables:

Curso de Graduacin

Pgina 36

PROGRAMACIN GRAFICA EN 3D
Random r = new Random(); // se utilizar para poner los enemigos en posiciones aleatorias const int numEnemyShips = 3; // numero mximo de enemigos simultneos GameObject[] enemyShips; Vector3 shipMinPosition = new Vector3(-2000.0f, 300.0f, -6000.0f); Vector3 shipMaxPosition = new Vector3(2000.0f, 800.0f, -4000.0f); const float shipMinVelocity = 5.0f; const float shipMaxVelocity = 10.0f; Ahora cargamos el contenido en LoadContent y a cada nave enemiga le ponemos una escala de 0.1 y una rotacin (0,Pi,0) para que mire hacia la torreta. En el mtodo Update llamamos a un nuevo mtodo UpdateEnemyShip() que definiremos a continuacin y que actualizar las naves enemigas. Con el siguiente cdigo lo conseguiremos void UpdateEnemyShips() { foreach (GameObject ship in enemyShips) { if (ship.alive) { ship.position += ship.velocity; if (ship.position.Z > 500.0f) { ship.alive = false; } } else { ship.alive = true; ship.position = new Vector3( MathHelper.Lerp( shipMinPosition.X, shipMaxPosition.X, (float)r.NextDouble()), MathHelper.Lerp( shipMinPosition.Y, shipMaxPosition.Y, (float)r.NextDouble()), MathHelper.Lerp( shipMinPosition.Z, shipMaxPosition.Z, (float)r.NextDouble()));

Curso de Graduacin

Pgina 37

PROGRAMACIN GRAFICA EN 3D
ship.velocity = new Vector3( 0.0f, 0.0f, MathHelper.Lerp(shipMinVelocity, shipMaxVelocity, (float)r.NextDouble())); } } } En esta funcin hemos utilizado a la funcin Lerp. Esta funcin asigna un valor entre un mximo y un mnimo, que sea proporcional a un valor entre 0 y 1 que se le pasa como tercer argumente. Ahora solo queda pintar las naves para ello hay que modificar el mtodo Draw. Haz un bucle que pinte las naves vivas. Listo vamos a probar.

OH!! No se mueren??
Eso es que no hemos programado el test de colisin entre los misiles y los platillos. Para ello introducimos una clusula else en el if, que comprueba que no se ha salido del mundo, del mtodo UpdateMissile(). Simplemente ponemos una clusula else y llamamos a un nuevo mtodo TestColision(missile). Para este test de colisiones vamos a utilizar la esfera que contienen las mayas de representacin ya que de esta manera nos permitir calcular las intersecciones entre dos para realizar el test. El cdigo de la funcin de colisiones queda como:

Curso de Graduacin

Pgina 38

PROGRAMACIN GRAFICA EN 3D
void TestCollision(GameObject missile) { BoundingSphere missilesphere = missile.model.Meshes[0].BoundingSphere; missilesphere.Center = missile.position; missilesphere.Radius *= missile.scale; foreach (GameObject ship in enemyShips) { if (ship.alive) { BoundingSphere shipsphere = ship.model.Meshes[0].BoundingSphere; shipsphere.Center = ship.position; shipsphere.Radius *= ship.scale; if (shipsphere.Intersects(missilesphere)) { missile.alive = false; ship.alive = false; break; } } } }

[XNA] Deteccin de colisiones en un mundo 3D (Episode I)


Deteccin de colisiones en videojuegos 3D. Esta servir ms tarde para mover objetos por este mundo 3D sin que se salgan de los lmites, o se atraviesen los unos a los otros por ejemplo. As pues la deteccin de colisiones consiste bsicamente en comprobar si nuestros objetos interseccionan en el espacio, pues bien, la deteccin de colisiones en un espacio tridimensional, se basa en XNA en los objetos BoundingBox y BoundingSphere, que no son ms que cuerpos geomtricos que no se renderizan pero que utilizaremos para realizar el clculo de las colisiones. Tened en cuenta que si hiciramos la comprobacin de colisiones polgono a polgono necesitaramos una CPU que todava hoy no se ha fabricado para poder hacer los clculos de forma rpida.

Curso de Graduacin

Pgina 39

PROGRAMACIN GRAFICA EN 3D

Como una imagen vale ms que mil palabras, aqu tenemos un par de ejemplos de boundingboxes, como veis, no son ms que prismas que envuelven unos modelos 3D. En un juego obviamente estos prismas no se renderizan, pero puede ser til hacerlo para debugar nuestro juego. Un boundingShpere sera exactamente lo mismo, pero obviamente tendramos una esfera en lugar de un prisma. Recordar aqu que siempre va a ser mucho ms ptimo utilizar BoundingSpheres que no BoundingBoxes. Porqu? porque las esferas son fciles de transformar y es muy fcil de comprobar si interseccionan con otras esferas. Al fin y al cabo, matemticamente estamos hablando de un centro en un vector 3D y un radio as pues las operaciones con esferas son muy rpidas. Claro est que las cajas van a ser necesarias a veces pero es recomendable usarlas solo para objetos del escenario, como es nuestro caso en estos momentos. Tanto la clase BoundingBox como la BoundingSphere tienen como mtodo ms importante el Intersects. Como no quiero quitarle el pan al MSDN, la explicacin detallada de estas clases y sus mtodos podis consultarla all. Lo que si har es destacar que el intersects nos permitir comprobar si el objeto bounding intersecciona con mltiples objetos muy interesantes:

otros boundingbox otros boundingsphere rayos frustrums planos

Bsicamente aqu lo utilizar para detectar colisiones con otros objetos de tipo bounding, pero tened presentes las posibilidades. En artculos anteriores he hablado tanto de frustrums como de rayos, podis consultarlos si tenis ganas :-) En este caso concretamente desarrollar una clase que permitir aplicar BoundingBoxes y BoundingSpheres de forma masiva sobre un mundo 3D. El hecho es que si tenemos un mundo, o nivel del juego, sobre el cual queremos hacer deteccin de colisiones, esto se traduce en que tenemos que crear un montn de BoundingBox y BoundingSpheres.
Curso de Graduacin Pgina 40

PROGRAMACIN GRAFICA EN 3D
Cmo los creamos y mantenemos? Cmo los instanciamos en el cdigo? A ver, lo ideal, sera tener un editor visual de niveles, y hacerlo grficamente, y finalmente serializar el mundo en un XML, para cargarlo despus desde el juego, deserializando ese XML. Pues bien como no voy a currarme aqu y ahora un editor de niveles espero que os conformaris con la serializacin y deserializacin de / a XML. Esto es algo sencillsimo para cualquier programador de .Net, pero a ser posible hay que hacerlo con un poco de gracia. Lo que he planteado es lo siguiente:

El caso es que tendremos una clase que he llamado DatosBounding que ser deserializada desde un fichero XML, y con ella una lisa de objetos de tipo DatosBoundingBox y DatosBoundingSphere. Esta informacin posteriormente la podremos asignar a la clase que contenga la informacin del mundo o escenario 3D. Para serializar la clase a un XML tendramos el siguiente cdigo:
1: DatosBounding listaBoundings = new DatosBounding(); 2: 3: listaBoundings.listaBoundingSpheres.Add(new DatosBoundingSphere(new Vector3(1.0f,2.5f,3.2322f), 5.4f)); 4: listaBoundings.listaBoundingSpheres.Add(new DatosBoundingSphere(new Vector3(4.0f, 4.5f, 2.2322f), 22.4f)); 5: listaBoundings.listaBoundingBoxes.Add(new DatosBoundingBox(new Vector3(1,2,3), new Vector3(5,6,7))); 6: 7: XmlSerializer ser = new XmlSerializer(typeof(DatosBounding)); 8: TextWriter escritor = new StreamWriter(@"C:\test.xml"); 9: ser.Serialize(escritor, listaBoundings); 10: 11: escritor.Close();

Lo cual, una vez ejecutado, generar el siguiente fichero XML:


Curso de Graduacin Pgina 41

PROGRAMACIN GRAFICA EN 3D
<?xml version="1.0" encoding="utf-8"?> <DatosBounding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <listaBoundingSpheres> <DatosBoundingSphere> <Radio>0</Radio> <posicin> <X>1</X> <Y>2.5</Y> <Z>3.2322</Z> </posicin> </DatosBoundingSphere> <DatosBoundingSphere> <Radio>0</Radio> <posicin> <X>4</X> <Y>4.5</Y> <Z>2.2322</Z> </posicin> </DatosBoundingSphere> </listaBoundingSpheres> <listaBoundingBoxes> <DatosBoundingBox> <PosicionMin> <X>1</X> <Y>2</Y> <Z>3</Z> </PosicionMin> <PosicionMax> <X>5</X> <Y>6</Y> <Z>7</Z> </PosicionMax> </DatosBoundingBox> </listaBoundingBoxes> </DatosBounding>

Posteriormente,

desde

nuestro

juego,

necesitaremos

cargar

esta

informacin

(supuestamente en un juego el XML sera muchsimo mayor, la cual cosa compensara la serializacin y deserializacin). El proceso de carga de un XML se llama deserializacin, y el cdigo sera este:
1: 2: 3: 4: 5: DatosBounding listaBoundings2 = new DatosBounding(); TextReader lector = new StreamReader(@"C:\test.xml"); listaBoundings2 = (DatosBounding)ser.Deserialize(lector); lector.Close();

Observad que leo/almaceno los datos en la raz de la C:, chicos, no hagis esto en casa :-) Lo importante aqu es que despus de llamar al mtodo Deserialize ya tendramos una lista de Boundings (listaBoundings2) cargada con los datos del XML! En el siguiente episodio ms! Hasta entonces, si queris podis apalearme por escribir tanto :-P

Deteccin de colisiones en un mundo 3D (Episode II)


Aplicando el concepto a un posible nivel de un videojuego. En este episodio seguir desarrollado este "nivel" del videojuego, en concreto disear una pantalla muy simple,
Curso de Graduacin Pgina 42

PROGRAMACIN GRAFICA EN 3D
pero lo ms importante: intentar simplificar el trabajo que supondra aadir ms niveles a un juego y gestionar su informacin. As pues esta parte del tutorial podra considerarse prcticamente ms afn a las estructuras de datos que no a la deteccin de colisiones (tema al que volveremos en el episodio III). Lo primero va a ser disponer de 1 o N modelos que conformen nuestro mundo. En este momento pondr un modelo que se compondr de distintos objetos, no obstante, lo ideal es que existan N objetos formando nuestro "mundo", y que renderizemos slo aquellos que son visibles por el jugador (los que estn en el frustum de la cmara). Pero por ahora no nos compliquemos la vida... un mundo con un solo modelo. En mi caso lo he hecho en MAX, pero hay un montn de aplicaciones de modelado 3D gratuitas, posteriormente habr que exportar el modelo a FBX. Como podis comprobar ni siquiera aplico texturas al modelo en este caso se trata de una simple prueba de concepto que iremos perfeccionando.

Al final el resultado ser este (otra vez estoy utilizando el robot modelado por Jordi Gimenez):

Curso de Graduacin

Pgina 43

PROGRAMACIN GRAFICA EN 3D

As pues vayamos a ver qu aproximacin he utilizado para implementar un gestor de niveles he creado una clase ManagerNiveles, que gestiona y contiene un almacn de datos al que le he llamado Nivel. Nivel podra contener cualquier nivel de nuestro videojuego, mientras la definicin del mismo exista en el fichero XML correspondiente (nivelN.xml), y es que basndome en lo anterior, ManagerNiveles carga toda la informacin desde un fichero externo para hacer ms fcil de gestionar N niveles en un videojuego. Este XML contiene informacin de los boundingbox y boundingspheres que se utilizarn en la siguiente parte del tutorial.

Esta clase ManagerNiveles es muy interesante porque encapsula la informacin del nivel y el mtodo necesario para pintarlo, as como la referencia a todos sus modelos y sus objetos bounding. As pues, en la clase Game, tendremos un cdigo muy simple,
Curso de Graduacin Pgina 44

PROGRAMACIN GRAFICA EN 3D
comenzando obviamente por una instancia a la clase:
1: ManagerNiveles managerNivel;

Despus slo tendremos que hacer un new de la instancia y inicializar el nivel que queramos, en nuestro caso el 1:
1: // Inicializo el nivel 2: managerNivel.IniciarNivel(1);

Y para renderizar el nivel, el mtodo Draw de la clase game har simplemente lo siguiente:
1: // Pintamos el nivel 2: managerNivel.Draw(gameTime, camara.Vista, camara.Proyeccion);

Sencillo, verdad? Ahora veamos el mtodo IniciarNivel de la clase ManagerNiveles.


1: // Deserializamos los datos desde el XML y establecemos los mismos en la clase colisionesMundo 2: nivel = new Nivel(); 3: 4: XmlSerializer ser = new XmlSerializer(typeof(Nivel)); 5: 6: TextReader lector = new StreamReader( 7: string.Format(@"{0}\Content\Niveles\Nivel{1}.xml", Environment.CurrentDirectory, numeroNivel)); 8: nivel = (Nivel)ser.Deserialize(lector); 9: lector.Close(); 10: 11: // Inicializamos los objetos del mundo 12: foreach (DatosModelo objeto in nivel.objetosMundo) 13: { 14: nivel.modelosMundo.Add(new ObjetoInerte(objeto.nombreModelo, content)); 15: nivel.modelosMundo[nivel.modelosMundo.Count - 1].transformacionRaiz.Escala *= Matrix.CreateScale(objeto.Escala); 16: }

Ese lo que hace es deserializar la informacin del XML en un objeto de tipo Nivel. Posteriormente carga en memoria todos los modelos que contiene el nivel. El dibujado, al que hemos llamado antes desde la clase game, simplemente recorre los modelos y los pinta uno a uno (aqu es donde tendremos que utilizar ms tarde un frustum culling para pintar slo los modelos que se encuentran dentro del frustum de la cmara):
1: public void Draw(GameTime gameTime, Matrix vista, Matrix proyeccion) 2: { 3: foreach (ObjetoInerte modelo in nivel.modelosMundo) 4: { 5: modelo.Draw(gameTime, vista, proyeccin); 6: } 7: }

Deteccin de colisiones en un mundo 3D (Episode III)


Curso de Graduacin Pgina 45

PROGRAMACIN GRAFICA EN 3D
Por fin aplicaremos deteccin de colisiones al nivel 3D que hemos generado para "el videojuego". Tenemos un XML en el que se definen los objetos BoundingBox y BoundingSphere que se usarn en el nivel para la deteccin de colisiones contra el mismo, tenemos tambin un mundo que se carga a partir de este XML y tenemos adems un robot que se mueve con animaciones propias por este mundo tridimensional. Qu nos falta ahora? Es bien sencillo detectar las colisiones entre el robot y el mundo! Es decir, hacer que el robot no pueda traspasar las paredes como lo haca en el vdeo anterior. Lo que vamos a conseguir es lo siguiente:

Superados los pasos anteriores, aadir esta funcionalidad no va a ser demasiado difcil lo primero que har es asegurarme de que se rendericen los boundingbox y boundingsphere cuando lo necesite. En un videojuebgo en produccin obviamente estos objetos no se renderizan, pero durante el desarrollo puede ser muy til, con objeto de debugacin. Y adems, en nuestro caso tenemos que definir la posicin de los boundings en un XML sin un editor de niveles visual as que nos ser muy til poder ver donde queda cada bounding, as ser ms fcil editar dicho XML. As pues el cdigo sera el siguiente:
1: public void Draw(GameTime gameTime, Matrix vista, Matrix proyeccion) 2: { 3: // Pintar modelos 4: foreach (ObjetoInerte modelo in nivel.modelosMundo) 5: { 6: modelo.Draw(gameTime, vista, proyeccion);

Curso de Graduacin

Pgina 46

PROGRAMACIN GRAFICA EN 3D
7: } 8: 9: if (Configuracion.Debug) 10: { 11: // Pintar cajas 12: BoundingBoxRender renderCaja = new BoundingBoxRender(); 13: 14: foreach (BoundingBox caja in nivel.boundingBoxes) 15: { 16: renderCaja.Render(caja, Configuracion.graficos, vista, proyeccion, Color.Orange); 17: } 18: 19: // Pintar esferas 20: BoundingSphereRender renderEsfera = new BoundingSphereRender(); 21: 22: foreach (BoundingSphere esfera in nivel.boundingSpheres) 23: { 24: renderEsfera.Render(new BoundingSphere(esfera.Center, esfera.Radius), Configuracion.graficos, vista, proyeccion, Color.Orange); 25: 26: 27: } } }

Algo extrao? Supongo que habris observado la existencia de una instancia de una clase que se llama BoundingBoxRender y otra BoundingSphereRender. Esta es una clase que permite renderizar BoundingBox y BoundingSpheres respectivamente (con ese nombre ndie lo dira eh? :-P). Estas son clases sencillas pero extremadamente tiles, que supongo que baj algn da de Ziggyware (snif, snif, esa web ha muerto). Pues bien, esta pequea clase nos ayudar a debugar nuestros juegos. Ahora que tenemos los boundings cargados y pintndose hay que comprobar si el robot colisiona contra ellos. Para ello aadir algunas lneas al mtodo que ya exista en el ejemplo anterior: LeerInput.
1: Vector3 posicionInicial; 2: 3: posicionInicial = base.transformacionRaiz.ObtenerMundo().Translation;

Bsicamente aqu guardo la posicin del robot antes de aplicarle todas las transformaciones. El mtodo Matrix.Translation permite obtener la posicin de la misma en un vector 3D. Aplicamos las transformaciones pertinentes a las matrices, tal y como hacamos en el ejemplo anterior, todava dentro de LeerInput, y comprobaremos si tras las mismas existe colisin entre el robot y el mundo. Si eso ocurre, devolvemos al robot a su posicin inicial
Curso de Graduacin Pgina 47

PROGRAMACIN GRAFICA EN 3D
antes de leer el input de teclado. El resultado es que el robot ya no puede atravesar las paredes!
1: if (nivel.HayColision(this.esfera)) 2: { 3: base.transformacionRaiz.Traslacion.Translation = posicionInicial; 4: this.esfera.Center = posicin + desplazamiento; 5: }

El mtodo Hay Colision no hace magia simplemente recorre los boundings del nivel para ver si hay colisin contra el boundingsphere que tiene el propio robot.
1: public bool HayColision(BoundingSphere bounding) 2: { 3: foreach (BoundingSphere boundingSphereCheck in boundingSpheres) 4: { 5: if(boundingSphereCheck.Intersects(bounding)) 6: return true; 7: } 8: foreach(BoundingBox boundingBoxCheck in boundingBoxes) 9: { 10: if (boundingBoxCheck.Intersects(bounding)) 11: return true; 12: } 13: 14: return false; 15: }

Importante: En un juego real tendramos que limitar la comprobacin de colisiones de alguna manera. Existen mltiples formas, y en uno de mis artculos hablaba de una de ellas,
El Cdigo esta en el archivo Voxels

Deteccin de colisiones por descomposicin del espacio en una rejilla de vxeles


Imaginemos que nuestro juego tiene cientos de agentes desplazndose por el mundo virtual del mismo, y debemos comprobar si estn consiguiendo colisionar con el agente que representa el jugador... Cmo lo haramos? La opcin ms fcil es comprobar si el bounding de cada uno de estos enemigos colisiona con el bounding del jugador... pero esto podra ser costoso, refirindonos al rendimiento. Una forma ms ptima de hacerlo es descomponer el espacio, lo cual consiste en testear las colisiones entre objetos que se encuentren en la misma zona del espacio. De descomposiciones del espacio existen los siguientes tipos (hay variantes... pero estos
Curso de Graduacin Pgina 48

PROGRAMACIN GRAFICA EN 3D
son los principales):

Octrees Rejilla de vxeles

Ambas soluciones son buenas, pero hay que tener cuidado en su uso. En este artculo veremos especficamente la rejilla de vxeles.

Rejilla de Vxeles
La definicin pura y dura de voxel lo describe como un elemento volumtrico dentro de una rejilla imaginaria en el espacio tridimensional (la imagen de debajo es muy aclaradora en este sentido). Esto ya nos indica como vamos a relacionar esto con la deteccin de colisiones... nuestros objetos en el espacio 3D estarn virtualmente dentro de un voxel u otro, y slo procederemos a testear la colisin en el caso en el que dos objetos se encuentren en el mismo voxel. Parecido con el Quadtree? Bastante, la diferencia est en que aqu no hay voxels anidados...

Es mas, en la imagen previa, vemos una "montaa" de voxels, pero nosotros podramos considerar que slo tenemos un nivel de voxels, con lo cual en este caso la comprobacin de si dos objetos se encuentran en el mismo voxel resulta trivial. Ms que esa montaa de voxels, pasaramos a tener lo siguiente:
Curso de Graduacin Pgina 49

PROGRAMACIN GRAFICA EN 3D

Eso es "trampear" un poco con el concepto de Voxel, lo se, pero los programadores somos as :-P El resultado es que los puntos rojos son los objetos que estaran en el espacio tridimensional vistos desde arriba, y el punto verde sera el agente del jugador. Si dividimos la pantalla en estos "voxels", slo ser necesario testear una colisin! Como tantas otras veces, comentar que obviamente esta solucin no es exclusiva para XNA, el algoritmo nos servir para cualquier tecnologa.

Implementacin en XNA
Lo que a mi me gusta es que las cosas que hago sean fciles de debugar, de modo que si hay algn bug o problema, encontrar la solucin sea rpido. Como hemos dicho vamos a trabajar con una descomposicin del espacio, pues qu mejor que "pintar" esa descomposicin en pantalla. Esto lo haremos trabajando con primitivas (dibujaremos las lneas de esta descomposicin). Para hacer esto utilizaremos la siguiente clase Grid:

Curso de Graduacin

Pgina 50

PROGRAMACIN GRAFICA EN 3D

Esta clase ha sido creada a partir de una modificacin de un cdigo existente en la web del Creators Club. Con esto ya tendremos la "rejilla" pintada por pantalla. El siguiente paso interesante tambin para debugar, sera mostrar en la pantalla en qu parte de esta descomposicin se encuentra el agente u objeto que representa el jugador. Esto lo haremos con el siguiente mtodo, el "clculo" es muy sencillo y rpido como se ve: private Point GetNumCelda(Vector3 posicionObjeto) { Point numCelda; numCelda = new Point((int)posicionObjeto.X / anchoCelda, (int)posicionObjeto.Z / anchoCelda); return numCelda; }

Curso de Graduacin

Pgina 51

PROGRAMACIN GRAFICA EN 3D
Adems, pondremos un agente en la pantalla de forma que pueda moverlo el jugador, esto lo haremos con el cdigo que venimos utilizando en los artculos previos (no volver a escribirlo para no repetirme). El resultado de la ejecucin es el siguiente:

Nota: En este ejemplo la descomposicin se ha hecho en celdas muy pequeas, en un videojuego las celdas podran y deberan ser ms grandes, en proporcin con el tamao de nuestros agentes, la cantidad de objetos cuyas colisiones queramos testear, etc. Tengamos en cuenta, que para saber en qu celda se encuentra el agente, slo usamos la posicin de su "centro", no sus dimensiones. Esto quiere decir que nuestro objeto agente podra estar en medio de dos celdas y por lo tanto podramos considerar que se encuentra en dos celdas, y no en una, incluso en cuatro, si se encuentra en una esquina de una celda. Para no complicar la obtencin de la informacin de en qu celdas estamos, lo que haremos es comprobar la colisin con la celda "centro" y todas las adyacentes, de la forma que indica el dibujo siguiente:

Curso de Graduacin

Pgina 52

PROGRAMACIN GRAFICA EN 3D

Un momento, se nos olvida algo... contra qu vamos a comprobar las colisiones? Necesitamos uno o varios modelos, que a ser posible se muevan por la pantalla, para hacerlo. Haba pensado en el modelo siguiente:

Adems del modelo en formato DirectX (.X) necesitaremos almacenar este en algn sitio, una buena forma de hacerlo es en una lista genrica, en la clase Game: List<ExtendedModel> patos; Despus slo tenemos que inicializarlos, con posiciones distintas y direcciones algo aleatorias, esto lo haremos en el mtodo LoadContent() de la clase Game: patos = new List<ExtendedModel>(); Model modeloPato = Content.Load<Model>(@"Models\pato"); for (int a = 0; a <= 5; a++) for (int i = 0; i <= 5; i++)
Curso de Graduacin Pgina 53

PROGRAMACIN GRAFICA EN 3D
{ ExtendedModel m1 = new ExtendedModel(GraphicsDevice); m1.position = new Vector3(anchoCelda * i - 500 * a, 30.0f, i * 200.0f - 400 * a); m1.model = modeloPato; m1.scale = 300.0f; m1.altura = 40; m1.anchura = 40; m1.speed.X += (float)rnd.Next(10) / 10; m1.speed.Z += (float)rnd.Next(10) / 10; patos.Add(m1); } Y creo que aqu se hace necesario explicar mnimamente la clase ExtendedModel (que no es parte del Framework de XNA obviamente).

Bueno, no tiene ningn secreto, esta clase contiene principalmente un modelo de XNA del tipo Model, y lo que hace es dibujarlo por pantalla mediante el mtodo Draw. Adems le he puesto algunas propiedades como posicin, velocidad, escala, etc, que me son de ayuda para hacer ejemplos rpidos de XNA, no dira que sea un modelo de codificacin o diseo ptimo para un juego, as que ojo con su uso ;-)
Curso de Graduacin Pgina 54

PROGRAMACIN GRAFICA EN 3D
Ahora, slo nos queda darle un poco de lgica a nuestro "juego", en el mtodo Update() de la clase Game, pondremos el cdigo siguiente: // Boundingbox del agente agente.bounding = new BoundingSphere(new Vector3(agente.position.X, agente.position.Y, agente.position.Z), agente.anchura); // Miro donde est el agente Point numCelda = GetNumCelda(this.agente.position); // Lgica del pato foreach (ExtendedModel pato in patos) { pato.bounding = new BoundingSphere(new Vector3(pato.position.X, pato.position.Y * 2, pato.position.Z), pato.anchura); pato.rotation = pato.rotation + new Vector3(0.0f, 0.05f, 0.0f); pato.position += pato.speed; if (pato.position.Z < -600 || pato.position.Z > 600) pato.speed.Z *= -1; if (pato.position.X < -600 || pato.position.X > 600) pato.speed.X *= -1; pato.position.X = MathHelper.Clamp(pato.position.X, -600, 600); pato.position.Z = MathHelper.Clamp(pato.position.Z, -600, 600); // Inicialmente asumo que no hay colisin pato.boundingColor = Color.Purple; Point celdaPato = GetNumCelda(pato.position); // Miro si el pato est en la misma zona del espacio que el agente if (celdaPato == new Point(numCelda.X - 1, numCelda.Y - 1) || celdaPato == new Point(numCelda.X, numCelda.Y - 1) || celdaPato == new Point(numCelda.X + 1, numCelda.Y - 1) || celdaPato == new Point(numCelda.X - 1, numCelda.Y) || celdaPato == new Point(numCelda.X, numCelda.Y) || celdaPato == new Point(numCelda.X + 1, numCelda.Y) || celdaPato == new Point(numCelda.X - 1, numCelda.Y + 1) || celdaPato == new Point(numCelda.X, numCelda.Y + 1) || celdaPato == new Point(numCelda.X + 1, numCelda.Y + 1)) { // Lo est? pues perfecto, comprovamos la colisin if (pato.bounding.Intersects(agente.bounding)) pato.boundingColor = Color.Red; // En este caso, simplemente hacemos que el boundingSphere del pato se pinte en rojo }
Curso de Graduacin Pgina 55

PROGRAMACIN GRAFICA EN 3D
Una de las cosas que me preocupa en esta implementacin es ese If, bastante "gordo", y que adems se ejecuta un montn de veces por segundo. Para que esta implementacin le salga "rentable" a nuestro procesador, el "mundo 3D" cuyas colisiones queremos comprobar tiene que ser bastante grande y contener bastantes elementos a comprobar.
El Cdigo esta en el archivo Voxels

Curso de Graduacin

Pgina 56

Das könnte Ihnen auch gefallen