• Advertisement
Sign in to follow this  

3D FPS: Bullet Collision Detection

This topic is 3032 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
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
Shouldn't
Ray ray = new Ray(nearPoint, farPoint);
be:
Ray ray = new Ray(nearPoint, direction);
?

Share this post


Link to post
Share on other sites
Ok, I think I'm getting closer to the problem -- my ray co-ordinates don't seem to be correct. I changed them to what I "think" it should be like. Unfortunately now, I don't get any hits at all...

 
Vector3 nearSource = new Vector3(cameraPosition.X, cameraPosition.Y, cameraPosition.Z);
Vector3 farSource = new Vector3(cameraPosition.X, cameraPosition.Y, cameraPosition.Z + 1.0f);

// and then create a new ray using nearPoint as the source.
Vector3 direction = farSource - nearSource;
direction.Normalize();
Ray ray = new Ray(nearSource, direction);



So, basically, I have my gun mounted to my camera. Hence, the bullet will be drawn at the camera's position and will then move in a straight line by incrementing Z at each invocation of Draw(). Similarly, a ray will be drawn for every Draw() of the bullet. Any hints?

Share this post


Link to post
Share on other sites
Hi mattd, yes, thanks :-). I changed the code (see previous post -- sorry, I didn't see your reply until having posted this)

Share this post


Link to post
Share on other sites
Are you sure the ray is moving in the direction of the player ? You can't just add a value to the Z component.
To find the direction vector, you can either use spherical coordinates or simple rotation matrix multiplications.

Anyway, use the DEBUG :D!
Place a breakpoint somewhere reachable once the shot has been cast, and then follow the values and see what's wrong. I always do that, and it always works.

Share this post


Link to post
Share on other sites
What if you just let:

Vector3 nearSource = cameraPosition;
Vector3 farSource = cameraTarget;

Share this post


Link to post
Share on other sites
I'm afraid I don't quite understand why I can't add a value to the Z component? Do you mean for the bullet to move (because it is moving..so..it works) or do you mean for the near/far points of the ray?

Also, I don't quite understand what is wrong with the direction vector? Shouldn't nearSource - farSource give me that? How would I find the direction using matrix multiplication?


With regards to DEBUG -- been trying to do that. According to what I see, it's ok...

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

Share this post


Link to post
Share on other sites
Re: "What if you just let:
Vector3 nearSource = cameraPosition;
Vector3 farSource = cameraTarget;"

Mhh, that produces even odder results...I now score hit-points when shooting in any direction, but only when moving the camera in a circular direction while shooting :D

Share this post


Link to post
Share on other sites
PS
The thing is that

cameraTarget = cameraReference + cameraPosition;

So, when moving, this is what happens:


cameraPitch += (mouseY * 0.4f) * deltaTime;
cameraYaw -= (mouseX * 0.4f) * deltaTime;
cameraPitch = MathHelper.Clamp(cameraPitch, MathHelper.ToRadians(-89.9f), MathHelper.ToRadians(89.9f));
Matrix cameraViewRotationMatrix = Matrix.CreateRotationX(cameraPitch) * Matrix.CreateRotationY(cameraYaw);
Matrix cameraMoveRotationMatrix = Matrix.CreateRotationY(cameraYaw);
Vector3 transformedCameraReference = Vector3.Transform(cameraReference, cameraViewRotationMatrix);
cameraPosition += Vector3.Transform(moveVector, cameraMoveRotationMatrix);
cameraTarget = transformedCameraReference + cameraPosition;
X = cameraPosition.X;
Y = cameraPosition.Y;
Z = cameraPosition.Z;

Share this post


Link to post
Share on other sites
Reading over your code a bit more carefully, since you're using modelPosition to hold the position of the bullet, surely it should be taking part in the ray position/direction calculations?

You'd only use the camera pos/lookat vectors like I mentioned before if you want to do an 'instantaneous' collision test, i.e., the bullet moves at infinite speed.

Try:

Vector3 nearSource = modelPosition;
Vector3 farSource = modelPosition + Vector3(0, 0, bulletSpeed);


EDIT: You realize that by only moving the bullet's Z position, it's always going to move in that fashion, even if the camera is looking in a different direction when it was fired? Perhaps the bullet should have its own direction vector initialized to the camera direction vector when it gets fired, and it moves along this vector, ie. modelPosition += bulletDir * bulletSpeed every Draw. You'd then replace Vector3(0, 0, bulletSpeed) with just bulletDir * bulletSpeed in the above near/far source calculations.

Share this post


Link to post
Share on other sites
Hi Mattd,

Thanks, that makes a lot more sense :-)

However the problem of scoring only when moving backwards persists. That is, the bullets only "hit" as I move backwards. Once one bullet hits, then I can shoot into a completely opposite direction and still score.

I think I got myself in over the head and should have tried something easier. However I'd still like to figure out what is wrong.

The thing is that if the movement issue would have to do with the fact that frames only get drawn as I move, then surely any movement would do.

Here's the entire code.

