|
|
|
|
|
As you have probably already guessed, this chapter we’re going to detect when our plane has collided with an element of the scene. We will define 3 kinds of collisions:
Building: When the player has crashed against a building of our 3D city Boundary: When the xwing is outside the city, below the ground or too high in the sky Target: If the xwing has crashed against one of the targets we’ll add in a next chapter.
So first add this enumeration to the top of your code, for example above your variable declarations:
enum CollisionType { None, Building, Boundary, Target }
To detect collisions, we’re going to model our xwing as a sphere, an abstraction which is good enough for this purpose.
By modelling our xwing as a sphere, we can use the functionality of XNA to detect for collisions. For each building of our city, we will create a BoundingBox object. Next, if we create a BoundingSphere object for our xwing, we can use the Contains method to check if they collide!
So first, we need to create a BoundingBox for each building of our city, and add all BoundingBoxes together in one big array. Start by defining this array:
BoundingBox[] buildingBoundingBoxes; BoundingBox completeCityBox;
The completeCityBox will be used to check whether the player has flown out of the city.
Next, add this method to your code:
private void SetUpBoundingBoxes() { int cityWidth = floorPlan.GetLength(0); int cityLength = floorPlan.GetLength(1);
List<BoundingBox>
bbList = new List<BoundingBox>
(); for (int x = 0; x < cityWidth; x++) { for (int z = 0; z < cityLength; z++) { int buildingType = floorPlan[x, z]; if (buildingType != 0) { int buildingHeight = buildingHeights[buildingType]; Vector3[] buildingPoints = new Vector3[2]; buildingPoints[0] = new Vector3(x, 0, -z); buildingPoints[1] = new Vector3(x + 1, buildingHeight, -z - 1); BoundingBox buildingBox = BoundingBox.CreateFromPoints(buildingPoints); bbList.Add(buildingBox); } } } buildingBoundingBoxes= bbList.ToArray(); }
You start by retrieving the width and length of your city, and by creating a List capable of storing BoundingBoxes.
Next, you scroll through the floorPlan and check the type of building for each tile of the city. If there is a building, you create an array of 2 Vector3s: one indicating the lower-back-left point of the builting, and one indicating the upper-front-right point of the building. These 2 points indicate a box! So you ask XNA to construct a BoundingBox object based on these 2 points using the BoundingBox.CreateFromPoints method. Once the BoundingBox for the current building has been created, you add it to the List.
In the end, you convert the List to an array and store this array in the buildingBoundingBoxes variable.
We still need to create the BoundingBox that contains the whole city, which allows us to detect whether the xwing has left the city. This is done using exactly the same approach: your create an array of 2 points. The first point is the lower-back-left point of the city, the other is the upper-front-right point of the city. From these points you create a BoundingBox which you store in the completeCityBox variable.
Put this code at the end of the SetUpBoundingBoxes method:
Vector3[] boundaryPoints = new Vector3[2]; boundaryPoints[0] = new Vector3(0, 0, 0); boundaryPoints[1] = new Vector3(cityWidth, 20, -cityLength); completeCityBox = BoundingBox.CreateFromPoints(boundaryPoints);
These BoundingBoxes only need to be defined once, so call this method from our Initialize method:
SetUpBoundingBoxes();
With our BoundingBoxes defined, we can add the CheckCollision method:
private CollisionType CheckCollision(BoundingSphere sphere) { for (int i = 0; i < buildingBoundingBoxes.Length; i++) if (buildingBoundingBoxes[i].Contains(sphere) != ContainmentType.Disjoint) return CollisionType.Building; if (completeCityBox.Contains(sphere) != ContainmentType.Contains) return CollisionType.Boundary; return CollisionType.None; }
This method expects a BoundingSphere object from the calling code. This BoundingSphere will be the sphere around our xwing.
First, we check if there is a collision between our xwing and one of the BoundingBoxes of the building by calling the Contains method. If the result is not Disjont, there is a collision, so we return CollisionType.Building.
If the xwing doesn’t collide with the buildings, we check the Contains method between our xwing sphere and the box surrounding our city. This time, the box should completely Contain our xwing. If not, our xwing is too low, too high or outside the city. Which means we return CollisionType.Boundary.
If there is no collision between the xwing and the buildings, and if the xwing is inside the city box, everything is OK and we return CollisionType.None.
Now all we need to do is create a BoundingSphere around our xwing, and pass this BoundingSphere to our CheckCollision method! So add this code to our Update method:
BoundingSphere xwingSpere = new BoundingSphere(xwingPosition, 0.04f); if (CheckCollision(xwingSpere) != CollisionType.None) { xwingPosition = new Vector3(8, 1, -3); xwingRotation = Quaternion.Identity; gameSpeed /= 1.1f; }
The first line creates a BoundingSphere, with its origin at the position of our xwing. We set its radius to 0.04f, which somewhat corresponds to the size of our Model.
Next, we pass this BoundingSphere to the CheckCollision method. If the returned CollisionType is not None, we reset the position and rotation of our camera, and decrease the speed of the game as things were clearly going too fast for the player.
Now run this code, and make sure you crash as soon as possible to try out your new code!

