Jump to content
  • Advertisement
Sign in to follow this  
suntzu2007

3D FPS: Bullet Collision Detection

This topic is 3210 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all, ( Apologies if this topic was addressed elsewhere, however my search remained fruitless. ) I'm relatively new to XNA and am working on a simple 3D FPS. The problem I am having is that my collision detection system does not seem to work for the bullets that I am firing. In other words, shooting at a target does not trigger a collision detection. For every target, I create a bounding sphere by extracting the bounding sphere of each mesh and then merging them. i.e.
public override void Draw(GameTime gameTime, Matrix projection, Matrix view)
        {
            Matrix[] transforms = new Matrix[_model.Bones.Count];

            _model.CopyAbsoluteBoneTransformsTo(transforms);
            bounds = new BoundingSphere();
            foreach (ModelMesh mesh in _model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.Projection = Projection;
                    effect.View = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up);
                    effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateRotationY(600.0f) * Matrix.CreateRotationX(300.0f) *
                          Matrix.CreateScale(1.9f) * Matrix.CreateTranslation(modelPosition);

                }
                bounds = BoundingSphere.CreateMerged(bounds, mesh.BoundingSphere);
                mesh.Draw();
            }
            bounds = bounds.Transform(Matrix.CreateTranslation(modelPosition));
        }

Bounding spheres are created for each bullet using the same method. Firing a bullet is achieved by creating a new bullet at the camera's position, and then propagating the bullet in a straight line along the Z axis by incrementing the Z-axis co-ordinates at each "Draw". At the beginning of "Draw", I am checking whether the bullet is colliding with an object (or whether it as too far from the game world to be drawn). If the bullet collides, points should be increased etc and the bullet is removed from the game world.
public override void Draw(GameTime gameTime, Matrix projection, Matrix view)
        {
            Matrix[] transforms = new Matrix[_model.Bones.Count];
            if (world.BulletCollision(this.GetBounds()))
            {
                redundant = true;
                return;
            }
            if (propagate)
            {
                modelPosition.Z += bulletSpeed;
            }
            if (modelPosition.Z > 1000.0f)
            {
                redundant = true;
            }
            _model.CopyAbsoluteBoneTransformsTo(transforms);
            bounds = new BoundingSphere();
            foreach (ModelMesh mesh in _model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.Projection = Projection;
                    effect.View = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up);
                    effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateRotationY(600.0f) * Matrix.CreateScale(0.5f) * Matrix.CreateTranslation(modelPosition);

                }
                bounds = BoundingSphere.CreateMerged(bounds, mesh.BoundingSphere);
                mesh.Draw();
            }
            bounds = bounds.Transform(Matrix.CreateScale(1.0f) * Matrix.CreateTranslation(modelPosition));
        }

My collision detection looks is as follows:
        public bool BulletCollision(BoundingSphere bounds)
        {
            foreach (ISceneObject obj in worldObjects)
            {
                if (obj.GetBounds().Radius == -1)
                {
                    continue;
                }

                if (obj.GetType().ToString() != "shootingrange.GameWorld.Building")
                {
                    continue;
                }
                Console.WriteLine(bounds.Center);
                if (obj.GetBounds().Contains(bounds) == ContainmentType.Contains ||
                    obj.GetBounds().Intersects(bounds))
                {
                    Game.points++;
                    return true;
                }
            }
            return false;
        }

Any help would be greatly appreciated :-)

Share this post


Link to post
Share on other sites
Advertisement
Hi!

I would say your problem is the bullet is moving too fast for this simple collision detection to work. The bullet is in front of a character in one frame, and already behind it in the next, so the collision never registers.

A simple way of fixing this is in addition to the bounding sphere test, doing a raycast forward from the bullet to check for objects the bullet is about to hit. If they are closer than the distance the bullet moves in one frame, a hit should occur.

Share this post


Link to post
Share on other sites
Hi Morrandir,

Thanks for your reply. What do you mean by "raycast"? I tried moving the bullet at a slower speed and it didn't work either (maybe the bullet was still to fast though).

Also, would you recommend another technique instead?

Thanks,

Share this post


Link to post
Share on other sites
Also by "raycast" he means calculating a line from point A, which is the position in the first frame, to point B, which is the position in the second frame.

Andy

Share this post


Link to post
Share on other sites
Just a stab in the dark;


if (obj.GetType().ToString() != "shootingrange.GameWorld.Building")
{
continue;
}



Sure that comparison should be !=, and not ==? ('Building' didn't sound like the name of what your target objects would be to me)

Share this post


Link to post
Share on other sites
Ray casting means testing if a ray intersects an object. In this case you create a ray starting form the bullet position, in the direction it is traveling in, and test for collision with all geometry in the scene (respectively with their bounding boxes).

I'm not too well versed in XNA, but it seems you can use BoundingSphere.Intersects(Ray ray) for the testing.

Also, for testing if this really is the problem, try to decrease the bullet speed dramatically, so that there is no chance for a bullet to skip an enemy, than shoot it at a stationary enemy and see if the collision registers. Ideally you should also draw the bullet and enemy bounding spheres so you can inspect them visually.

Share this post


Link to post
Share on other sites
Hi all,

Thanks very much for your help.

@NineYearCycle and Morrandir,
Trying out your tips now :-) Thanks!

