XNA 3D model collision is inaccurate

Posted by Daniel Lopez on Game Development See other posts from Game Development or by Daniel Lopez
Published on 2011-11-26T23:14:49Z Indexed on 2011/11/27 2:03 UTC
Read the original article Hit count: 444

Filed under:
|
|
|
|

I am creating a classic game in 3d that deals with asteriods and you have to shoot them and avoid being hit from them. I can generate the asteroids just fine and the ship can shoot bullets just fine. But the asteroids always hit the ship even it doesn't look they are even close. I know 2D collision very well but not 3D so can someone please shed some light to my problem. Thanks in advance.

Code For ModelRenderer:

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;

namespace _3D_Asteroids
{
    class ModelRenderer
    {
        private float aspectratio;
        private Model model;
        private Vector3 camerapos;
        private Vector3 modelpos;
        private Matrix rotationy;
        float radiansy = 0;
        private bool isalive;

        public ModelRenderer(Model m, float AspectRatio, Vector3 initial_pos, Vector3 initialcamerapos)
        {
            isalive = true;
            model = m;
            if (model.Meshes.Count == 0)
            {
                throw new Exception("Invalid model because it contains zero meshes!");
            }
            modelpos = initial_pos;
            camerapos = initialcamerapos;
            aspectratio = AspectRatio;
            return;
        }
        public float RadiusOfSphere
        {

            get
            {
                return model.Meshes[0].BoundingSphere.Radius;
            }
        }
        public BoundingBox BoxBounds
        {
            get
            {

                return BoundingBox.CreateFromSphere(model.Meshes[0].BoundingSphere);
            }
        }

        public BoundingSphere SphereBounds
        {

            get
            {

                return model.Meshes[0].BoundingSphere;
            }
        }
        public Vector3 CameraPosition
        {
            set
            {
                camerapos = value;
            }
            get
            {
                return camerapos;
            }

        }
        public bool IsAlive
        {
            get
            {
                return isalive;
            }
        }
        public Vector3 ModelPosition
        {
            set
            {
                modelpos = value;
            }
            get
            {
                return modelpos;
            }
        }
        public void RotateY(float radians)
        {
            radiansy += radians;
            rotationy = Matrix.CreateRotationY(radiansy);
        }
        public Matrix RotationY
        {
            set
            {
                rotationy = value;
            }
            get
            {
                return rotationy;
            }
        }
        public float AspectRatio
        {
            set
            {
                aspectratio = value;
            }
            get
            {
                return aspectratio;
            }
        }
        public void Kill()
        {
            isalive = false;
        }
        public void Draw(float scale)
        {
            Matrix world;
            if (rotationy == new Matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
            {
                world = Matrix.CreateScale(scale) * Matrix.CreateTranslation(modelpos);
            }
            else
            {
                world = rotationy * Matrix.CreateScale(scale) * Matrix.CreateTranslation(modelpos);
            }
            Matrix view = Matrix.CreateLookAt(camerapos, Vector3.Zero, Vector3.Up);
            Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), this.AspectRatio, 1f, 100000f);

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = world;
                    effect.View = view;
                    effect.Projection = projection;

                }
                mesh.Draw();
            }
        }
        public void Draw()
        {
            Matrix world;
            if (rotationy == new Matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
            {
                world = Matrix.CreateTranslation(modelpos);
            }
            else
            {
                world = rotationy * Matrix.CreateTranslation(modelpos);
            }
            Matrix view = Matrix.CreateLookAt(camerapos, Vector3.Zero, Vector3.Up);
            Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), this.AspectRatio, 1f, 100000f);

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = world;
                    effect.View = view;
                    effect.Projection = projection;

                }
                mesh.Draw();
            }
        }
    }

Code For Game1:

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;