Bullet.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace shootingrange.GameWorld
{
class Bullet : Camera, ISceneObject
{
private bool propagate = false;
private bool redundant = false;
public static float bulletSpeed = 10.0f;

private BoundingSphere bounds;
private SceneWorld world;
private GraphicsDeviceManager graphics;
private Vector3 bulletDir;

public Bullet(Vector3 modelPosition, SceneWorld world, GraphicsDeviceManager graphics, Vector3 bulletDir)
{
base.SetModelPosition(modelPosition);
base.SetCameraPosition(new Vector3(0.0f, 0.0f, 1.0f));
base.SetCameraReference(new Vector3(0.0f, 0.0f, 1.0f));
base.SetCameraYaw(0.0f);
base.SetCameraPitch(0.0f);
base.SetNearPlane(1.0f);
this.world = world;
this.graphics = graphics;
this.bulletDir = cameraPosition;
}

public override void Initialize(Model model, float aspectRatio)
{
base.Initialize(model, aspectRatio);
}
public override void ActionEvent(int actionEvent)
{
}

public void SetPropagate(bool p)
{
propagate = p;
}

public bool IsRedundant()
{
return redundant;
}

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

Vector3 nearSource = modelPosition;
Vector3 farSource = modelPosition + (bulletDir * bulletSpeed);

// and then create a new ray using nearPoint as the source.
Vector3 direction = farSource - nearSource;
direction.Normalize();
Ray ray = new Ray(nearSource, direction);


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 override void Move(float mouseX, float mouseY, float deltaTime, Vector3 moveVector, SceneWorld world)
{
base.Move(mouseX, mouseY, deltaTime, moveVector, world);
}

public BoundingSphere GetBounds()
{
return bounds;
}
}
}






Target.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace shootingrange.GameWorld
{
class Target : Camera, ISceneObject
{
private BoundingSphere bounds;

public Target(Vector3 modelPosition)
{
base.SetModelPosition(modelPosition);
base.SetCameraPosition(new Vector3(0.0f, 0.0f, 1.0f));
base.SetCameraReference(new Vector3(0.0f, 0.0f, 1.0f));
base.SetCameraYaw(0.0f);
base.SetCameraPitch(0.0f);
base.SetNearPlane(1.0f);

}

public override void Initialize(Model model, float aspectRatio)
{
base.Initialize(model, aspectRatio);
}
public override void ActionEvent(int actionEvent)
{
}

public bool IsRedundant()
{
return false;
}

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.CreateScale(19.0f) * Matrix.CreateTranslation(modelPosition));
}

public Vector3 GetPosition()
{
return modelPosition;
}


public override void Move(float mouseX, float mouseY, float deltaTime, Vector3 moveVector, SceneWorld world)
{
base.Move(mouseX, mouseY, deltaTime, moveVector, world);
}

public BoundingSphere GetBounds()
{
return bounds;
}
}
}




Camera.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace shootingrange
{
class Camera
{
protected Model _model;
protected Matrix[] _boneTransforms;
protected float _angle = 0f;
protected Boolean isMovingZ = false;
protected Boolean isMoving = false;
protected Boolean isMovingX = false;
protected float zScale = 0.3f;
protected float xScale = 0.3f;
protected float yScale = 0.3f;
protected float xPosition = 0, yPosition = 0, zPosition = 0;


protected Vector3 cameraPosition;
protected Vector3 cameraTarget;
protected Vector3 cameraReference;

protected float aspectRatio;
protected float nearPlane;
protected float farPlane;
protected float fieldOfView;

protected Matrix Projection;

protected float cameraYaw;
protected float cameraPitch;

protected Vector3 modelPosition;

public static float X = 0;
public static float Y = 0;
public static float Z = 0;


public virtual void Initialize(Model model, float aspectRatio)
{

_model = model;
_boneTransforms = new Matrix[_model.Bones.Count];
_model.CopyAbsoluteBoneTransformsTo(_boneTransforms);
foreach (ModelMesh mesh in _model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
}
}

farPlane = 100000.0f;
fieldOfView = 45.0f;

cameraTarget = cameraReference + cameraPosition;

this.aspectRatio = aspectRatio;

if (modelPosition == null)
{
modelPosition = new Vector3(0.0f, 0.0f, 0.0f);
}

Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(fieldOfView), aspectRatio, nearPlane, farPlane);
}

public void SetModelPosition(Vector3 modelPosition)
{
this.modelPosition = modelPosition;
}

public void SetCameraPosition(Vector3 cameraPosition)
{
this.cameraPosition = cameraPosition;
}

public void SetCameraReference(Vector3 cameraReference)
{
this.cameraReference = cameraReference;
}

public void SetAspectRation(float aspectRatio)
{
this.aspectRatio = aspectRatio;
}

public void SetCameraYaw(float cameraYaw)
{
this.cameraYaw = cameraYaw;
}

public void SetCameraPitch(float cameraPitch)
{
this.cameraPitch = cameraPitch;
}

public void SetNearPlane(float nearPlane)
{
this.nearPlane = nearPlane;
}

public void SetFarPlane(float farPlane)
{
this.farPlane = farPlane;
}



