¿Que tal pequeños cangrejillos? Disculpad la tardanza de esta quinta parte del tutorial, pero me ha dado mas problemas de los que me esperaba, y además he andado algo ocupadillo. En esta parte de tutorial vamos a aprender a manejar detección de colisiones, parte fundamental de prácticamente cualquier videojuego, así que nos ponemos serios, que empieza lo difícil. Empecemos por la teoría.
Existen numerosas técnicas de detección de colisiones entre sprites (no exclusivas de XNA, son comunes en el mundo del desarrollo de videojuegos). Lo más fácil y eficiente (de cara a procesamiento y obtención de buenos resultados de performance) es utilizar rectángulos, detectando colisión cuando estos se intersecan.
El problema obvio es la imprecisión en la detección de la colisión. Según esto, un impreciso acercamiento entre los dos objetos ya supondría una colisión, como se puede ver en las imágenes anteriores.
En muy pocas ocasiones, pues, esta técnica tan simple nos valdrá. Aunque existen otras variantes más precisas, y todavía eficientes, basadas en la detección de colisiones mediante rectángulos. Una de ellas sería dividir cada uno de los sprites en rectángulos más pequeños, y englobarlos todos en uno más grande. Entonces, primero intentaríamos detectar la colisión mediante los rectángulos grandes, y solo si esta es afirmativa, intentaríamos detectar la colisión entre los rectángulos más pequeños (y estos son los que decidirían si hay colisión o no).
Como esta existen muchas variantes de la misma técnica, basadas en diferentes formas geométricas, y si bien esta técnica es mucho mas precisa que la anterior, aun puede ser más precisa, aplicando la detección de colisiones denominada “Perfecta” o Pixel a Pixel, en la cual se obtendrá precisión perfecta, activándose solo cuando se colisione a nivel de pixel.
La principal desventaja de esta técnica es que es más complicada que las anteriores, y por tanto tiene un coste computacional mucho mayor. Sin embargo en nuestro caso vamos a adoptar una solución intermedia, comprobando solo la colisión pixel a pixel solo si se detecta colisión con rectángulos. Evidentemente el rendimiento no es igual que con las técnicas más simples, pero se mejorará muchísimo.
Fuente: http://geeks.ms/blogs/jbosch/archive/2009/08/08/xna-pixel-perfect-collision-con-xna-en-base-a-un-mapa-de-colisiones-2d.aspx
Pues explicado esto, vamos al tema. Lo primero que habrá que hacer es una clase para crear enemigos. Esta parte voy rápido que no hay nada nuevo. Añadimos la imagen del enemigo como siempre (metiéndola en la carpeta content y arrastrándola al VS). Una vez esta esto, creamos la clase Enemigo1, algo así:
class Enemigo1
{
private const int anchoImagen = 40;
private const int altoImagen = 57;
private Texture2D imagen;
public Texture2D Imagen
{
get { return imagen; }
}
private Rectangle bounds;
public Rectangle Bounds
{
get { return bounds; }
}
private Vector2 posicion;
public Vector2 Posicion
{
get { return posicion; }
}
int altoVentana;
int anchoVentana;
public event EventHandler FueraDePantalla;
public Enemigo1(Vector2 posicion, int altoVentana, int anchoVentana, ContentManager Content)
{
this.altoVentana = altoVentana;
this.anchoVentana = anchoVentana;
this.posicion = posicion;
//Ya que los disparos pueden surgir en cualquier momento, y no al principio de la ejecución no tiene sentido tener un método
//LoadContent que cargue las imágenes. En vez de eso las cargaremos en el constructor.
imagen = Content.Load<Texture2D>("EnemigoBasico");
}
public void Update()
{
posicion.Y += 3;
bounds = new Rectangle((int)posicion.X, (int)posicion.Y, anchoImagen, altoImagen);
if (posicion.Y >= altoVentana)
FueraDePantalla(this, null);
}
public void Draw(SpriteBatch spbtch)
{
spbtch.Draw(imagen, posicion, new Rectangle(0,0,anchoImagen, altoImagen), Color.White);
}
}
Imagino que a estas alturas sabréis por donde van los tiros. 40 y 57 son las dimensiones de la imagen. Además de los atributos de siempre vemos que tenemos un Rectangle, llamado bounds, en el que almacenamos el rectángulo que representa la imagen dibujada en pantalla y lo mantenemos actualizado (en update). Además añadimos varias propiedades para poder acceder a imagen, _posicion y bounds sin necesidad de implementar un método get (Gracias a esto se podra acceder simplemente con enemigo.Imagen o enemigo.Bounds. Este nuevo atributo, así como las diferentes propiedades también tienen que ser añadidas a las clases Nave y Disparo, pues nos hará falta para la detección de colisiones:
class Disparo
{
private const int anchoImagen = 6;
private const int altoImagen = 22;
private Texture2D imagen;
public Texture2D Imagen
{
get { return imagen; }
}
private Rectangle bounds;
public Rectangle Bounds
{
get { return bounds; }
}
private Vector2 posicion;
public Vector2 Posicion
{
get { return posicion; }
}
public event EventHandler FueraDePantalla;
public Disparo(Vector2 posicion, int anchoNave, ContentManager Content)
{
this.posicion = posicion;
//movemos un poco la posicion del disparo, para que salga desde el centro de la nave y no desde una esquina
posicion.X += (anchoNave / 2);
//lo que acabamos de centrar es la esquina superior izquierda del disparo. Así situaremos el centro alineado con el centro de la imagen
//los 3 pixeles extra es por que la imagen del disparo no esta perfectamente centrada.
posicion.X -= (anchoImagen / 2)+3;
//Ya que los disparos pueden surgir en cualquier momento, y no al principio de la ejecución no tiene sentido tener un método
//LoadContent que cargue las imágenes. En vez de eso las cargaremos en el constructor.
imagen = Content.Load<Texture2D>("weapons");
}
public void Update()
{
posicion.Y-=5;
bounds = new Rectangle((int)posicion.X, (int)posicion.Y, anchoImagen, altoImagen);
if (posicion.Y <= 0)
FueraDePantalla(this, null);
}
public void Draw(SpriteBatch spbtch)
{
spbtch.Draw(imagen, posicion, new Rectangle(0,0,anchoImagen, altoImagen), Color.White);
}
}
class Nave
{
private Rectangle rectangulo;
private const int anchoImagen = 42;
private const int altoImagen = 44;
private Texture2D imagen;
public Texture2D Imagen
{
get { return imagen; }
}
//Este rectangulo representa la posicion dentro de la ventana, mientras que el que ya teniamos
//representa la posición dentro de la imagen.
private Rectangle bounds;
public Rectangle Bounds
{
get { return bounds; }
}
private Vector2 posicion;
public Vector2 Posicion
{
get { return posicion; }
}
private int altoVentana;
private int anchoVentana;
private Listdisparos;
public List Disparos
{
get { return disparos; }
}
private int frameCounter = 0;
private ContentManager content;
public Nave(int altoVentana, int anchoVentana)
{
this.altoVentana = altoVentana;
this.anchoVentana = anchoVentana;
posicion = new Vector2(altoVentana - altoImagen * 2, (anchoVentana - anchoImagen) / 2);
CrearRectangulo(anchoImagen, altoImagen * 2);
disparos = new List<Disparo>();
}
public void LoadContent(ContentManager Content)
{
this.content = Content;
imagen = Content.Load<Texture2D>("battleship");
}
public void Update()
{
UpdateShots();
UpdatePosition();
UpdateRectangle();
}
private void UpdateShots()
{
frameCounter++;
if (Keyboard.GetState().IsKeyDown(Keys.Z) && disparos.Count < 6 && frameCounter > 7)
{
Disparo s = new Disparo(posicion, anchoImagen, content);
disparos.Add(s);
s.FueraDePantalla += new EventHandler(FueraDePantallaHandler);
frameCounter = 0;
}
disparos.ForEach(x => x.Update());
}
private void UpdatePosition()
{
if (Keyboard.GetState().IsKeyDown(Keys.Left) && posicion.X > 5)
posicion.X -= 5;
if (Keyboard.GetState().IsKeyDown(Keys.Right) && posicion.X < (anchoVentana - anchoImagen))
posicion.X += 5;
if (Keyboard.GetState().IsKeyDown(Keys.Up) && posicion.Y > 5)
posicion.Y -= 5;
if (Keyboard.GetState().IsKeyDown(Keys.Down) && posicion.Y < (altoVentana - altoImagen))
posicion.Y += 5;
bounds = new Rectangle((int)Posicion.X, (int)Posicion.Y, anchoImagen, altoImagen);
}
private void UpdateRectangle()
{
//a partir de aquí escojemos la parte de imagen que queremos dibujar y la almacenamos en rectangle,
// en funcion de la combinaion de botones que se esten pulsando.
if (Keyboard.GetState().IsKeyDown(Keys.Left) && Keyboard.GetState().IsKeyDown(Keys.Up))
{
CrearRectangulo(0, altoImagen);
}
else if (Keyboard.GetState().IsKeyDown(Keys.Right) && Keyboard.GetState().IsKeyDown(Keys.Up))
{
CrearRectangulo(anchoImagen * 2, altoImagen);
}
else if (Keyboard.GetState().IsKeyDown(Keys.Up))
{
CrearRectangulo(anchoImagen, altoImagen);
}
else if (Keyboard.GetState().IsKeyDown(Keys.Left) && Keyboard.GetState().IsKeyDown(Keys.Down))
{
CrearRectangulo(0, 0);
}
else if (Keyboard.GetState().IsKeyDown(Keys.Right) && Keyboard.GetState().IsKeyDown(Keys.Down))
{
CrearRectangulo(anchoImagen * 2, 0);
}
else if (Keyboard.GetState().IsKeyDown(Keys.Down))
{
CrearRectangulo(anchoImagen, 0);
}
else if (Keyboard.GetState().IsKeyDown(Keys.Left))
{
CrearRectangulo(0, altoImagen * 2);
}
else if (Keyboard.GetState().IsKeyDown(Keys.Right))
{
CrearRectangulo(anchoImagen * 2, altoImagen * 2);
}
else
{
CrearRectangulo(anchoImagen, altoImagen * 2);
}
}
private void CrearRectangulo(int x, int y)
{
rectangulo = new Rectangle(x, y, anchoImagen, altoImagen);
}
public void Draw(SpriteBatch spbtch)
{
spbtch.Draw(imagen, posicion, rectangulo, Color.White);
DrawShots(spbtch);
}
private void DrawShots(SpriteBatch spbtch)
{
foreach (Disparo s in disparos)
{
s.Draw(spbtch);
}
}
private void FueraDePantallaHandler(Object sender, EventArgs args)
{
disparos.Remove((Disparo)sender);
}
}
Solo muestro las partes que cambio por evitar hacer un post enorme, así que cuidado si copiáis pegáis. Con esto añadimos las nuevas propiedades, el nuevo atributo, y mantenemos actualizado este atributo. Es importante darse cuenta de la diferencia entre este rectángulo (bounds) que muestra el rectángulo ocupado por la imagen en la ventana, y el otro rectángulo que ya teníamos en Nave, que representa que parte de la imagen se va a dibujar.
Ahora que esta todo preparado, lo primero que hay que hacer es mostrar los enemigos por pantalla. Esto lo manejaremos en la clase Game1 de momento (En la próxima parte del tutorial se hará una pequeña reestructuración).
public class Game1 : Microsoft.Xna.Framework.Game
{
private GraphicsDeviceManager graphics;
private SpriteBatch spriteBatch;
private Nave nave;
private Fondo fondo;
private int frameCounter = 0;
private List<Enemigo1> enemigos;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
enemigos = new List<Enemigo1>();
Content.RootDirectory = "Content";
}
...
protected override void Update(GameTime gameTime)
{
// Cuidado con esto, no usamos mando.
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
this.Exit();
fondo.Update();
nave.Update();
UpdateEnemigos();
base.Update(gameTime);
}
private void UpdateEnemigos()
{
frameCounter++;
if (frameCounter > 60)
{
Random r = new Random();
Enemigo1 e = new Enemigo1(
new Vector2(r.Next(graphics.PreferredBackBufferWidth), -57),
graphics.PreferredBackBufferHeight,
graphics.PreferredBackBufferWidth,
Content
);
enemigos.Add(e);
e.FueraDePantalla += new EventHandler(FueraDePantallaHandler);
frameCounter = 0;
}
enemigos.ForEach(x => x.Update());
}
private void FueraDePantallaHandler(Object sender, EventArgs args)
{
enemigos.Remove((Enemigo1)sender);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
fondo.Draw(spriteBatch);
nave.Draw(spriteBatch);
foreach (Enemigo1 e in enemigos)
{
e.Draw(spriteBatch);
}
spriteBatch.End();
base.Draw(gameTime);
}
Añadimos a los atributos una lista de enemigos, que luego instanciamos en el constructor, además de un int para contar los frames (ahora veréis para que). Después, en Update, añadimos UpdateEnemigos, que hará varias cosas. Lo primero será aumentar frameCounter para así saber cuando pasan 60 frames.
Si el contador es mayor que 60 procederemos a añadir un nuevo enemigo (Con esto evitamos añadir sabedioscuantos enemigos por segundo). Para ello, instanciamos un objeto Random, y creamos un enemigo con una coordenada X aleatoria (Con la anchura de la pantalla de máximo) y –57 de coordenada Y para que entre poco a poco a la pantalla. Añadimos este enemigo recién creado a la lista enemigos, controlamos el evento FueraDePantalla (De igual manera que lo hacemos con los disparos, para borrar los enemigos que se salgan de la pantalla). Por ultimo ponemos el contador de frames a 0.
Después, fuera del If (esto se tiene que ejecutar siempre, no solo cada 60 frames) se invocara a Update de todos los enemigos. Una vez más no utilizamos foreach por que es posible que se dispare FueraDePantalla y que cambie enemigos, lo que haría que un foreach diese un error en tiempo de ejecución, pero la expresión lambda utilizada en esencia hace lo mismo.
Tras esto podemos probar a ejecutar y veremos que ya tenemos una serie de enemigos en pantalla cada X tiempo. ¿Va cogiendo forma no?. Ahora que ya está todo preparado podemos empezar con la detección de colisiones, manejada una vez mas desde Game1 temporalmente:
protected override void Update(GameTime gameTime)
{
// Cuidado con esto, no usamos mando.
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
this.Exit();
fondo.Update();
nave.Update();
UpdateEnemigos();
UpdateColisiones();
base.Update(gameTime);
}
...
private void UpdateColisiones()
{
//eliminamos cualquier enemigo que haya colisionado contra cualquier disparo en la pantalla
bool colision = false;
Enemigo1 enemigoABorrar = null;
Disparo disparoABorrar = null;
foreach (Enemigo1 e in enemigos)
{
foreach (Disparo d in nave.Disparos)
{
if(ColisionEnemigoDisparo(e, d))
{
//si hay colision ponemos el tag a true, almacenamos el disparo y el enemigo y rompemos el primer bucle
colision = true;
enemigoABorrar = e;
disparoABorrar = d;
break;
}
}
//si el tag esta activo rompemos el segundo bucle
if (colision)
break;
}
//si el tag esta activo borramos el enemigo y el disparo
if (colision)
{
enemigos.Remove(enemigoABorrar);
nave.Disparos.Remove(disparoABorrar);
}
}
private bool ColisionEnemigoDisparo(Enemigo1 e, Disparo d)
{
//Si los rectangle de e y de d se intersectan comprobamos la colision.
if(e.Bounds.Intersects(d.Bounds))
return ColisionPixel(d.Imagen, e.Imagen, d.Posicion, e.Posicion);
return false;
}
public static bool ColisionPixel(Texture2D texturaA, Texture2D texturaB, Vector2 posicionA, Vector2 posicionB)
{
bool colisionPxAPx = false;
uint[] bitsA = new uint[texturaA.Width * texturaA.Height];
uint[] bitsB = new uint[texturaB.Width * texturaB.Height];
Rectangle rectanguloA = new Rectangle(Convert.ToInt32(posicionA.X), Convert.ToInt32(posicionA.Y), texturaA.Width, texturaA.Height);
Rectangle rectanguloB = new Rectangle(Convert.ToInt32(posicionB.X), Convert.ToInt32(posicionB.Y), texturaB.Width, texturaB.Height);
//almacenamos los datos de los pixeles en las variables locales bitsA y bitsB
texturaA.GetData(bitsA);
texturaB.GetData(bitsB);
//almacenamos las coordenadas que delimitaran la zona en la que trabajaremos
int x1 = Math.Max(rectanguloA.X, rectanguloB.X);
int x2 = Math.Min(rectanguloA.X + rectanguloA.Width, rectanguloB.X + rectanguloB.Width);
int y1 = Math.Max(rectanguloA.Y, rectanguloB.Y);
int y2 = Math.Min(rectanguloA.Y + rectanguloA.Height, rectanguloB.Y + rectanguloB.Height);
for (int y = y1; y < y2; ++y)
{
for (int x = x1; x < x2; ++x)
{
if (((bitsA[(x - rectanguloA.X) + (y - rectanguloA.Y) * rectanguloA.Width] & 0xFF000000) >> 24) > 20 &&
((bitsB[(x - rectanguloB.X) + (y - rectanguloB.Y) * rectanguloB.Width] & 0xFF000000) >> 24) > 20)
{
//Se comprueba el canal alpha de las dos imagenes en el mismo pixel. Si los dos son visibles hay colision.
colisionPxAPx = true;
break;
}
}
// Rompe el bucle si la condicion ya se ha cumplido.
if (colisionPxAPx)
{
break;
}
}
return colisionPxAPx;
}
Añadimos a Update otro método en el cual manejaremos las colisiones. Como ya mencionamos anteriormente no se puede eliminar la lista que se este iterando dentro de un iterador foreach, pero esta vez no podremos usar una expresión lambda, así que utilizaremos un tag y unas variables locales para saber cuando se encuentra una colisión y borrar tanto el disparo como el enemigo después de salir del bucle.
En el método colisión enemigo disparo, usado por el método anterior, comprobamos si hay una colisión rectangular, y en caso de que sea así pasamos a ver si hay colisión pixel a pixel. En caso de que sea así devolvemos true y si no devolvemos false.
En cuanto al método estático ColisionPixel, almacenamos los datos de los pixeles en los arrays bitsA y bitsB, buscamos los limites del espacio que vamos a manejar, con las coordenadas x1, x2, y1 y y2. Con todo esto recorremos los arrays bitsA y bitsB con dos bucles for en los cuales comprobamos en cada iteración el canal Alpha de transparencia de las dos imágenes en un punto concreto. Por esto en caso de que ninguna de las dos imágenes sea transparente en cualquier punto significa que hay colisión, por lo que devolveremos true.
Con esto la detección de colisiones esta completa, y lo podemos ver ejecutando el juego, aunque no se aprecie en la captura :P La colisión de los enemigos con las nave la pondremos mas adelante, cuando se añadan las vidas y la pantalla de gameover. No obstante esto empieza a coger forma ya, ¿no? Podéis bajar el código hasta ahora en el siguiente enlace: http://www.megaupload.com/?d=OE6KUY4S
En la próxima entrega del tutorial menú, pantalla de pausa y pantalla de gameover. No os lo perdáis y ¡Sed buenos cangrejos!
EDIT: He cambiado algunas cosas en el código respecto a la parte 5 del tutorial original con la intención de obtener un código más limpio. Quite todas las _ de las variables, y me asegure de que todas las propiedades estaban en mayúscula. A parte de esto también cambie algunos puntos donde se accedía a las variables mediante las propiedades desde dentro de la misma clase.
Para los que hagan el tutorial a partir de ahora (22/09/2010) no lo notarán, pues ya esta actualizado, pero los que ya han hecho el tutorial que se bajen mi código, ya que esta corregido. Es posible que se me colase alguna errata, en tal caso hacédmelo saber.
Hola soy Juan Antonio,sigo emocionado con tu blog,jej,despues de realizar el modulo superior de ASI donde solamente programabamos algo en C,esto me resulta un poco complicado,pero con tu ayuda se entienden bastante las cosas..Gracias..
ResponderEliminarPor cierto,pasate por el post de Junio,tengo una pequeña duda, a ver si me puedes ayudar, saludos..
buenas!
ResponderEliminarprimero de todo me gustaria felicitarte por este impresionante tutorial y agradecerte mucho todo este trabajo. como bien decias en otro apartado, google sabe mucho pero en cada sitio dicen cosas distintas asi que al final la cosa se vuelve bastante liosa de hacer.
dicho esto me gustaria hacerte un par de preguntas a ver si sabrias responderme el porqué:
1- estoy siguiendo el tutorial y me falta el tema de poner las naves y eso de modo que le he pasado a un amigo la carpeta donde estan los ficheros para que lo pruebe y bueno, le pedia una version actualizada del framework (la ha actualizado) y luego al abrir el archivo le dice: el programa dejó de funcionar. sabes si hay que hacer alguna cosa (empaquetar alguna libreria) para que la otra gente pueda probarlo?
2- sabes como añadir las bibliotecas de la directx a un proyecto de c#? es que en las otras versiones vas a añadir referencia y te deja buscarlas pero en el vs2010 no me sale nada para buscar. he instalado el paquete de la directx que salio hace poco pero no pasa nada :S
perdona por acapararte de esta forma.
PD: difundire este blog por mi facultad (ingenieria) para que tengas mas seguidores que te lo mereces =)
En cuanto a la primera pregunta, creo que se por que es. Tu amigo necesitara instalarse xna y no solo el .net framework para que funcionen los programas desarrollados en xna. Es una putada, pero al fin y al cabo xna esta mas pensado para xbox y para moviles que para pc en mi opinion. En cuanto a lo segundo lo siento pero no tengo ni idea, soy tan nuevo como tu con el vs2010, y ya en los antiguos era complicado. Perdona x tardar tanto en contestar pero han sido unas laaargas vacaciones xD tengo que volver a ponerme con los tutoriales
ResponderEliminarHola,mi pregunta era la misma que la que planteaba joan marc,simplemente que el programa no me funciona en pc's que no tienen instalado el vs2010.Aunque por bueno,supongo que las librerías solamente las llevará el visual studio y por eso no funcionará en otros pc's.
ResponderEliminarY otra cosa,ya que poseo una X360,como puedo ejecutar el programa allí?ya que los ejecutables de la X360 si mal no recuerdo tienen extensión .xex,tampoco es una cosa que me preocupe,pero me pica la curiosidad jeje.
Gracias por tu atención y por tu tiempo y
Sigue así con tus tutos Wardamo,que estan muy interesantes,intentaré difundir este blog por clase lo mas que pueda.
Gracias por tu ayuda crack!!
Saludos
Para ello hay que seguir unos cuantos pasos en tu proyecto, modificando algunas cosillas como los controles y los sonidos, y tb cambiar el tipo de proyecto. Lo tienes bastante bien explicado aquí, no debería ser dificil: http://blogs.msdn.com/b/coding4fun/archive/2007/03/23/1938626.aspx
ResponderEliminarjajaja! no te preocupes hombre, en verano todos tenemos la obligación de descansar. lo del XNA lo tendré en cuenta para cuando se lo pase a los amigos (mayoritariamente se lo pasaré a 2 con los que queremos empezar a hacer juegos).
ResponderEliminary el tema de las librerias.. da igual, por lo que tengo visto de XNA creo que no me hará falta recurrir a ellas.
Ánimo con los tutoriales!!
Si, no hace falta agregar ninguna, me parecio raro pero imagine que lo usarias para otra cosa, pero usando las libreriass de xna y tal no hace falta agregar nada
ResponderEliminarDe lujo Wardamo,jeje,voy a hecharle el guante ya mismo,gracias por el enlace ,voy a ver que saco.
ResponderEliminarSeguiremos atentos al blog,esta interesantísimo jeje..
Saludos
He hecho algunos cambios en esta ultima parte, revisad el comentario que puse al final del blog los que han hecho anteriormente esta parte del tutorial. Perdon por mi grán ausencia este tiempo, pero tengo clase de tarde, y por la mañana cuesta más mentalizarse de hacer esas cosas. Pronto continuare con el tutorial de xna, con otras 2 partes más al menos (entramos en la recta final ^^)
ResponderEliminarEsperando parte 6
ResponderEliminareso eso cangrejo padre, esperamos la parte 6
ResponderEliminarbuenisimo tu blog la verdad xD te felicito por tu trabajo y por compartir tus conocimientos
ResponderEliminarsaludos desde mexico !
espero pronto la parte 6 y por favor si por algún motivo ya no tienes tiempo, publica algunas paginas donde podamos terminar el proyecto o si ya lo tienes no dudes en poner el link del proyecto.
ResponderEliminarEl proyecto esta tal como lo teneis vosotros, pues lo voy haciendo según hago el tutorial. En cuanto a otras paginas, hay muchos tutoriales de XNA por ahi, aunque no se si los habrá tan completos (por eso me puse a hacer este) Con mi horario me es muy dificil continuar pero lo intentare, tened un poco de paciencia :)
ResponderEliminarespero mas noticias tuyas.
ResponderEliminarOye me gusto tus tutoriales ojala puedieras poner mas ya que soy nuevo en esto de programacion en xna y me familiarizo, me quede en el 5 eperando el 6ojala se pueda dar pronto XD gracias por tu entrega
ResponderEliminarHola, me gustaria pedirte consejo para empezar en esto de programar videojuegos. Mi situacion es que estoy en la universidad estudiando informatica y se me da bastante bien el tema de la programacion y lo disfruto, y siempre he querido trabajar en el mundo de los videojuegos ya que es mi hobby nº1, asi que aqui estoy, buscando informacion hasta que he llegado a tu blog. El caso es que he encontrado por internet un libro en ingles que tiene buenas referencias y he pensado en usarlo como base para empezar a aprender por mi cuenta y no solo lo que me enseñen en la universidad, que evidentemente no esta orientado a los videojuegos.
ResponderEliminarEn mi universidad he aprendido a programar en java con el eclipse. Mi intencion es aprender C++ ya que parece ser la mejor opcion, y me encontre con este libro : "Beginning C++ Game Programming - Michael Dawson". Pero nada mas empezar a leerlo me ha surgido la duda de: qué programa/entorno/IDE uso??? EL libro aconseja Dev-C++, pero segun he visto en internet esta anticuado y la mejor opcion (pensando en Windows) parece ser Visual Studio 2010. Uso este ultimo? O sigo con eclipse para C++??
Alguna recomendacion??
P.D: Siento el caos que he liado en mi comentario pero es que no paro de darle vueltas al tema en mi cabeza y a lo mejor no he expresado algo bien o no lo he explicado lo suficiente para hacerme entender.
Hola unosinemule. Visual Estudio siempre es una buena opción, pero esta el tema de que no es exactamente c++, sino .net c++, es decir, es una version ligeramente distinta y eso te puede dar guerra a la hora de incluir algunas librerias si estan pensadas para c++ puro, pero por lo que se es bastante raro, asi que si no te dan problemas las librerias usa VS.
ResponderEliminarTengo un tutorial de los primeros de como incluir librerias en VS c++, por que era un lio, pero me temo que es para una version antigua de VS.
Un saludo, espero haberte ayudado
felicidades por tu tutorial esta muy bueno... estamos a la espera de la parte 6.. Gracias por este aporte tan bueno y bien explicado
ResponderEliminarMuy buen aporte, veo que hace un año no posteas nada, please queremos aprender mas, ya tengo las colisiones entre disparo y enemigo, trate d hacer la colisión entre nave y enemigo pero no me sale :(, ayuda!!!!
ResponderEliminarMuy buenos los tutoriales, los estoy siguiendo y ayudan mucho, una pregunta, tienes planeado seguir con ellos?
ResponderEliminara la espera de la parte 6 :( por fa!!! queremos aprender mas
ResponderEliminarAmigo, que tutorial tan bueno! por los comentarios todos estamos esperando el tutorial No. 6, se q andas ocupado, pero, lo haras???
ResponderEliminarespero ansioso el siguiente! :D
Muy buen tutorial!! Esperando la parte 6 =)
ResponderEliminarMuchas gracias por el tutorial. Haras pronto el 6? Gracias!!
ResponderEliminaresperando que continues con este magnifico tutorial!
ResponderEliminarMuy bueno el tutorial
ResponderEliminaresperando la parte 6
espero la subas pronto :)
Excelente Tutorial
ResponderEliminarSoy uno más que espera en algún momento publiques la sexta parte o algún otro tutorial con otro proyecto de videojuegos.
Muchas Gracias
Saludos
Que paso hermano te fuiste y abandonas el barco (tu blog), acaso no te das cuenta que ya hay muchos pasajeros adentro!!
ResponderEliminarAlguien sería tan amable de resubir el código a algún servidor funcional (no solo de esta parte, también de las anteriores) muchas gracias de antemano...
ResponderEliminar