Meteorit 011

Aclaracion importante sobre metodos abstract/virtual.
Usando la clase Proyectil me he dado cuenta de la diferencia entre metodos abstract y virtual.
Aqui dos formas identicas de declarar Metodo():
public abstract void Metodo(); <- Asi se escribiria si es abstracto
public virtual void Metodo() {} <- Asi se escribiria si es virtual
Por tanto abstract solo es un prototipo de la funcion, no admite implementacion {}. Pero equivale a un metodo virtual con implementacion vacia.

Aclaracion importante para el uso de sonidos.
No tuve ni que mirar en google (mas que nada xk no tenia internet), era facilisimo e intuitivo.
1 – Añadir el sonido en si (yo he usado el laser1.wav de alberto) al content.
2 – Declarar y cargar el efecto de sonido en el gestor de contenidos. SoundEffect laser = GestorContenidos.contenido.Load(«laser 1»);
3 – Reproducirlo cuando se quiera, por ejemplo en el metodo que dispara balas. void dispararBalas(){ laser.Play(); }
Esto es un SoundEffect, no es una cancion. Tambien esta la clase SoundEffectInstance, Song, SongCollection y no se si hay mas relacionadas con el audio… pero para efectos lo mas seguro es que se necesite esta, SoundEffect.

Meteorit 010

Declaracion de la plantilla de las balas (todas tienen los mismos apartados pero diferente comportamiento)

Atributos:
– Sprite bala; Sprite de la bala (habra mas si hay mas balas)
– Rectangle colision; Rectangulo de colision de la bala (habra mas si hay mas balas)

Constructores:
– Bala() Donde TIPO = {Simple, 2Diagonal, 2Trasera, 2EjeY} por ahora. Este constructor carga la imagen de la bala en su sprite y asigna las posiciones iniciales de la bala y de su rectangulo de colision.

Metodos:
– Update(int velocidad) Mueve n = velocidad pixels la bala
– Draw() Dibuja la bala en pantalla

Comportamientos:
Lo unico que variara en los distintos tipos de balas seran las posiciones iniciales y el comportamiento en update.
Simple -> Se carga en el extremo derecho de la nave, justo al medio de su altura. Su comportamiento sera rectilineo hacia la derecha.
2Diagonal -> 2 Balas que se cargan en el mismo sitio que la simple. Su comportamiento sera en diagonal 45º hacia delante, una hacia arriba y otra hacia abajo.
2Trasera -> 2 Balas que se cargan en la parte trasera de la nave, a 1/3 y 2/3 de la altura de la nave respectivamente. Su comportamiento es como la simple pero hacia la izquierda.
2EjeY -> 2 Balas que se cargan en el medio de la nave, en los extremos de arriba y abajo. Su comportamiento es rectilineo sobre el eje Y, una arriba y otra abajo.

Para usar la clase:
Lo logico es que no se quiera solo instanciar 1 bala, por eso explicare como instanciar un conjunto de balas.
Usaremos una lista de balas, mas concretamente de proyectiles. List balas = new List();
Para una prueba de todos los tipos de balas crearemos varios metodos que carguen varios tipos de balas, aqui un ejemplo de balasimple. void dispararBala(){ BalaSimple bala = new BalaSimple(); balas.Add(bala); }
El metodo lanzador de balas deberia estar ya hecho, sera un boton al ser pulsado, el cual llamara a dispararBala() y eso creara la instancia y la añadira a la lista.
Una vez añadidas las balas creamos la logica de movimiento en Update, en este ejemplo se mueven 8 pixels cada 1 segundo. if(gameTime.TotalGameTime.Seconds % 1 == 0) { for(int i = 0; i < balas.Count; i++) { if(balas[i].estoyVivo) balas[i].Update(8); else balas.Remove(balas[i]); } } Esto actualizaria la posicion de la bala si esta viva (sigue en la pantalla), en caso contrario se elimina de la lista y posteriormente el garbage collector de c# eliminaria la instancia de la bala ya que no se ha referenciado otra vez.
En el metodo Draw simplemente haremos un foreach de la lista para dibujar todas las balas. foreach(Proyectil bala in balas) bala.Draw();

Meteorit 009

Declaracion de la clase Proyectil

Atributos:
– bool estoyVivo; Cuando nace esta a true, cuando se sale de la pantalla esta a false y se elimina de la lista de balas
– const int RESOLUCION_HORIZONTAL = 800; Ancho de la pantalla del dispositivo
– const int RESOLUCION_VERTICAL = 480; Alto de la pantalla del dispositivo

Constructores:
– Proyectil() Simplemente establece a true estoyVivo

Metodos:
– abstract Update(int velocidad); Sin implementacion (porque cada subclase tendra su propio comportamiento). Mueve la bala n = velocidad pixels
– abstract Draw(); Sin implementacion. Dibuja la/s bala/s en pantalla