public void Update(GameTime gameTime)
{
}

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

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.CreateTranslation(modelPosition);
}
mesh.Draw();
}
}

public virtual void Move(float mouseX, float mouseY, float deltaTime, Vector3 moveVector, SceneWorld world)
{
cameraPitch += (mouseY * 0.4f) * deltaTime;
cameraYaw -= (mouseX * 0.4f) * deltaTime;
cameraPitch = MathHelper.Clamp(cameraPitch, MathHelper.ToRadians(-89.9f), MathHelper.ToRadians(89.9f));
Matrix cameraViewRotationMatrix = Matrix.CreateRotationX(cameraPitch) * Matrix.CreateRotationY(cameraYaw);
Matrix cameraMoveRotationMatrix = Matrix.CreateRotationY(cameraYaw);
Vector3 transformedCameraReference = Vector3.Transform(cameraReference, cameraViewRotationMatrix);
cameraPosition += Vector3.Transform(moveVector, cameraMoveRotationMatrix);
cameraTarget = transformedCameraReference + cameraPosition;
X = cameraPosition.X;
Y = cameraPosition.Y;
Z = cameraPosition.Z;
}

public virtual void ActionEvent(int actionEvent)
{
}
}
}





Game.cs (snippet of when I fire)

if (playerGun.CheckAmmo() > 0)
{
gunShotSound.Play();
gunTimer = 0f;

playerGun.ActionEvent(Gun.SHOOT);
// Fire the bullet
totalShots++;
bullet = new Bullet(new Vector3(Camera.X, Camera.Y, Camera.Z), world, graphics, new Vector3(Camera.X, Camera.Y, Camera.Z));
Model bulletModel = Content.Load<Model>(@"Models\\bullet");
bullet.Initialize(bulletModel, (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height);
bullet.SetPropagate(true);
world.AddObject(bullet);

playerGun.ActionEvent(Gun.RECOIL);
while (gunTimer < 0.5f)
{
gunTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
}
gunTimer = 0f;
shotFired = true;
}




I suspect my problem lies somewhere with the camera position, or probably with the fact that my class inherits Camera...

Thanks for your time :-)

Share this post


Link to post
Share on other sites
Quote:
The thing is that if the movement issue would have to do with the fact that frames only get drawn as I move, then surely any movement would do.

How come you only draw frames when you're moving?

This whole 'it only works when I move my camera like this' makes it sound like the bullet's setup and state (direction and position) are still being modified by the camera code or something. (But I guess you figured that out already!)

Anyway, some quick things to try.

In Bullet's constructor:
this.bulletDir = cameraPosition;
Should be:
this.bulletDir = bulletDir;
?

In that Game.cs snippet:
bullet = new Bullet(new Vector3(Camera.X, Camera.Y, Camera.Z), world, graphics, new Vector3(Camera.X, Camera.Y, Camera.Z));
Should be:
Vector3 cameraDir = Camera.cameraTarget - Camera.cameraPosition;
cameraDir.Normalize();
bullet = new Bullet(new Vector3(Camera.X, Camera.Y, Camera.Z), world, graphics, cameraDir);

?

Also, the way you derive from Camera for game objects is, like you've noticed, a little bizarre :) They're game entities, not cameras!

