Tutorial [Entrega 5]: Añadiendo disparos de los enemigos
Llegados a este punto, tenemos nuestra nave frente a unas cuantas naves enemigas. Nuestra nave puede disparar y eliminar a los enemigos, pero los enemigos todavía no pueden dispara para intentar eliminarnos. Vamos a ver como podemos hacer esto después del salto.
Como ya sabéis, si tenéis alguna duda, podéis plantearla en el foro: http://www.foro.xna-tutorial.com/viewtopic.php?f=4&p=8#p8
Añadir los disparos de los enemigos, dado el código que tenemos ya escrito en anteriores entregas del tutorial, va a ser realmente fácil.
Veamos los cambios que debemos introducir según las clases.
Clase “Enemigo”
Lógicamente, esta será la clase que debemos modificar. Lo que haremos será principalmente generar un numero aleatorio, el cual nos indicará si el enemigo debe disparar o no debe disparar. Manos a la obra.
Primero nos definiremos dos nuevos atributos:
2 public int probabilidadDisparo = 2;
El número 2 de “probabilidadDisparo” es la cota que indica si debe disparar o no. Si subimos ese número, los enemigos dispararan más a menudo. De esta forma podemos aumentar el nivel de dificultad (la cadencia de disparos enemigos) simplemente incrementando ese valor.
Ahora vamos al método “Actualizar()”, y le añadimos lo siguiente:
2 {
3
4 if (disp == null)
5 {
6 if (rand.Next(1000) < probabilidadDisparo)
7 disparar();
8 }
9 else
10 {
11 disp.actualiza();
12 }
13
14 }
Esto lo que hará será obtener un nuevo numero aleatorio, y si ese numero es menor que la cota de “probabilidadDisparo” entonces disparará. Esto solo lo hará si el enemigo no ha disparado (recordemos que cada nave solo puede tener un disparo al mismo tiempo). Si ya había disparado, entonces actualizaremos el disparo.
Clase “Nave”
En esta clase vamos a definir una propiedad que nos devuelva el rectángulo que envuelve a la nave para poder comprobar las colisiones. Es un cambio menor.
2 {
3 get { return new Rectangle((int)pos.X, (int)pos.Y, textura.Width, textura.Height); }
4 }
Clase “Game”
Y ahora viene la chica: si unos de los disparos del enemigo nos da, tenemos que eliminar nuestra nave. En próximas versiones lo que haremos será decrementar el contador de vidas de nuestra nave, pero ahora sólo tendremos una vida.
Así que antes de comprobar la colisión de nuestros disparos con los enemigos, que lo vimos en la anterior entrega, comprobaremos que ningún disparo de los enemigos colisione con nuestra nave. Si colisiona, eliminaremos nuestra nave.
2 {
3 if (e.Disparo != null && nave != null)
4 {
5 if (e.Disparo.Rect.Intersects(nave.Rectangulo))
6 {
7 nave = null;
8 e.Disparo = null;
9 }
10 }
11 }
Como ahora nuestra nave puede ser null, tenemos que cambiar todas las referencias a nuestra nave para que antes de ejecutar cualquier cosa compruebe que la nave no es null. Si no lo hacemos, nuestro juego “petará” cuando nos de un impacto. El código entero de la clase “game” es el siguiente:
2 using System.Collections.Generic;
3 using System.Collections;
4 using System.Linq;
5 using Microsoft.Xna.Framework;
6 using Microsoft.Xna.Framework.Audio;
7 using Microsoft.Xna.Framework.Content;
8 using Microsoft.Xna.Framework.GamerServices;
9 using Microsoft.Xna.Framework.Graphics;
10 using Microsoft.Xna.Framework.Input;
11 using Microsoft.Xna.Framework.Media;
12 using Microsoft.Xna.Framework.Net;
13 using Microsoft.Xna.Framework.Storage;
14
15 namespace spaceInvaders
16 {
17 /// <summary>
18 /// This is the main type for your game
19 /// </summary>
20 public class Game1 : Microsoft.Xna.Framework.Game
21 {
22 GraphicsDeviceManager graphics;
23 SpriteBatch spriteBatch;
24 Nave nave;
25 //Enemigo enemigo;
26 ArrayList enemigos;
27
28
29 /// <summary>
30 /// Constructor de la clase
31 /// </summary>
32 public Game1()
33 {
34 graphics = new GraphicsDeviceManager(this);
35 Content.RootDirectory = "Content";
36
37 }
38
39 /// <summary>
40 /// Allows the game to perform any initialization it needs to before starting to run.
41 /// This is where it can query for any required services and load any non-graphic
42 /// related content. Calling base.Initialize will enumerate through any components
43 /// and initialize them as well.
44 /// </summary>
45 protected override void Initialize()
46 {
47 // TODO: Add your initialization logic here
48 nave = new Nave();
49 enemigos = new ArrayList();
50
51
52 base.Initialize();
53 }
54
55 /// <summary>
56 /// LoadContent will be called once per game and is the place to load
57 /// all of your content.
58 /// </summary>
59 protected override void LoadContent()
60 {
61 // Create a new SpriteBatch, which can be used to draw textures.
62 spriteBatch = new SpriteBatch(GraphicsDevice);
63
64 //Le decimos a la nave que textura tiene que cargar
65 nave.Textura = Content.Load<Texture2D>("nave");
66 nave.TexturaDisparo = Content.Load<Texture2D>("disparoNave");
67
68
69 //Creamos los enemigos y los metemos en el arraylist
70 int numeroEnemigos = 5;
71 float X = 275;
72 float Y = 100;
73
74 for (int i = 0; i < numeroEnemigos;i++)
75 {
76 Vector2 pos = new Vector2(X, Y);
77 Enemigo e = new Enemigo(pos);
78 enemigos.Add(e);
79 X += 50;
80 }
81
82 //cargamos las texturas del enemigo
83 foreach(Enemigo e in enemigos)
84 {
85 e.Textura = Content.Load<Texture2D>("enemigo");
86 e.TexturaDisparo = Content.Load<Texture2D>("disparoNave");
87 }
88 // TODO: use this.Content to load your game content here
89 }
90
91 /// <summary>
92 /// UnloadContent will be called once per game and is the place to unload
93 /// all content.
94 /// </summary>
95 protected override void UnloadContent()
96 {
97 // TODO: Unload any non ContentManager content here
98 }
99
100 /// <summary>
101 /// Allows the game to run logic such as updating the world,
102 /// checking for collisions, gathering input, and playing audio.
103 /// </summary>
104 /// <param name="gameTime">Provides a snapshot of timing values.</param>
105 protected override void Update(GameTime gameTime)
106 {
107 // Allows the game to exit
108 if (Keyboard.GetState().IsKeyDown(Keys.Escape))
109 this.Exit();
110
111 // TODO: Add your update logic here
112 if (nave != null)
113 nave.actualiza();
114
115
116 foreach (Enemigo e in enemigos)
117 {
118 e.actualiza();
119 }
120
121
122 //comprobamos si algun disparo enemigo nos ha dado
123 foreach (Enemigo e in enemigos)
124 {
125 if (e.Disparo != null && nave != null)
126 {
127 if (e.Disparo.Rect.Intersects(nave.Rectangulo))
128 {
129 nave = null;
130 e.Disparo = null;
131 }
132 }
133 }
134
135 //Comprobamos que el disparo colisiona con algun enemigo
136 //Si colisiona, eliminamos el enemigo y el disparo
137
138 //Array auxiliar para alamacenar los enemigos que debemos eliminar
139 ArrayList eliminar = new ArrayList();
140
141 //Si no hay disparo, no hace falta que entre
142 if (nave != null && nave.Disparo != null)
143 {
144 //Recorremos la lista de enemigos y comprobamos si alguno colisiona
145 foreach (Enemigo e in enemigos)
146 {
147 Rectangle rectEnemigo = e.Rect;
148 Rectangle rectDisparo = nave.Disparo.Rect;
149
150 if (rectEnemigo.Intersects(rectDisparo))
151 {
152 //añadimos al enemigo a la lista a eliminar
153 eliminar.Add(e);
154 }
155 }
156
157 //si la lista de enemigos a eliminar no esta vacia
158 if (eliminar.Count > 0)
159 {
160 foreach (Enemigo el in eliminar)
161 {
162 //eliminamos al enemigo
163 enemigos.Remove(el);
164 }
165
166 //eliminamos el disparo
167 nave.Disparo = null;
168 }
169 }
170
171
172 base.Update(gameTime);
173 }
174
175 /// <summary>
176 /// This is called when the game should draw itself.
177 /// </summary>
178 /// <param name="gameTime">Provides a snapshot of timing values.</param>
179 protected override void Draw(GameTime gameTime)
180 {
181 GraphicsDevice.Clear(Color.Black);
182
183 // TODO: Add your drawing code here
184 spriteBatch.Begin();
185
186 if (nave != null) nave.Draw(spriteBatch); //pitnamos la nave
187 foreach (Enemigo e in enemigos)
188 {
189 e.Draw(spriteBatch);
190 }
191
192
193 spriteBatch.End();
194
195
196 base.Draw(gameTime);
197 }
198 }
199 }
200
Y ya está! ya tenemos nuestros enemigos disparando y nosotros también disparando.
Como siempre, puedes bajarte el código fuente del siguiente enlace:
Descargar Código Fuente del tutorial
Un saludo y hasta la próxima entrega!
Muchisimas gracias por estos tutos tan logrados, la verdad me estan siendo bastante utiles para iniciarme en esto del XNA. He de comentar que nada mas empezar esta parte comentas que nuestra nave ya dispara y te pones con los aliens, pero no es asi(te lo has saltao xDD) aunque sigiendo un poco el codigo se puede sacar. Lo otro, no se porque, le dado vueltas pero los aliens solo disparan una vez y como mucho repiten disparo si le dan a la nave nuestra ¿como es eso? muchas gracias^^
El fallo esta en la clase Enemigo.cs, la linea 114:
if (disp == null)
{
if (rand.Next(1000) < probabilidadDisparo)
disparar();
}
else
{
disparar();
}
Por que si no es un bucle infinito, dado que disp, nunca dejara de ser null.
Hola solo tengo una duda cuando agrego el arhcivo de imagen en content y compilo el programa no se compila y visual c# 2008 se cuelga. necesito ayuda porfavor.
Buen Tutorial……….
Lo que pasa es que la Instancia de disparo (disp) en la clase enemigo nunca se hace Null.
Amigo asi lo repare ,Agregale esta Linea ,
a la Clase Enemigo
public void actualiza()
{
if (disp == null)
{
if (rand.Next(10) 600)
disp = null;
}
}