Puesto que la clase es abstracta no se puede usar directamente, es solo una pseudointerfaz para sus clases derivadas.

Meteorit 008

Aclaracion importante del foreach y el for:
Si en el foreach se borran elementos de ese mismo vector recorrido petara porque se altera la cantidad de elementos en el vector, es obligado usar for
Ejemplo:
foreach(Proyectil bala in balas) balas.Remove(bala) <- Peta
for(int i = 0; i < balas.Count; i++) balas.Remove(balas[i]); <- No peta

Aclaracion importante del rectangulo de colisiones:
Cada vez que movemos algo que queramos que colisione tenemos que mover tambien su rectangulo, para ello cada metodo que mueva (Update para npcs y Mover para nave) reestablecera la localizacion del rectangulo, dejando su anchura y altura igual que en la declaracion.
Ejemplo:
colision2.Location = new Point((int)bala2.Posicion.X, (int)bala2.Posicion.Y);

Aclaracion importante de la posicion de objetos:
El vector de posicion de sprite es Vector2 con X e Y float, por eso hay que castearlo a int cuando queramos dibujar en pantalla, porque no existe el pixel 1’5. Pero acordaos siempre de castear a int al final, cuando se haya calculado ya el nuevo valor de la variable, sino puede pasar lo siguiente…
Ejemplo (teniendo a=1’5, b=2):
c = (int)a*b; //Esto quedaria 1*2 => c = 2
c = (int)(a*b) //Esto quedaria (int)1’5*2 => c = 3
Esta tonteria si se hace mal podria hacer por ejemplo que si mueves algo pixel a pixel con una pendiente X veas que realmente adopte un comportamiento rectilineo…

Meteorit 007

Declaracion de la clase Background

Atributos:
– const RESOLUCION_HORIZONTAL = 800; Esto deberia ser lo tipico de precompilador #define y tal, pero c# no permite hacer esto, asi que la pongo dentro de la clase. Es la constante que recoge el ancho de la pantalla, que se supone k siempre sera 800…
– bool reproduciendo; True -> Fondo scrolleando. False -> Fondo pausado.
– int numeroFondoActual; Es un entero k va incrementandose conforme cargamos fondos, para saber cuando parar de cargar.
– string resourceName; Contiene el nombre exacto de la parte comun del nombre de los fondos que se quiere cargar. Es decir si los fondos son «Fondo1.1, Fondo1.2» esta variable sera «Fondo1.»
– int numeroFondos; Numero total de fondos a cargar.
– Sprite fondoA; Sprite que contiene el primer fondo a cargar.
– Sprite fondoB; Sprite que contiene el segundo fondo a cargar.
//Esta preparado para cargar siempre 2 fondos, pero tambien funciona si quieres scrollear solo 1 fondo, pasandole por parametro al constructor numeroFondos = 1, aunque se cargara un fondo NULL a su derecha

Constructores:
– Background(string resourceName, int numeroFondos) Pasarle por parametro la cadena k identifica a los fondos y el numero de fondos a cargar

Metodos:
– Reproducir() Pone a true el bool reproduciendo
– Pausar() Pone a false el bool reproduciendo
– DibujarImagen(Sprite fondo) Muestra en pantalla el Sprite pasado por parametro (necesita acceso a un spriteBatch, k nosotros tenemos global en la clase estatica GestorContenidos)
– Draw() Metodo draw que llama a DibujarImagen con fondoA y con fondoB
– Update(int velocidad) Logica del background scrolling, por parametro se le pasa el numero de pixels que se movera cada vez que se llame a este metodo.

Para usar la clase se creara un fondo -> Background fondo;
Luego se instanciara con los identificadores y el numero de fondos -> fondo = new Background(«Fondo1.», 6);
Y luego se llamara a update para moverlo (en este ejemplo se mueve 16 pixels cada 1 segundos) -> if(gameTime.TotalGameTime.Seconds % 1 == 0) fondo.Update(16);

PD: NO es recomendable pasar mas de 1 por parametro en la velocidad, porque si se mueve de 3 en 3 por ejemplo y el ancho de la imagen no es multipo de 3, entonces se pintara en pantalla pixels k se salen de la imagen. Lo mejor es variar los segundos k transcurren hasta volver a llamar al metodo, y llamarlo con 1, para k vaya pixel a pixel. Lo he implementado asi para hacer pruebas, aunque es totalmente funcional siempre que se usen cantidades potencia de 2 -> 1, 2, 4, 8, 16, 32, 64, etc.

Meteorit 006

Descripcion de la clase Sprite
Sprite contiene 3 atributos privados: Texture2D imagen; Vector2 Posicion; y string source;
– imagen es el contenedor de la imagen
– posicion es la posicion en coordenadas X e Y de la esquina superior izquierda del rectangulo en el que esta inscrita la imagen
– source es el nombre sin extension del resource a cargar (en caso del sprite sera una imagen)