Perhaps if it's still not working, try commenting out the body of Bullet's Move method (assuming it's not actually used). Failing that, stop Bullet deriving from Camera :)

If this still doesn't help, try some visual debugging - get the bullet's location and direction explicitly rendered on screen each frame. Then modify your game loop so that you only advance a frame (i.e., call Bullet's Draw method) once every, say, 500ms.

On that note - having the Bullet's movement code in its Draw method is also a little strange. Consider splitting up the updating and the rendering of game entities into two separate methods.

Anyway, if none of that helps, let me know and I'll try reading your code even closer.

Share this post


Link to post
Share on other sites
I notice you have a FarClip of 100000 but do you ever set the NearClip? I don't see where it is set. If you have a NearClip of 0 and do typical raycast from the camera through the image plane, it is going to get some wacky results.

Share this post


Link to post
Share on other sites
Ok, I think the best thing is for me to start from scratch and re-write the game to avoid having this mess of inheriting the camera. This should a while. I'll get back here and let you know how things are going. Obviously if I come up with a solution, I'll post it here.

Thanks a lot for your help. Much appreciated.

Share this post


Link to post
Share on other sites
Hi guys,

I re-wrote the code to be a lot less messy. I got rid of the whole camera inheritance (a stupid idea of mine to do it in the first place). However I still can't figure out what I'm going wrong. I *think* it has something to do with how I calculate the bounds of my target because as I set the target's scale to 1.9f, I get hits no matter where I shoot (I transform the boundingsphere according to model position and scale -- see below). When I reduce the scale, to say, 1.0f, I don't get any hits. Now, that's as far as I can get. I don't seem to be able to draw the rays either (well, I'm working on this right now). However if somebody would have any clue...? I'm stuck in a one-way street and can't seem to get out of it...

Mattd, your advice has been very helpful .Any more ideas?

Code:
Target.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace shootingrange.GameWorld
{
class Target : BoundsDefinition,ISceneObject
{
private BoundingSphere bounds;
private Model model;
private Vector3 modelPosition;
private Matrix[] boneTransforms;
private readonly float SCALE;
private readonly float X_ROTATION;
private readonly float Y_ROTATION;
private readonly float Z_ROTATION;

public Target(Model model, Vector3 modelPosition)
{
this.modelPosition = modelPosition;
this.model = model;
SCALE = 1.9f;
X_ROTATION = 300.0f;
Y_ROTATION = 600.0f;
Z_ROTATION = 0;
boneTransforms = new Matrix[this.model.Bones.Count];
this.model.CopyAbsoluteBoneTransformsTo(boneTransforms);

}

public void ActionEvent(int actionEvent)
{
}

public bool IsRedundant()
{
return false;
}

public Vector3 GetPosition()
{
return modelPosition;
}


public BoundingSphere GetBounds()
{
base.GetBounds(model, boneTransforms);
bounds = bounds.Transform(Matrix.CreateScale(SCALE) * Matrix.CreateTranslation(modelPosition));
return bounds;
}

public float GetXRotation()
{
return X_ROTATION;
}

public float GetYRotation()
{
return Y_ROTATION;
}

public float GetZRotation()
{
return Z_ROTATION;
}

public Model GetModel()
{
return model;
}

public Matrix[] GetBoneTransforms()
{
return boneTransforms;
}

public float GetScale()
{
return SCALE;
}

public Vector3 GetModelPosition()
{
return modelPosition;
}

public void Update(GameTime time)
{
}

public void SetPosition(Vector3 modelPosition)
{
this.modelPosition = modelPosition;
}
}
}




Bullet.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace shootingrange.GameWorld
{
class Bullet : BoundsDefinition,ISceneObject
{
private bool propagate = false;
private bool redundant = false;
public static float bulletSpeed = 10.0f;
private SceneWorld world;
private Vector3 bulletDir;
private Vector3 offsetModelPosition;

private BoundingSphere bounds;
private Model model;
private Vector3 modelPosition;
private Matrix[] boneTransforms;
private readonly float SCALE;
private readonly float X_ROTATION;
private readonly float Y_ROTATION;
private readonly float Z_ROTATION;

public Bullet(Model model, Vector3 modelPosition, Vector3 bulletDir, SceneWorld world)
{
this.modelPosition = modelPosition;
this.offsetModelPosition = modelPosition;
this.model = model;
SCALE = 0.5f;
X_ROTATION = modelPosition.X;
Y_ROTATION = modelPosition.Y;
Z_ROTATION = modelPosition.Z;
boneTransforms = new Matrix[this.model.Bones.Count];
this.model.CopyAbsoluteBoneTransformsTo(boneTransforms);
this.world = world;
this.bulletDir = bulletDir;
}


public Bullet()
{
SCALE = 1.0f;
X_ROTATION = 0;
Y_ROTATION = 0;
Z_ROTATION = 0;
}

public void ActionEvent(int actionEvent)
{
}

public void SetPropagate(bool p)
{
propagate = p;
}

public Model GetModel()
{
return model;
}

public bool IsRedundant()
{
return redundant;
}

public BoundingSphere GetBounds()
{
base.GetBounds(model, boneTransforms);
bounds = bounds.Transform(Matrix.CreateScale(SCALE) * Matrix.CreateTranslation(modelPosition));
return bounds;
}

public float GetXRotation()
{
return X_ROTATION;
}

public float GetYRotation()
{
return Y_ROTATION;
}

public float GetZRotation()
{
return Z_ROTATION;
}

public Matrix[] GetBoneTransforms()
{
return boneTransforms;
}

public void Update(GameTime time)
{
if (Math.Abs(modelPosition.Z) > Math.Abs((offsetModelPosition.Z * 2.0f)))
{
redundant = true;
return;
}

modelPosition += bulletDir * bulletSpeed;

Vector3 nearSource = modelPosition;
Vector3 farSource = modelPosition + (bulletDir * bulletSpeed);

Vector3 direction = farSource - nearSource;
direction.Normalize();
Ray ray = new Ray(nearSource, direction);


if (world.BulletCollision(ray))
{
redundant = true;
return;
}
//if (propagate)
// {
// modelPosition.Z += bulletSpeed;
// }
}

public float GetScale()
{
return SCALE;
}

public Vector3 GetModelPosition()
{
return modelPosition;
}

public void SetPosition(Vector3 modelPosition)
{
this.modelPosition = modelPosition;
}
}
}




Camera.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using shootingrange.GameWorld;

namespace shootingrange
{
class Camera
{
private Vector3 cameraPosition;
private Vector3 cameraTarget;
private Vector3 cameraReference;

private float aspectRatio;
private float nearPlane;
private float farPlane;
private float fieldOfView;

private Matrix Projection;

private float cameraYaw;
private float cameraPitch;

private SceneWorld world;
private Gun playerGun;

public static float X = 0;
public static float Y = 0;
public static float Z = 0;


public Camera()
{
nearPlane = 1.0f;
farPlane = 100000.0f;
fieldOfView = 45.0f;
cameraPosition = new Vector3(0.0f, 0.0f, 1.0f);
cameraReference = new Vector3(0.0f, 0.0f, 1.0f);
cameraYaw = 0.0f;
cameraPitch = 0.0f;
cameraTarget = cameraReference + cameraPosition;
}

public void Initialize(float aspectRatio, Gun playerGun, SceneWorld world)
{
this.aspectRatio = aspectRatio;
this.world = world;
this.playerGun = playerGun;

Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(fieldOfView), aspectRatio, nearPlane, farPlane);
}


public void SetCameraPosition(Vector3 cameraPosition)
{
this.cameraPosition = cameraPosition;
}

public void SetCameraReference(Vector3 cameraReference)
{
this.cameraReference = cameraReference;
}

public void SetAspectRation(float aspectRatio)
{
this.aspectRatio = aspectRatio;
}

public void SetCameraYaw(float cameraYaw)
{
this.cameraYaw = cameraYaw;
}

public void SetCameraPitch(float cameraPitch)
{
this.cameraPitch = cameraPitch;
}

public void SetNearPlane(float nearPlane)
{
this.nearPlane = nearPlane;
}

public void SetFarPlane(float farPlane)
{
this.farPlane = farPlane;
}

public Vector3 GetCameraPosition()
{
return cameraPosition;
}

public Vector3 GetCameraReference()
{
return cameraReference;
}

public Vector3 GetCameraTarget()
{
return cameraTarget;
}



public void Update(GameTime gameTime)
{
}

public void Draw(GameTime gameTime, Matrix projection, Matrix view)
{
List<ISceneObject> list = world.GetObjects();
for (int i = 0; i < list.Count; i++)
{
ISceneObject obj = list;
if (obj.IsRedundant())
{
world.Remove(obj);
continue;
}
Vector3 modelPosition = obj.GetModelPosition();
Model model = obj.GetModel();
Matrix[] boneTransforms = obj.GetBoneTransforms();
Matrix[] transforms = new Matrix[model.Bones.Count];
// model.CopyAbsoluteBoneTransformsTo(boneTransforms); // MAYBE DO NOT DO THIS

model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.Projection = Projection;
effect.EnableDefaultLighting();
effect.View = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up);
effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateRotationY(obj.GetYRotation()) * Matrix.CreateRotationX(obj.GetXRotation())
* Matrix.CreateRotationZ(obj.GetZRotation()) * Matrix.CreateScale(obj.GetScale()) * Matrix.CreateTranslation(obj.GetModelPosition());
}
mesh.Draw();
}
}

// Player gun needs to be set to camera
// Vector3 position = cameraPosition * cameraTarget;
// position.X = position.X - 15.0f;
// position.Y = position.Y - 50.0f;
// position.Z = position.Z + 50.0f;
// playerGun.SetPosition(position);
DrawGun(gameTime, projection, view);
}

public void Move(float mouseX, float mouseY, float deltaTime, Vector3 moveVector, SceneWorld world)
{
cameraPitch += (mouseY * 0.4f) * deltaTime;
cameraYaw -= (mouseX * 0.4f) * deltaTime;
cameraPitch = MathHelper.Clamp(cameraPitch, MathHelper.ToRadians(-89.9f), MathHelper.ToRadians(89.9f));
Matrix cameraViewRotationMatrix = Matrix.CreateRotationX(cameraPitch) * Matrix.CreateRotationY(cameraYaw);
Matrix cameraMoveRotationMatrix = Matrix.CreateRotationY(cameraYaw);
Vector3 transformedCameraReference = Vector3.Transform(cameraReference, cameraViewRotationMatrix);
cameraPosition += Vector3.Transform(moveVector, cameraMoveRotationMatrix);
cameraTarget = transformedCameraReference + cameraPosition;
X = cameraPosition.X;
Y = cameraPosition.Y;
Z = cameraPosition.Z;
}

public void DrawGun(GameTime gameTime, Matrix projection, Matrix view)
{
Vector3 modelPosition = playerGun.GetModelPosition();
Model model = playerGun.GetModel();
Matrix[] boneTransforms = playerGun.GetBoneTransforms();
Matrix[] transforms = new Matrix[model.Bones.Count];
// model.CopyAbsoluteBoneTransformsTo(boneTransforms); // MAYBE DO NOT DO THIS
Vector3 target = new Vector3(0.0f, 0.0f, 1.0f) + new Vector3(0.0f, 0.0f, 1.0f);
model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.Projection = Projection;
effect.EnableDefaultLighting();
effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), target, Vector3.Up);
effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateRotationY(playerGun.GetYRotation()) * Matrix.CreateRotationX(playerGun.GetXRotation())
* Matrix.CreateRotationZ(playerGun.GetZRotation()) * Matrix.CreateScale(playerGun.GetScale()) * Matrix.CreateTranslation(playerGun.GetModelPosition());
}
mesh.Draw();
}
}
}
}




