| Volver al Inicio | Microsoft Student Tech Club: Universidad Libre |
Tutorial de XNA: El protagonista dispara

Se añade a la biblioteca de imágenes del proyecto.
Nuestro lanzador puede lanzar varios cohetes, luego requerimos un arreglo unidimensional de objetos.

Y se cargan las imágenes del cohete en el arreglo unidimensional de objetos

Cada cohete tiene una vida limitada: nace en el momento en que es disparado por el lanzador, viaja por la ventana y desaparece cuando llega al borde de esta. Luego es necesario saber cuando el cohete se mantiene activo, es decir, cuando se muestra por pantalla. Para eso debemos agregar un nuevo atributo a la clase ObjetosJuego de tipo booleano que cuando tiene el valor de "true" significa que el cohete esta activo y debe mostrarse, caso contrario, manténgalo oculto.
No solamente eso, también hay algo importante con los cohetes: se desplazan por la pantalla, el desplazamiento tiene una dirección y una velocidad, por lo tanto debemos agregar un atributo de velocidad (magnitud y dirección) a la clase ObjetosJuego.
El cambio se observa en la imagen

Este es el código
using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; using Microsoft.Xna.Framework.Net; using Microsoft.Xna.Framework.Storage; namespace PrimerJuego
{
class ObjetosJuego
{
public Texture2D sprite; //La textura (o imagen) que tendrá el sprite
public Vector2 posicion; //En que punto se encuentra ubicado el sprite
public Vector2 centro; //El centro por donde gira el lanzador
public float rotacion; //Angulo de rotación del lanzador y del cohete public bool esta_activo; //Sabe si esta activo o no el cohete
public Vector2 velocidad; //Dirección y velocidad del cohete //Constructor
public ObjetosJuego(Texture2D textura)
{
sprite = textura; //Recibe la textura por parámetro
posicion = Vector2.Zero; // Posición [0,0] por defecto
rotacion = (float) 0.0; //Rotación 0.0 en radianes por defecto
centro = new Vector2(sprite.Width / 2, sprite.Height / 2); //El centro del sprite esta_activo = false; //Por defecto no se muestra el cohete
velocidad = Vector2.Zero; //Por defecto el cohete no se mueve
}
}
} |
Ahora viene la programación del cohete como tal y estas son las cosas que hay que tener en cuenta:
1. El cohete se dispara cuando el usuario presiona la tecla de disparo. Hay que validar que el usuario no mantenga presionada esa tecla constantemente, debe soltarla.
2. Si un cohete está en estado inactivo, entonces es candidato a ser usado cuando el usuario presione la tecla de disparar.
3. El cohete una vez lanzado comienza a desplazarse por la ventana. Luego debemos actualizar su posición constantemente dada la velocidad y el ángulo de salida.
4. El cohete, una vez se salga de la ventana debe inactivarse.
Veamos caso por caso
1. El cohete se dispara cuando el usuario presiona la tecla de disparo. Hay que validar que el usuario no mantenga presionada esa tecla constantemente, debe soltarla.
Necesitamos guardar el estado anterior del teclado y solo se llamaría al método que dispara el cohete cuando el usuario presione y posteriormente suelte la tecla de disparo.
Se crea un nuevo atributo en la clase Game1
| //Almacena el estado anterior del teclado KeyboardState anteriorTeclado; |
y este es el código que lanza el disparo, el cual se ubica en Update()
//Dispara un solo cohete.
//Chequea si el usuario mantiene presionada la tecla de disparo por lo que hace caso omiso a eso,
//el usuario debe presionar y soltar la tecla de disparo para lanzar mas misiles.
if (estadoTeclado.IsKeyDown(Keys.Space) && anteriorTeclado.IsKeyUp(Keys.Space))
DispararCohete(); //Se actualiza el estado del teclado
anteriorTeclado = estadoTeclado; |
2. Si un cohete está en estado inactivo, entonces es candidato a ser usado cuando el usuario presione la tecla de disparar.
Hay un método llamado DispararCohete que activa el cohete e inicializa su comportamiento (velocidad, ángulo de salida, ángulo de rotación del sprite)
//Metodo llamado desde Update
public void DispararCohete()
{
//Busca un cohete inactivo
foreach (ObjetosJuego cohete in cohetes)
{
//Si encuentra un cohete inactivo
if (cohete.esta_activo == false)
{
//Activa el cohete (para que sea dibujado)
cohete.esta_activo = true; //El cohete inicia en la posición del lanzador
cohete.posicion = lanzador.posicion; //En que dirección y a que velocidad sale el cohete. Hay que tener en cuenta la rotación del lanzador
cohete.velocidad = new Vector2((float)Math.Cos(lanzador.rotacion), (float)Math.Sin(lanzador.rotacion)) * 4.0f; //Rote el sprite del cohete para que coincida con el del lanzador
cohete.rotacion = lanzador.rotacion; return;
}
}
} |
3. El cohete una vez lanzado comienza a desplazarse por la ventana. Luego debemos actualizar su posición constantemente dada la velocidad y el ángulo de salida con esta instrucción en Update()
| //Actualiza los cohetes ActualizaCohetes(); |
Ese método es:
//Método llamado desde Update
public void ActualizaCohetes()
{
//Va de cohete en bala
foreach (ObjetosJuego cohete in cohetes)
if (cohete.esta_activo == true)
{
//Solo es actualizar la velocidad porque el vector se actualiza gracias a esa magnitud
cohete.posicion += cohete.velocidad; //Si el cohete se sale de la pantalla entonces se desactiva
if (rectFondo.Contains(new Point((int)cohete.posicion.X, (int)cohete.posicion.Y)) == false)
cohete.esta_activo = false;
}
}
|
Y ese método resuelve el punto 4. El cohete, una vez se salga de la ventana debe inactivarse.
El código completo de Game1.cs es el siguiente:
using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; using Microsoft.Xna.Framework.Net; using Microsoft.Xna.Framework.Storage; namespace PrimerJuego
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch; //Contenedor de los sprite
Texture2D Fondo; //Variable de tipo textura que tendrá el fondo
Rectangle rectFondo; //Un rectángulo donde estará la textura del fondo ObjetosJuego lanzador; //El lanzador
ObjetosJuego baselanzador; //Base del lanzador //Almacena el estado anterior del ratón
MouseState anteriorRaton; //Almacena el estado anterior del teclado
KeyboardState anteriorTeclado; //Cohetes
ObjetosJuego[] cohetes;
const int MAXCOHETES = 7; public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
} /// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here base.Initialize();
} /// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);// TODO: use this.Content to load your game content here //Utiliza genéricos <> por eso el uso de <> En C++ se llaman plantillas. Content carga el contenido binario.
Fondo = Content.Load<Texture2D>("Imagenes\\FondoJuego"); //El rectángulo en el que estará contenido el fondo
rectFondo = new Rectangle(0, 0, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height); //Cargar e inicializar el Lanzador
lanzador = new ObjetosJuego(Content.Load<Texture2D>("Imagenes\\Lanzador")); //No es necesaria la extension .tga
lanzador.posicion = new Vector2(160, 530); //Posicion del lanzador //Cargar e inicializar la base
baselanzador = new ObjetosJuego(Content.Load<Texture2D>("Imagenes\\Base")); //No es necesaria la extension .tga
baselanzador.posicion = new Vector2(70, 560); //Posicion de la base del lanzador //Cargar e inicializar los cohetes
cohetes = new ObjetosJuego[MAXCOHETES];
for (int Cont = 0; Cont < MAXCOHETES; Cont++)
cohetes[Cont] = new ObjetosJuego(Content.Load<Texture2D>("Imagenes\\Cohete"));} /// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
} /// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit(); // Código para leer el estado del Gamepad de una XBOX. Se considera que hay un jugador al menos.
GamePadState estadoControl = GamePad.GetState(PlayerIndex.One); //Como hay que tener en cuenta los controles del XBOX llamados thumbstick, se
//observa que botón del XBOX oprimió, eso genera una constante algo grande por lo que se multiplica por 0.1
lanzador.rotacion += estadoControl.ThumbSticks.Left.X * 0.1f;//Esta es una compilación condicional. El teclado y el mouse solo son leídos si la aplicación ejecuta en un PC
#if !XBOX
//Código para leer el teclado
KeyboardState estadoTeclado = Keyboard.GetState();
if (estadoTeclado.IsKeyDown(Keys.Left)) lanzador.rotacion -= 0.1f; //Gira el lanzador hacia la izquierda
if (estadoTeclado.IsKeyDown(Keys.Right)) lanzador.rotacion += 0.1f; //Gira el lanzador hacia la derecha //Dispara un solo cohete.
//Chequea si el usuario mantiene presionada la tecla de disparo por lo que hace caso omiso a eso,
//el usuario debe presionar y soltar la tecla de disparo para lanzar mas misiles.
if (estadoTeclado.IsKeyDown(Keys.Space) && anteriorTeclado.IsKeyUp(Keys.Space))
DispararCohete(); //Se actualiza el estado del teclado
anteriorTeclado = estadoTeclado; //Código para leer el ratón
MouseState Raton = Mouse.GetState(); //Si movió el ratón entonces el lanzador cambia de ángulo
if (Raton != anteriorRaton)
{
int DiferX = Raton.X - anteriorRaton.X; //La diferencia entre la anterior posición del ratón y la nueva.
lanzador.rotacion += (float)DiferX / 100; //El movimiento en X del ratón cambia el ángulo del lanzador
} //Se actualiza el estado del ratón
anteriorRaton = Raton;
#endif //Esta instrucción limita entre 0 y 90 grados el giro del lanzador. Recordar que se hace uso de radianes.
lanzador.rotacion = MathHelper.Clamp(lanzador.rotacion, -MathHelper.PiOver2, 0); //Actualiza los cohetes
ActualizaCohetes(); base.Update(gameTime);
} /// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);// TODO: Add your drawing code here //Se inicia el spritebatch (Entre Begin y End se dibuja en cada unidad de tiempo los sprites)
spriteBatch.Begin(SpriteBlendMode.AlphaBlend); //Dibujar el fondo combinando el color blanco con el fondo
spriteBatch.Draw(Fondo, rectFondo, Color.White); //Dibuja la base del lanzador combinando con blanco
spriteBatch.Draw(baselanzador.sprite, baselanzador.posicion, Color.White); /* Dibuja el lanzador como tal, estos son los parámetros
* Parámetro 1: El sprite del lanzador
* Parámetro 2: La posición del lanzador
* Parámetro 3: null porque es solo una imagen (no una sucesión de estas
* Parámetro 4: Color.White, combinación de color
* Parámetro 5: Angulo de rotación por defecto
* Parámetro 6: Centro, el centro del sprite para girarlo después
* Parámetro 7: Escala a dibujar
* Parametro 8: Dibujar tal como es el sprite (no reflejo vertical ni horizontal)
* Parámetro 9: Ponerlo en la capa superior */
spriteBatch.Draw(lanzador.sprite, lanzador.posicion, null, Color.White, lanzador.rotacion, lanzador.centro, 0.6f, SpriteEffects.None, 0); //Dibuja cada cohete
foreach (ObjetosJuego cohete in cohetes)
if (cohete.esta_activo == true)
spriteBatch.Draw(cohete.sprite, cohete.posicion, null, Color.White, cohete.rotacion, cohete.centro, 0.2f, SpriteEffects.None, 0); //Finaliza el "ciclo" de los sprite
spriteBatch.End(); base.Draw(gameTime);
} //Metodo llamado desde Update
public void DispararCohete()
{
//Busca un cohete inactivo
foreach (ObjetosJuego cohete in cohetes)
{
//Si encuentra un cohete inactivo
if (cohete.esta_activo == false)
{
//Activa el cohete (para que sea dibujado)
cohete.esta_activo = true; //El cohete inicia en la posición del lanzador
cohete.posicion = lanzador.posicion; //En que dirección y a que velocidad sale el cohete. Hay que tener en cuenta la rotación del lanzador
cohete.velocidad = new Vector2((float)Math.Cos(lanzador.rotacion), (float)Math.Sin(lanzador.rotacion)) * 4.0f; //Rote el sprite del cohete para que coincida con el del lanzador
cohete.rotacion = lanzador.rotacion;
return;
}
}
} //Método llamado desde Update
public void ActualizaCohetes()
{
//Va de cohete en bala
foreach (ObjetosJuego cohete in cohetes)
if (cohete.esta_activo == true)
{
//Solo es actualizar la velocidad porque el vector se actualiza gracias a esa magnitud
cohete.posicion += cohete.velocidad; //Si el cohete se sale de la pantalla entonces se desactiva
if (rectFondo.Contains(new Point((int)cohete.posicion.X, (int)cohete.posicion.Y)) == false)
cohete.esta_activo = false;
}
}} } |
Este es el resultado, cada vez que se presiona la tecla espaciadora se lanza un cohete hasta MAXCOHETES

| Volver al Inicio | Célula Microsoft. Universidad Libre |