@ mattd
Ah yes, I had used a building as a target for testing purposes (larger than the NPC in case it had to do with a misplaced bounding sphere). Thanks!

Share this post


Link to post
Share on other sites
Ok, I finally managed to implement the Ray casting. However it's not quite working out. The only times that I seem to be able to "hit" and object is when I move backwards while shooting. I know it has "something" to do with the camera, however I can't quite figure out what it is. Also, once moving backwards when shooting, I hit the target even if pointing into opposite directions.

What I did was draw a ray at every "Draw" of the bullet (Please see the code below).

I've had problems drawing the ray, so, I just print the co-ordinates for every "hit". Here an example:

shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.075377 Z:-93.97278} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.075377 Z:-93.97278} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.075377 Z:-93.97278} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.075377 Z:-98.97278} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.075377 Z:-93.97278} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:3.94132 Z:-93.91934} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.425062 Y:3.94132 Z:-93.95586} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.22722 Y:3.941321 Z:-93.95522} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.029201 Y:4.008436 Z:-93.98106} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:111.8863 Y:4.20871 Z:-244.3544} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:111.8863 Y:4.20871 Z:-244.3544} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:111.8863 Y:4.20871 Z:-244.3544} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:111.8863 Y:4.20871 Z:-244.3544} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:133.2445 Y:4.275099 Z:-289.479} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:138.7093 Y:4.341291 Z:-300.7091} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:119.8614 Y:4.341294 Z:-266.0376} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:181.6297 Y:4.341298 Z:-201.135} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:172.7936 Y:4.3413 Z:-190.0566} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:168.4389 Y:4.341295 Z:-184.4362} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:150.1676 Y:4.275097 Z:-167.2586} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:124.8685 Y:4.275102 Z:-152.5962} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:97.7438 Y:4.208708 Z:-140.2434} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:69.82182 Y:4.142137 Z:-124.0002} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:40.14538 Y:4.142137 Z:-114.0002} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:10.46892 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]
shootingrange.GameWorld.Target Pos: {X:10 Y:-50 Z:-50}, Ray: [ {X:5.522848 Y:4.142136 Z:-94.00018} ]



Code:

Bullet.cs

public override void Draw(GameTime gameTime, Matrix projection, Matrix view)
{
Matrix[] transforms = new Matrix[_model.Bones.Count];

Vector3 nearSource = new Vector3(cameraPosition.X, cameraPosition.Y, 0.0f);
Vector3 farSource = new Vector3(cameraPosition.X, cameraPosition.Y, 1.0f);
Vector3 nearPoint = graphics.GraphicsDevice.Viewport.Unproject(nearSource, projection, Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up), Matrix.Identity);
Vector3 farPoint = graphics.GraphicsDevice.Viewport.Unproject(farSource, projection, Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up), Matrix.Identity);
Vector3 direction = farPoint - nearPoint;
direction.Normalize();

// and then create a new ray using nearPoint as the source.
Ray ray = new Ray(nearPoint, farPoint);

// if (ray.Intersects(this.GetBounds()).HasValue == true)
// {
// }


if (world.BulletCollision(ray))
{
redundant = true;
return;
}
if (propagate)
{
modelPosition.Z += bulletSpeed;
}
if (modelPosition.Z > 1000.0f)
{
redundant = true;
}
_model.CopyAbsoluteBoneTransformsTo(transforms);
bounds = new BoundingSphere();
foreach (ModelMesh mesh in _model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.Projection = Projection;
effect.View = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up);
effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateRotationY(600.0f) * Matrix.CreateScale(0.5f) * Matrix.CreateTranslation(modelPosition);

}
bounds = BoundingSphere.CreateMerged(bounds, mesh.BoundingSphere);
mesh.Draw();
}
bounds = bounds.Transform(Matrix.CreateScale(1.0f) * Matrix.CreateTranslation(modelPosition));
}





public bool BulletCollision(Ray ray)
{
foreach (ISceneObject obj in worldObjects)
{
if (obj.GetBounds().Radius == -1)
{
continue;
}

if (obj.GetType().ToString() != "shootingrange.GameWorld.Target")
{
continue;
}

// Use raycasting to determine whether the bullet hits its target
if (ray.Intersects(obj.GetBounds()).HasValue == true)
{
Game.points++;
Target t = (Target)obj;
Console.WriteLine(obj.GetType() + " Pos: " + t.GetPosition() + ", Ray: [ " + ray.Position + " ]");
return true;
}
}
return false;
}



Any more tips? :D Thanks :-)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!