BoundsDefinition.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace shootingrange
{
class BoundsDefinition
{
public virtual BoundingSphere GetBounds(Model model, Matrix[] boneTransforms)
{
BoundingSphere bounds;
Matrix[] transforms = new Matrix[model.Bones.Count];

model.CopyAbsoluteBoneTransformsTo(transforms);
bounds = new BoundingSphere();
foreach (ModelMesh mesh in model.Meshes)
{
bounds = BoundingSphere.CreateMerged(bounds, mesh.BoundingSphere);
mesh.Draw();
}
return bounds;
}
}
}




Game.cs

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;
using shootingrange.GameWorld;

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

// Create a new scene world. The scene world represents the shooting range
SceneWorld world;

// Describe camera's viewport and behaviour
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45f), (640.0f / 480.0f), 10.0f, 10000.0f);
Matrix view = Matrix.CreateLookAt(new Vector3(20f, 20f, 20f), Vector3.Zero, Vector3.Up);

private GraphicsDevice device;

//==================================
//============Menu=================
private Menu menu;
private bool menuUp = false;
private bool menuDown = false;
private Song backgroundMusic;
//==================================

//==================================
//============Scores=================
public static int points = 0;
private int totalShots = 0;
//==================================

//==================================
//============Assets=================
private SoundEffect menuSelectSound;
private SoundEffect gunShotSound;
private SoundEffect gunReloadSound;
private SoundEffect gunRemoveClipSound;
private SoundEffect step;