Sprite contiene 2 constructores: Sin parametros y con 2 parametros (Vector2, string)
– El primero sirve para hacer pruebas, por si no quieres inicializar los atributos para instanciarlos luego con las propiedades
– El segundo inicializa los 3 atributos privados, la imagen se carga con el gestor de contenidos el resource pasado por el parametro string

Sprite contiene 3 Propiedades: 1 para cada atributo privado
– set y get para imagen. Setea o devuelve el contenedor Texture2D de la imagen.
– set y get para posicion. Setea o devuelve la posicion en formato Vector2 (para setear coordenadas X e Y individualmente seguir leyendo)
– set y get para source. Setea o devuelve el nombre del source string.

Sprite contiene 3 Metodos: los dos para setear las coordenadas X e Y de la posicion y el de cargar imagen en el contenedor
– setPosX(int) Establece la coordenada X de la posicion
– setPosY(int) Establece la coordenada Y de la posicion
– CargarImagen(source) Carga en el atributo imagen un resource identificado por el parametro string source

Ejemplos de uso:
/*Constructor sin parametros y coordenadas X e Y separadas
Sprite sprite = new Sprite();
sprite.setPosX(0);
sprite.setPosY(0);
sprite.Source = «nave»;
sprite.CargarImagen(sprite.Source);
*/
/*Constuctor con parametros y posicion encapsulada en Vector2
Sprite sprite = new Sprite(new Vector2(0, 0), «nave»);
*/

Asumiendo que sprite tiene acceso a un gestor de contenidos estatico, el metodo CargarImagen sera asi:
CargarImagen(string source) { imagen = GestorContenidos.contenido.Load<Texture2D>(source); }

Meteorit 005

Intentando implementar un ContentManager me doy cuenta de que no es tan facil… hay que declarar servicios y cosas que desconozco por ahora.
He probado a hacer una clase estatica global con un parametro que recoge el Content creado por defecto y funciona, asi ya es visible globalmente ^^
Se me habia ocurrido tambien usar un puntero a Content y pasarlo a la clase estatica, asi cuando se solicite alguna operacion sobre la variable de mi clase estatica se estara haciendo sobre el Content. Para usar punteros se escribe «unsafe» delante del metodo/declaracion. ContentManager no permite que punteros apunten a el… asi que idea descartada.

Implementacion completa de la clase GestorContenidos:
using Microsoft.Xna.Framework.Content;

namespace Prueba
{
static class GestorContenidos
{
public static ContentManager contenido;
public static SpriteBatch spriteBatch;
}
}
Ejemplo de uso:
GestorContenidos.contenido = Content; //Esto se hara en el metodo Initialize de game, lo que hace es pasarle el Content a nuestra clase para k sea visible en todo el namespace (lo malo es que asi tenemos dos ContentManager)
fondo1a.Imagen = GestorContenidos.contenido.Load<Texture2D>(fondo.Source); //Esto se hara desde cualquier otra clase que quiera hacer algo en ese content

Actualizacion 2/04/2011:
He añadido un atributo estatico spriteBatch a la clase, modificando ligeramente tambien Game1. Game1 corresponde a un nivel/pantalla, nos viene muy bien que en ese nivel sea global el spritebatch y el contentmanager. En el siguiente nivel se volveria a instanciar spritebatch, por tanto se borraria todo rastro del anterior y se cargaria el nuevo content de ese nivel (cambiando tambien la carpeta fisica que almacena todos los datos del nivel con el atributo contenido.RootDirectory = «Content2»;)

Meteorit 004

Vector2 posicion es una estructura con int X e int Y. Su propiedad solo permite modificar elementos de tipo Vector2, pero no deja accesible X e Y. He encontrado 3 formas de hacer propiedades:
public Vector2 Posicion { get { return posicion; } set { posicion = value; } }
public Vector2 Posicion { get; set; }
public Vector2 Posicion { get { return posicion; } set { posicion = new Vector2(value.X, value.Y); } }

La primera seria la normal, la que enseñan en los manuales y todos deben saber.
La segunda parece que es una forma abreviada. No se si hace exactamente lo de arriba pero funciona igualmente, el problema es que sale warning porque «posicion» no se ha usado.
La tercera se parece a lo que buscamos pero no es, la he visto en codigos de ejemplo pero no la he testeado, en algun contexto podria ser util.

Me decanto por hacer un metodo set para X y para Y como toda la vida. Puede resultar un poco engorroso al usarlo pero la otra solucion seria hacerlo public…
public void setPosX(int X) { posicion.X = X; }
public void setPosY(int Y) { posicion.Y = Y; }
Y ejemplo de llamada: bala.setPosX(180); en lugar de bala.Posicion.X = 180; que NO funcionaria.