namespace _3D_Asteroids
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        int score = 0, lives = 5;
        SpriteBatch spriteBatch;
        GameState gstate = GameState.OnMenuScreen;
        Menu menu = new Menu(Color.Yellow, Color.White);
        SpriteFont font;
        Texture2D background;
        ModelRenderer ship;
        Model b, a;
        List<ModelRenderer> bullets = new List<ModelRenderer>();
        List<ModelRenderer> asteriods = new List<ModelRenderer>();
        float time = 0.0f;
        int framecount = 0;
        SoundEffect effect;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 796;
            graphics.ApplyChanges();
            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);
            font = Content.Load<SpriteFont>("Fonts\\Lucida Console");
            background = Content.Load<Texture2D>("Textures\\B1_stars");
            Model p1 = Content.Load<Model>("Models\\p1_wedge");
            b = Content.Load<Model>("Models\\pea_proj");
            a = Content.Load<Model>("Models\\asteroid1");
            effect = Content.Load<SoundEffect>("Audio\\tx0_fire1");
            ship = new ModelRenderer(p1, GraphicsDevice.Viewport.AspectRatio, new Vector3(0, 0, 0), new Vector3(0, 0, 9000));
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {

        }

        /// <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)
        {

            KeyboardState state = Keyboard.GetState(PlayerIndex.One);
            switch (gstate)
            {
                case GameState.OnMenuScreen:
                {
                    if (state.IsKeyDown(Keys.Enter))
                    {
                        switch (menu.SelectedChoice)
                        {
                            case MenuChoices.Play:
                            {
                                gstate = GameState.GameStarted;
                                break;
                            }
                            case MenuChoices.Exit:
                            {
                                this.Exit();
                                break;
                            }
                        }

                    }
                    if (state.IsKeyDown(Keys.Down))
                    {
                        menu.MoveSelectedMenuChoiceDown(gameTime);
                    }
                    else if(state.IsKeyDown(Keys.Up))
                    {
                        menu.MoveSelectedMenuChoiceUp(gameTime);
                    }
                    else
                    {
                        menu.KeysReleased();
                    }

                    break;
                }
                case GameState.GameStarted:
                {
                    foreach (ModelRenderer bullet in bullets)
                    {
                        if (bullet.ModelPosition.X < (ship.ModelPosition.X + 4000) && bullet.ModelPosition.Z < (ship.ModelPosition.X + 4000) && bullet.ModelPosition.X > (ship.ModelPosition.Z - 4000) && bullet.ModelPosition.Z > (ship.ModelPosition.Z - 4000))
                        {
                            bullet.ModelPosition += (bullet.RotationY.Forward * 120);
                        }
                        else if (collidedwithasteriod(bullet))
                        {
                            bullet.Kill();
                        }
                        else
                        {

                            bullet.Kill();
                        }

                    }
                    foreach (ModelRenderer asteroid in asteriods)
                    {
                        if (ship.SphereBounds.Intersects(asteroid.BoxBounds))
                        {
                            lives -= 1;
                            asteroid.Kill(); // This always hits no matter where the ship goes.
                        }
                        else
                        {
                            asteroid.ModelPosition -= (asteroid.RotationY.Forward * 50);
                        }
                    }
                    for (int index = 0; index < asteriods.Count; index++)
                    {
                        if (asteriods[index].IsAlive == false)
                        {
                            asteriods.RemoveAt(index);
                        }
                    }
                    for (int index = 0; index < bullets.Count; index++)
                    {
                        if (bullets[index].IsAlive == false)
                        {
                            bullets.RemoveAt(index);
                        }

                    }
                    if (state.IsKeyDown(Keys.Left))
                    {
                        ship.RotateY(0.1f);
                        if (state.IsKeyDown(Keys.Space))
                        {
                            if (time < 17)
                            {
                                firebullet();
                                //effect.Play();
                            }
                        }
                        else
                        {
                            time = 0;
                        }
                    }
                    else if (state.IsKeyDown(Keys.Right))
                    {
                        ship.RotateY(-0.1f);
                        if (state.IsKeyDown(Keys.Space))
                        {
                            if (time < 17)
                            {
                                firebullet();
                                //effect.Play();
                            }
                        }
                        else
                        {
                            time = 0;
                        }
                    }
                    else if (state.IsKeyDown(Keys.Up))
                    {
                        ship.ModelPosition += (ship.RotationY.Forward * 50);
                        if (state.IsKeyDown(Keys.Space))
                        {
                            if (time < 17)
                            {
                                firebullet();
                                //effect.Play();
                            }
                        }
                        else
                        {
                            time = 0;
                        }
                    }
                    else if (state.IsKeyDown(Keys.Space))
                    {
                        time += gameTime.ElapsedGameTime.Milliseconds;
                        if (time < 17)
                        {
                            firebullet();
                            //effect.Play();
                        }
                    }

                    else
                    {
                        time = 0.0f;
                    }
                    if ((framecount % 60) == 0)
                    {
                        createasteroid();
                        framecount = 0;
                    }
                    framecount++;
                    break;
                }
            }

            base.Update(gameTime);
        }
        void firebullet()
        {
            if (bullets.Count < 3)
            {
                ModelRenderer bullet = new ModelRenderer(b, GraphicsDevice.Viewport.AspectRatio, ship.ModelPosition, new Vector3(0, 0, 9000));            
                bullet.RotationY = ship.RotationY;
                bullets.Add(bullet);
            }

        }
        void createasteroid()
        {
            if (asteriods.Count < 2)
            {
                Random random = new Random();
                float z = random.Next(-13000, -11000);
                float x = random.Next(-9000, -8000);
                Random random2 = new Random();
                int degrees = random.Next(0, 45);
                float radians = MathHelper.ToRadians(degrees);
                ModelRenderer asteroid = new ModelRenderer(a, GraphicsDevice.Viewport.AspectRatio, new Vector3(x, 0, z), new Vector3(0,0, 9000));
                asteroid.RotateY(radians);
                asteriods.Add(asteroid);
            }
        }
        /// <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);

            switch (gstate)
            {
                case GameState.OnMenuScreen:
                {
                    spriteBatch.Begin();
                    spriteBatch.Draw(background, Vector2.Zero, Color.White);
                    menu.DrawMenu(ref spriteBatch, font, new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2) - new Vector2(50f), 100f);
                    spriteBatch.End();
                    break;
                }
                case GameState.GameStarted:
                {
                    spriteBatch.Begin();
                    spriteBatch.Draw(background, Vector2.Zero, Color.White);
                    spriteBatch.DrawString(font, "Score: " + score.ToString() + "\nLives: " + lives.ToString(), Vector2.Zero, Color.White);
                    spriteBatch.End();
                    ship.Draw();
                    foreach (ModelRenderer bullet in bullets)
                    {
                        bullet.Draw();
                    }
                    foreach (ModelRenderer asteroid in asteriods)
                    {
                        asteroid.Draw(0.1f);
                    }
                    break;
                }
            }

            base.Draw(gameTime);
        }
        bool collidedwithasteriod(ModelRenderer bullet)
        {
            foreach (ModelRenderer asteroid in asteriods)
            {
                if (bullet.SphereBounds.Intersects(asteroid.BoxBounds))
                {
                    score += 10;
                    asteroid.Kill();
                    return true;
                }
            }
            return false;
        }
    }
}

}

© Game Development or respective owner

Related posts about XNA

Related posts about c#