private Texture2D backgroundTexture;
private Bullet bullet;
//==================================

private MouseState mStateCashe;
private float gunTimer = 0f;
private float stepTimer = 0f;
private float gunReloadTimer = 0f;
private float interval = 100.0f; // 100 milliseconds
private float intervalReload = 0.5f; // 5 seconds
private float intervalStep = 500.0f;

private Gun playerGun;
private Camera camera;

// Actions
private Boolean reloading = false;
private Boolean shotFired = false;

// Define gamestates
public enum GameState
{
StateMenu,
StateGamePlaying,
StateGameOver,
}
// Initial game state
GameState currentState = GameState.StateMenu;

public Game()
{
graphics = new GraphicsDeviceManager(this);
//graphics.ToggleFullScreen();
Content.RootDirectory = "Content";
world = new SceneWorld();
camera = new Camera();
}



/// <summary>Initializes the game world i.e. adds all
/// objects and characters to the world
/// </summary>
private void initGameWorld()
{
// Create a range target
Target shootingRangeTarget = new Target(Content.Load<Model>(@"Models\target"), new Vector3(10.0f, -50.0f, -50.0f));

// Create the player's gun
playerGun = new Gun(Content.Load<Model>(@"Models\gun"), new Vector3(-10.0f, -55.5f, 60.0f));

// Create game world objects
Building building = new Building(Content.Load<Model>(@"Models\fw43_lowpoly_n1"), new Vector3(300.0f, -50.0f, -250.0f));
Building building2 = new Building(Content.Load<Model>(@"Models\fw43_lowpoly_n1"), new Vector3(-500.0f, -50.0f, -60.0f));
WaterTower waterTower = new WaterTower(Content.Load<Model>(@"Models\Water_Reservoir"), new Vector3(200.0f, -50.0f, 550.0f));
SkyDome dome = new SkyDome(Content.Load<Model>(@"Models\\skysphere"), new Vector3(0.0f, 0.0f, 0.0f));
Tree tree = new Tree(Content.Load<Model>(@"Models\tree"), new Vector3(500.0f, -50.0f, -250.0f));
ConiferousTree tree2 = new ConiferousTree(Content.Load<Model>(@"Models\shareware_output"), new Vector3(500.0f, -50.0f, 650.0f));

// Add game world objects to the game world
world.AddObject(dome);
world.AddObject(shootingRangeTarget);
// Add the player's gun to the game world
//world.AddObject(playerGun);
world.AddObject(building);
world.AddObject(building2);
world.AddObject(waterTower);
world.AddObject(tree);
world.AddObject(tree2);

// Load the background texture
backgroundTexture = Content.Load<Texture2D>(@"Textures\background");
}


/// <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
graphics.PreferredBackBufferWidth = 1024;
graphics.PreferredBackBufferHeight = 768;
graphics.ApplyChanges();
//this.IsMouseVisible = true;
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);
device = graphics.GraphicsDevice;

// Create main menu
menu = new Menu(Color.White, Color.White, Content.Load<SpriteFont>("Arial"), this);

// Add menu items
menu.AddMenuItem("Play", new Vector2(450, 250));
menu.AddMenuItem("About", new Vector2(450, 350));
menu.AddMenuItem("Exit", new Vector2(450, 450));

// Load theme music
backgroundMusic = Content.Load<Song>("MenuThemeSong");
MediaPlayer.Play(backgroundMusic);

// Load menu sounds
menuSelectSound = Content.Load<SoundEffect>("menu-select");

