This project is read-only.

Controlar colisiones con un Bounding Box en 2D

En todos los juegos o por lo menos en la inmensa mayoría de ellos llega un momento en el que tenemos que detectar cuando dos objetos del juego consiguen colisionar.

En XNA, la verdad es que lo tenemos bastante fácil. Están implementadas unas estructuras que permiten a través de llamadas a métodos saber si dos objetos colisionan en un determinado momento y de esta forma podemos avanzar más rápidamente en el desarrollo de nuestros juegos.

XNA nos proporciona tres tipos de volúmenes en los cuales encerrar nuestras estructuras ya sean sprites o modelos 3D, son: bounding box, bounding sphere y bounding frustrum.

El bounding box lo que hace es encerrar un modelo dentro de una caja que está alineada con los ejes x,y,z. Este tipo de volumen es el indicado sobre todo para objetos que se adapten bien a formas rectangulares, ya que si por ejemplo metemos una pelota dentro de una caja nos quedan partes del volumen sin ocupar y cuando veamos el choque quedará mal porque veremos un espacio entre la pelota y la otra figura con la que este colisionando. También hay que tener en cuenta que si rotamos la figura también hay que rotar la caja porqué si no este método no funciona.

El bounding sphere como su nombre indica es una esfera y al contrario que en la estructura anterior lo que puede ocurrir es que existan partes del modelo que se salgan de la esfera. Una ventaja que tenemos con esta estructura es que no tenemos la necesidad de rotarla nunca por lo que resulta más rápida que la estructura anterior.

Por último también tenemos la estructura bounding frustrum que controla el espacio que visiona la cámara. Esta estructura no controla colisiones, sino que sirve para determinar si se ve el objeto o no y de esta forma no tenerlos que dibujar.

Voy a dejar un ejemplillo de cómo comprobar si se chocan dos cajas en 2D, aunque el bounding box está pensado para objetos 3D si al constructor le damos los valores correspondientes a la X y a la Y, y cero a la Z, ya lo tenemos adaptado al 2D.

El ejemplillo es un poco "cutre-rapidillo" pero cualquier duda dejar un comentario en los foros e intentare explicarlo un poco mejor. A continuación tenéis el código y adjunto el proyecto para probarlo.


using System;
using System.Collections.Generic;
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.Net;
using Microsoft.Xna.Framework.Storage;

namespace DemoColisiones
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        ContentManager content;

        Texture2D coche;
        Texture2D muro;

        BoundingBox b_coche;
        BoundingBox b_muro;

        Rectangle r_coche;
        Rectangle r_muro;

        Vector2 posicion_coche;



        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            content = new ContentManager(Services);
            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
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 600;
            graphics.IsFullScreen = false;
            graphics.ApplyChanges();

            posicion_coche = new Vector2(600, 220);

            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);
            coche = Content.Load<Texture2D>("cochecejo");
            muro = Content.Load<Texture2D>("murillo");
            // TODO: use this.Content to load your game content here
        }

        /// <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();

            r_muro = new Rectangle(0, 200, muro.Width, muro.Height);
            b_muro = new BoundingBox(new Vector3(0,200, 0), new Vector3(muro.Width, 200 + muro.Height, 0));
            
            r_coche = new Rectangle((int)posicion_coche.X, (int)posicion_coche.Y, coche.Width, coche.Height);
            b_coche = new BoundingBox(new Vector3(posicion_coche, 0), new Vector3(posicion_coche.X + coche.Width, posicion_coche.Y + coche.Height, 0));
            // TODO: Add your update logic here
            elteclado();


            //Aquí es donde se le pregunta al bounding box
            if (b_coche.Intersects(b_muro))
            {
               posicion_coche.X += 10;
	        }

            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)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            spriteBatch.Draw(muro, r_muro, Color.White);
            spriteBatch.Draw(coche, r_coche, Color.White);
            spriteBatch.End();

            // TODO: Add your drawing code here

            base.Draw(gameTime);
        }

        KeyboardState estadoteclado;
        void elteclado()
        {
            estadoteclado = Keyboard.GetState();
            Keys[] teclaspulsada = estadoteclado.GetPressedKeys();

            foreach (Keys ekey in teclaspulsada)
            {
                if (ekey == Keys.Left)
                {
                    posicion_coche.X -= 10;
                    r_coche = new Rectangle((int)posicion_coche.X, (int)posicion_coche.Y, coche.Width, coche.Height);
                }

                if (ekey == Keys.Up)
                {
                    posicion_coche.Y -= 10;
                    r_coche = new Rectangle((int)posicion_coche.X, (int)posicion_coche.Y, coche.Width, coche.Height);
                }

                if (ekey == Keys.Down)
                {
                    posicion_coche.Y += 10;
                    r_coche = new Rectangle((int)posicion_coche.X, (int)posicion_coche.Y, coche.Width, coche.Height);
                }

                if (ekey == Keys.Right)
                {
                    posicion_coche.X += 10;
                    r_coche = new Rectangle((int)posicion_coche.X, (int)posicion_coche.Y, coche.Width, coche.Height);
                }

                if (ekey == Keys.Space)
                    {
                        posicion_coche.X = 600;
                        posicion_coche.Y = 220;
                        r_coche = new Rectangle(600, 220, coche.Width, coche.Height);
                    }
                if (ekey == Keys.Escape)
                    this.Exit();

            }
        }
    }
}



Lo dicho cualquier cuestión dejarla en el Foro he intentaré contestarla.

Descarga proyecto

DemoColisiones.zip

Saludetes

César Reneses Cárcamo
Coordinador Albacete DotNetClub

Last edited Dec 15, 2007 at 2:24 PM by csharp, version 4

Comments

No comments yet.