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
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