// Load gun sound effects
gunShotSound = Content.Load<SoundEffect>(@"Sound/ak");
gunReloadSound = Content.Load<SoundEffect>(@"Sound/reload_ak");
gunRemoveClipSound = Content.Load<SoundEffect>(@"Sound/remove_clip");
step = Content.Load<SoundEffect>(@"Sound/pl_step4");

initGameWorld();
}

/// <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>
String collision = "-1";
protected override void Update(GameTime gameTime)
{
KeyboardState keys = Keyboard.GetState();
MouseState mouseState = Mouse.GetState();
switch (currentState)
{
#region GameState StateTitleScreen
case GameState.StateMenu:
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();

if (keys.IsKeyDown(Keys.Up) && menuUp)
{
menu.SelectPrevious();
menuSelectSound.Play();
menuUp = false;
}
else if (keys.IsKeyUp(Keys.Up))
{
menuUp = true;
}
if (keys.IsKeyDown(Keys.Down) && menuDown)
{
menu.SelectNext();
menuSelectSound.Play();
menuDown = false;
}
else if (keys.IsKeyUp(Keys.Down))
{
menuDown = true;
}
if (keys.IsKeyDown(Keys.Enter))
{
menu.Clicked(menu.GetSelectedIndex());
}

menu.update(gameTime);
break;
#endregion

#region GameState StateGamePlaying
case GameState.StateGamePlaying:

if (shotFired)
{
float timer = 0;
while (timer < 0.8f)
{
timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
}
playerGun.ActionEvent(Gun.STABLE);
shotFired = false;
}

if (reloading)
{
gunReloadTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (gunReloadTimer > 1.8f)
{
gunReloadTimer = 0;
gunReloadSound.Play();
reloading = false;
playerGun.ActionEvent(Gun.STABLE);
Draw(gameTime);
}
}

if (keys.IsKeyDown(Keys.Escape))
{
Environment.Exit(0);
}
KeyboardState kState = Keyboard.GetState();
MouseState mState = Mouse.GetState();
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
Vector3 moveVector = Vector3.Zero;
if (kState.IsKeyDown(Keys.W))
{
stepTimer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
if (stepTimer > intervalStep)
{
step.Play();
stepTimer = 0;
}

world.Collision(playerGun.GetBounds());


moveVector.Z += 300.0f * deltaTime;

}
if (kState.IsKeyDown(Keys.S))
{
stepTimer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
if (stepTimer > intervalStep)
{
step.Play();
stepTimer = 0;
}
moveVector.Z -= 300.0f * deltaTime;
world.Collision(playerGun.GetBounds());
}
if (kState.IsKeyDown(Keys.A))
{
stepTimer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
if (stepTimer > intervalStep)
{
step.Play();
stepTimer = 0;
}
moveVector.X += 300.0f * deltaTime;

world.Collision(playerGun.GetBounds());

}
if (kState.IsKeyDown(Keys.D))
{
stepTimer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
if (stepTimer > intervalStep)
{
step.Play();
stepTimer = 0;
}
moveVector.X -= 300.0f * deltaTime;
world.Collision(playerGun.GetBounds());
}
if (kState.IsKeyDown(Keys.R))
{
// Re-load
gunTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (gunTimer > intervalReload)
{
playerGun.ActionEvent(Gun.RELOAD);
gunRemoveClipSound.Play();
gunTimer = 0f;
reloading = true;
camera.Draw(gameTime, projection, view);
}

}
if (kState.IsKeyDown(Keys.C))
{
if (Bullet.bulletSpeed == 10.0f)
{
Bullet.bulletSpeed = 0.1f;
}
else
{
Bullet.bulletSpeed = 10.0f;
}
}
if (mouseState.RightButton == ButtonState.Pressed)
{
playerGun.ActionEvent(Gun.AIM_GUN);
}
else if (mouseState.RightButton == ButtonState.Released)
{
playerGun.ActionEvent(Gun.CARRY_GUN);
}
if (mouseState.LeftButton == ButtonState.Pressed)
{
// Fire the gun
gunTimer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
if (gunTimer > interval)
{
if (playerGun.CheckAmmo() > 0)
{
gunShotSound.Play();
gunTimer = 0f;

playerGun.ActionEvent(Gun.SHOOT);
// Fire the bullet
totalShots++;
Vector3 cameraDir = camera.GetCameraTarget() - camera.GetCameraPosition();
cameraDir.Normalize();
bullet = new Bullet(Content.Load<Model>(@"Models\bullet"), camera.GetCameraPosition(), cameraDir, world);
bullet.SetPropagate(true);
world.AddObject(bullet);

playerGun.ActionEvent(Gun.RECOIL);
while (gunTimer < 0.5f)
{
gunTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
}
gunTimer = 0f;
shotFired = true;
}
else
{
while (gunTimer < 1.5f)
{
gunTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
}
gunTimer = 0f;
gunRemoveClipSound.Play();
}

}
}

camera.Move(mState.X - mStateCashe.X, mState.Y - mStateCashe.Y, deltaTime, moveVector, world);
Draw(gameTime);

break;
#endregion


#region GameState StateGameOver
case GameState.StateGameOver:
// Game over screen code goes here
break;
#endregion
}
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.Black);