Click here to go to the forum on this chapter!
Or click on one of the topics on this chapter to go there: Collision with series 1 I put the plane into the series 1 and i am trying ...CollisionDetection multy-hight objects So I am creating another simulator project Involvi...Manual compute boundingbox Here I have search from the xna creator club forum...Multiple boundingsphere The intention of xna team to let us use boundingsp...Is there a predefine geometry? In xna, is there any predefine geometry type such ...Collision Deection How would I go about creating a colision detection...
Well that image didn’t differ much from last chapter’s image … but this time we’ve implemented collision detection!
What good is a flightsim without some targets to shoot at? Let’s add them in the next chapter.
using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Net; using Microsoft.Xna.Framework.Storage; namespace XNAseries2 { public class Game1 : Microsoft.Xna.Framework.Game {
enum CollisionType { None, Building, Boundary, Target }
int[] buildingHeights = new int[] { 0, 2, 2, 6, 5, 4 }; GraphicsDeviceManager graphics; GraphicsDevice device; Effect effect; Vector3 lightDirection = new Vector3(3, -2, 5); Matrix viewMatrix; Matrix projectionMatrix; Texture2D sceneryTexture; Model xwingModel; Vector3 xwingPosition = new Vector3(8, 1, -3); Quaternion xwingRotation = Quaternion.Identity; int[,] floorPlan; float gameSpeed = 1.0f; VertexBuffer cityVertexBuffer; VertexDeclaration texturedVertexDeclaration;
BoundingBox[] buildingBoundingBoxes; BoundingBox completeCityBox;
public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { graphics.PreferredBackBufferWidth = 500; graphics.PreferredBackBufferHeight = 500; graphics.IsFullScreen = false; graphics.ApplyChanges(); Window.Title = "Riemer's XNA Tutorials -- Series 2"; LoadFloorPlan(); lightDirection.Normalize();
SetUpBoundingBoxes();
base.Initialize(); } protected override void LoadContent() { device = graphics.GraphicsDevice;
effect = Content.Load<Effect>
("effects"); sceneryTexture = Content.Load<Texture2D>
("texturemap"); xwingModel = LoadModel("xwing");
SetUpVertices(); SetUpCamera(); }
private Model LoadModel(string assetName) {
Model newModel = Content.Load<Model>
(assetName); foreach (ModelMesh mesh in newModel.Meshes) foreach (ModelMeshPart meshPart in mesh.MeshParts) meshPart.Effect = effect.Clone(device); return newModel; }
private void LoadFloorPlan() { floorPlan = new int[,] { {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,1,1,0,0,0,1,1,0,0,1,0,1}, {1,0,0,1,1,0,0,0,1,0,0,0,1,0,1}, {1,0,0,0,1,1,0,1,1,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,1,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,1,1,0,0,0,1,0,0,0,0,0,0,1}, {1,0,1,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,1,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,1,0,0,0,1,0,0,0,0,1}, {1,0,1,0,0,0,0,0,0,1,0,0,0,0,1}, {1,0,1,1,0,0,0,0,1,1,0,0,0,1,1}, {1,0,0,0,0,0,0,0,1,1,0,0,0,1,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, };
Random random = new Random(); int differentBuildings = buildingHeights.Length - 1; for (int x = 0; x < floorPlan.GetLength(0); x++) for (int y = 0; y < floorPlan.GetLength(1); y++) if (floorPlan[x, y] == 1) floorPlan[x, y] = random.Next(differentBuildings) + 1; }
private void SetUpBoundingBoxes() { int cityWidth = floorPlan.GetLength(0); int cityLength = floorPlan.GetLength(1);
List<BoundingBox>
bbList = new List<BoundingBox>
(); for (int x = 0; x < cityWidth; x++) { for (int z = 0; z < cityLength; z++) { int buildingType = floorPlan[x, z]; if (buildingType != 0) { int buildingHeight = buildingHeights[buildingType]; Vector3[] buildingPoints = new Vector3[2]; buildingPoints[0] = new Vector3(x, 0, -z); buildingPoints[1] = new Vector3(x + 1, buildingHeight, -z - 1); BoundingBox buildingBox = BoundingBox.CreateFromPoints(buildingPoints); bbList.Add(buildingBox); } } } buildingBoundingBoxes = bbList.ToArray();
Vector3[] boundaryPoints = new Vector3[2]; boundaryPoints[0] = new Vector3(0, 0, 0); boundaryPoints[1] = new Vector3(cityWidth, 20, -cityLength); completeCityBox = BoundingBox.CreateFromPoints(boundaryPoints); }
private void SetUpCamera() { viewMatrix = Matrix.CreateLookAt(new Vector3(20, 13, -5), new Vector3(8, 0, -7), new Vector3(0, 1, 0)); projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 0.2f, 500.0f); } private void SetUpVertices() { int differentBuildings = buildingHeights.Length - 1; float imagesInTexture = 1 + differentBuildings * 2; int cityWidth = floorPlan.GetLength(0); int cityLength = floorPlan.GetLength(1);
List<VertexPositionNormalTexture>
verticesList = new List<VertexPositionNormalTexture>
(); for (int x = 0; x < cityWidth; x++) { for (int z = 0; z < cityLength; z++) { int currentbuilding = floorPlan[x, z];
//floor or ceiling verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z), new Vector3(0, 1, 0), new Vector2(currentbuilding * 2 / imagesInTexture, 1))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2 + 1) / imagesInTexture, 1)));
verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2 + 1) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2 + 1) / imagesInTexture, 1)));
if (currentbuilding != 0) { //front wall verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2) / imagesInTexture, 1))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 1)));
verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2) / imagesInTexture, 1))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2) / imagesInTexture, 0)));
//back wall verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2) / imagesInTexture, 1))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 1))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));
verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));
//left wall verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 1))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z - 1), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 1))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));
verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));
//right wall verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 1))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z - 1), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 1)));
verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z - 1), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 1))); verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 0))); } } }
cityVertexBuffer = new VertexBuffer(device, verticesList.Count * VertexPositionNormalTexture.SizeInBytes, BufferUsage.WriteOnly);
cityVertexBuffer.SetData<VertexPositionNormalTexture>
(verticesList.ToArray()); texturedVertexDeclaration = new VertexDeclaration(device, VertexPositionNormalTexture.VertexElements); }
protected override void UnloadContent() { }
protected override void Update(GameTime gameTime) { ProcessKeyboard(gameTime); float moveSpeed = gameTime.ElapsedGameTime.Milliseconds / 500.0f * gameSpeed; MoveForward(ref xwingPosition, xwingRotation, moveSpeed);
BoundingSphere xwingSpere = new BoundingSphere(xwingPosition, 0.04f); if (CheckCollision(xwingSpere) != CollisionType.None) { xwingPosition = new Vector3(8, 1, -3); xwingRotation = Quaternion.Identity; gameSpeed /= 1.1f; }
UpdateCamera(); base.Update(gameTime); } private void UpdateCamera() { Vector3 campos = new Vector3(0, 0.1f, 0.6f); campos = Vector3.Transform(campos, Matrix.CreateFromQuaternion(xwingRotation)); campos += xwingPosition; Vector3 camup = new Vector3(0, 1, 0); camup = Vector3.Transform(camup, Matrix.CreateFromQuaternion(xwingRotation)); viewMatrix = Matrix.CreateLookAt(campos, xwingPosition, camup); projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 0.2f, 500.0f); } private void ProcessKeyboard(GameTime gameTime) { float leftRightRot = 0; float turningSpeed = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f; turningSpeed *= 1.6f * gameSpeed; KeyboardState keys = Keyboard.GetState(); if (keys.IsKeyDown(Keys.Right)) leftRightRot += turningSpeed; if (keys.IsKeyDown(Keys.Left)) leftRightRot -= turningSpeed; float upDownRot = 0; if (keys.IsKeyDown(Keys.Down)) upDownRot += turningSpeed; if (keys.IsKeyDown(Keys.Up)) upDownRot -= turningSpeed; Quaternion additionalRot = Quaternion.CreateFromAxisAngle(new Vector3(0, 0, -1), leftRightRot) * Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), upDownRot); xwingRotation *= additionalRot; } private void MoveForward(ref Vector3 position, Quaternion rotationQuat, float speed) { Vector3 addVector = Vector3.Transform(new Vector3(0, 0, -1), rotationQuat); position += addVector * speed; }
private CollisionType CheckCollision(BoundingSphere sphere) { for (int i = 0; i < buildingBoundingBoxes.Length; i++) if (buildingBoundingBoxes[i].Contains(sphere) != ContainmentType.Disjoint) return CollisionType.Building; if (completeCityBox.Contains(sphere) != ContainmentType.Contains) return CollisionType.Boundary; return CollisionType.None; }
protected override void Draw(GameTime gameTime) { device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.DarkSlateBlue, 1.0f, 0); DrawCity(); DrawModel(); base.Draw(gameTime); } private void DrawCity() { effect.CurrentTechnique = effect.Techniques["Textured"]; effect.Parameters["xWorld"].SetValue(Matrix.Identity); effect.Parameters["xView"].SetValue(viewMatrix); effect.Parameters["xProjection"].SetValue(projectionMatrix); effect.Parameters["xTexture"].SetValue(sceneryTexture); effect.Parameters["xEnableLighting"].SetValue(true); effect.Parameters["xLightDirection"].SetValue(lightDirection); effect.Parameters["xAmbient"].SetValue(0.5f); effect.Begin(); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Begin(); device.VertexDeclaration = texturedVertexDeclaration; device.Vertices[0].SetSource(cityVertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes); device.DrawPrimitives(PrimitiveType.TriangleList, 0, cityVertexBuffer.SizeInBytes / VertexPositionNormalTexture.SizeInBytes / 3); pass.End(); } effect.End(); } private void DrawModel() { Matrix worldMatrix = Matrix.CreateScale(0.0005f, 0.0005f, 0.0005f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateFromQuaternion(xwingRotation) * Matrix.CreateTranslation(xwingPosition); Matrix[] xwingTransforms = new Matrix[xwingModel.Bones.Count]; xwingModel.CopyAbsoluteBoneTransformsTo(xwingTransforms); foreach (ModelMesh mesh in xwingModel.Meshes) { foreach (Effect currentEffect in mesh.Effects) { currentEffect.CurrentTechnique = currentEffect.Techniques["Colored"]; currentEffect.Parameters["xWorld"].SetValue(xwingTransforms[mesh.ParentBone.Index] * worldMatrix); currentEffect.Parameters["xView"].SetValue(viewMatrix); currentEffect.Parameters["xProjection"].SetValue(projectionMatrix); currentEffect.Parameters["xEnableLighting"].SetValue(true); currentEffect.Parameters["xLightDirection"].SetValue(lightDirection); currentEffect.Parameters["xAmbient"].SetValue(0.5f); } mesh.Draw(); } } } }
- Website design & XNA + DirectX code : Riemer Grootjans - ©2003 - 2008 Riemer Grootjans
|
|
|
|
|