switch (currentState)
{
#region GameState StateTitleScreen - Drawing code
case GameState.StateMenu:
spriteBatch.Begin();
int screenWidth = device.PresentationParameters.BackBufferWidth;
int screenHeight = device.PresentationParameters.BackBufferHeight;
Rectangle screenRectangle = new Rectangle(0, 0, screenWidth, screenHeight);
spriteBatch.Draw(backgroundTexture, screenRectangle, Color.White);
spriteBatch.End();
menu.Draw(spriteBatch);
break;
#endregion

#region GameState StateGamePlaying - Drawing code
case GameState.StateGamePlaying:
MediaPlayer.Volume = 0.2f;
camera.Initialize((float)graphics.GraphicsDevice.Viewport.Width / (float)graphics.GraphicsDevice.Viewport.Height, playerGun, world);
world.Update(gameTime);
camera.Draw(gameTime, projection, view);
camera.Update(gameTime);
// playerGun.Draw(gameTime, projection, view);
// playerGun.Update(gameTime);
Mouse.SetPosition(graphics.GraphicsDevice.Viewport.Width / 2, graphics.GraphicsDevice.Viewport.Height / 2);
mStateCashe = Mouse.GetState();
//menu.Draw(spriteBatch);
spriteBatch.Begin();
spriteBatch.DrawString(Content.Load<SpriteFont>("Arial"), "Points: " + points, new Vector2(5, 5), Color.White);
float hits;
if (points == 0)
{
hits = 0;
} else
{
hits = (points / totalShots) * 100.0f;
}
spriteBatch.DrawString(Content.Load<SpriteFont>("Arial"), "% Hits: " + hits, new Vector2(5, 35), Color.White);
if (playerGun.CheckAmmo() > 0)
{
spriteBatch.DrawString(Content.Load<SpriteFont>("Arial"), "Ammo: " + playerGun.CheckAmmo(), new Vector2(5, 65), Color.White);
}
else if (playerGun.CheckAmmo() <= 0)
{
spriteBatch.DrawString(Content.Load<SpriteFont>("Arial"), "Reload", new Vector2(5, 65), Color.Red);
}
spriteBatch.End();
break;
#endregion

#region GameState StateGameOver - Drawing code
case GameState.StateGameOver:
// draw something
break;
#endregion
}

//spritebatch.End();

base.Draw(gameTime);
}

public void SetState(GameState newState)
{
currentState = newState;
}

}
}




SceneWorld.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using shootingrange.GameWorld;

namespace shootingrange
{
class SceneWorld
{
private List<ISceneObject> worldObjects;
private Vector3 cameraPosition;
private Vector3 cameraTarget;
private Vector3 cameraReference;

public SceneWorld()
{
worldObjects = new List<ISceneObject>();
cameraPosition = new Vector3(0.0f, 0.0f, 0.0f);
cameraReference = new Vector3(0.0f, 0.0f, 1.0f);
cameraTarget = cameraReference + cameraPosition;
}

public void AddObject(ISceneObject obj)
{
worldObjects.Add(obj);
}

public void removeObject(ISceneObject obj)
{
worldObjects.Remove(obj);
}

public void Update(GameTime gameTime)
{
foreach (ISceneObject obj in worldObjects)
{
obj.Update(gameTime);
}
}

public void Initialize(Model model, float aspectRatio)
{
//foreach (ISceneObject obj in worldObjects)
//{
// obj.Initialize(model, aspectRatio);
// }
}

public void Remove(ISceneObject obj)
{
worldObjects.Remove(obj);
}

public List<ISceneObject> GetObjects()
{
return worldObjects;
}

public void ActionEvent(int action)
{
foreach (ISceneObject obj in worldObjects)
{
obj.ActionEvent(action);
}
}

public bool Collision(BoundingSphere bounds)
{
foreach (ISceneObject obj in worldObjects)
{
if (obj.GetBounds().Radius == -1)
{
continue;
}
if (obj.GetBounds().Intersects(bounds))
{
Console.WriteLine(obj.GetType() + ", " + obj.GetBounds().Radius);
return true;
}
}
return false;
}

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++;
return true;
}
}
return false;
}
}
}


Share this post


Link to post
Share on other sites
SOLVED!

I just realized that I forgot to point the bullet's bounds to it's bounding sphere. i.e. I had


public BoundingSphere GetBounds()
{
base.GetBounds(model, boneTransforms);
bounds = bounds.Transform(Matrix.CreateScale(SCALE) * Matrix.CreateTranslation(modelPosition));
return bounds;
}



BUT, I should obviously have done:


public BoundingSphere GetBounds()
{
bounds = base.GetBounds(model, boneTransforms);
bounds = bounds.Transform(Matrix.CreateScale(SCALE) * Matrix.CreateTranslation(modelPosition));
return bounds;
}



I feel stupid now :-P

Thanks for your help everyone. I'll post the completed code for future reference later once it's done in case somebody has a similar raycast issue.

Share this post


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

  • Advertisement