As you can see from the screenshot below, my desired behavior is:
The problem is which is not shown in the picture below is that the ball goes right through any of the walls, instead of colliding or bouncing.
I have tried updating the model position of the ball's sphere but it still won't cut it. I have tried setting the radius of the ball to a large number like 100 - but it collides when it starts moving before it even hits the wall and starts vibrating.
Source Code (Ball.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;
namespace _3D_Pong
{
class Ball
{
private Model model;
private Vector3 modelpos;
private Random random = new Random();
public Vector3 ModelPosition { get; set; }
private Vector3 FowardDirection { get; set; }
private float randomangle;
private int direction = 0;
private bool start = false;
private int v;
public Ball(Model m, Vector3 initial_position, int velocity = 30)
{
v = velocity;
model = m;
modelpos = initial_position;
randomangle = MathHelper.ToRadians(random.Next(0, 45));
direction = random.Next(1);
FowardDirection = Matrix.CreateRotationY(randomangle).Forward;
}
public void BeginMoving()
{
start = true;
}
private BoundingSphere BallsBounds()
{
Matrix worldTransform = Matrix.CreateTranslation(modelpos);
Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
ModelMesh mesh = model.Meshes[0];
foreach (ModelMeshPart meshPart in mesh.MeshParts)
{
int vertexStride = meshPart.VertexBuffer.VertexDeclaration.VertexStride;
int vertexBufferSize = meshPart.NumVertices * vertexStride;
float[] vertexData = new float[vertexBufferSize / sizeof(float)];
meshPart.VertexBuffer.GetData<float>(vertexData);
for (int i = 0; i < vertexBufferSize / sizeof(float); i += vertexStride / sizeof(float))
{
Vector3 transformedPosition = Vector3.Transform(new Vector3(vertexData[i], vertexData[i + 1], vertexData[i + 2]), worldTransform);
min = Vector3.Min(min, transformedPosition);
max = Vector3.Max(max, transformedPosition);
}
}
BoundingSphere sphere = BoundingSphere.CreateFromBoundingBox(new BoundingBox(min, max));
return sphere;
}
public void Draw(Camera camera, ArenaRenderer arena)
{
if (start)
{
bool predicate1, predicate2, predicate3, predicate4;
predicate1 = BallsBounds().Intersects(arena.FirstWall());
predicate2 = BallsBounds().Intersects(arena.SecondWall());
predicate3 = BallsBounds().Intersects(arena.ThirdWall());
predicate4 = BallsBounds().Intersects(arena.FourthWall());
if (predicate1 || predicate2 || predicate3 || predicate4)
{
if (direction == 0)
{
direction = 1;
randomangle = MathHelper.ToRadians(random.Next(0, 45));
FowardDirection = Matrix.CreateRotationY(randomangle).Forward;
}
else if (direction == 1)
{
direction = 0;
randomangle = MathHelper.ToRadians(random.Next(0, 45));
FowardDirection = Matrix.CreateRotationY(randomangle).Forward;
}
}
if (direction == 1)
{
modelpos += FowardDirection * v;
}
else
{
modelpos -= FowardDirection * v;
}
}
model.Draw(Matrix.CreateTranslation(modelpos), camera.View, camera.Projection);
}
}
}
Source (Arena.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;
namespace _3D_Pong
{
class ArenaRenderer
{
private Model model;
public ArenaRenderer(Model m)
{
model = m;
}
public BoundingBox FirstWall()
{
Matrix worldTransform = Matrix.CreateTranslation(Vector3.Zero);
// Initialize minimum and maximum corners of the bounding box to max and min values
Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
ModelMesh mesh = model.Meshes[0];
foreach (ModelMeshPart meshPart in mesh.MeshParts)
{
// Vertex buffer parameters
int vertexStride = meshPart.VertexBuffer.VertexDeclaration.VertexStride;
int vertexBufferSize = meshPart.NumVertices * vertexStride;
// Get vertex data as float
float[] vertexData = new float[vertexBufferSize / sizeof(float)];
meshPart.VertexBuffer.GetData<float>(vertexData);
// Iterate through vertices (possibly) growing bounding box, all calculations are done in world space
for (int i = 0; i < vertexBufferSize / sizeof(float); i += vertexStride / sizeof(float))
{
Vector3 transformedPosition = Vector3.Transform(new Vector3(vertexData[i], vertexData[i + 1], vertexData[i + 2]), worldTransform);
min = Vector3.Min(min, transformedPosition);
max = Vector3.Max(max, transformedPosition);
}
}
// Create and return bounding box
return new BoundingBox(min, max);
}
public BoundingBox SecondWall()
{
Matrix worldTransform = Matrix.CreateTranslation(Vector3.Zero);
// Initialize minimum and maximum corners of the bounding box to max and min values
Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
ModelMesh mesh = model.Meshes[1];
foreach (ModelMeshPart meshPart in mesh.MeshParts)
{
// Vertex buffer parameters
int vertexStride = meshPart.VertexBuffer.VertexDeclaration.VertexStride;
int vertexBufferSize = meshPart.NumVertices * vertexStride;
// Get vertex data as float
float[] vertexData = new float[vertexBufferSize / sizeof(float)];
meshPart.VertexBuffer.GetData<float>(vertexData);
// Iterate through vertices (possibly) growing bounding box, all calculations are done in world space
for (int i = 0; i < vertexBufferSize / sizeof(float); i += vertexStride / sizeof(float))
{
Vector3 transformedPosition = Vector3.Transform(new Vector3(vertexData[i], vertexData[i + 1], vertexData[i + 2]), worldTransform);
min = Vector3.Min(min, transformedPosition);
max = Vector3.Max(max, transformedPosition);
}
}
// Create and return bounding box
return new BoundingBox(min, max);
}
public BoundingBox ThirdWall()
{
Matrix worldTransform = Matrix.CreateTranslation(Vector3.Zero);
// Initialize minimum and maximum corners of the bounding box to max and min values
Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
ModelMesh mesh = model.Meshes[2];
foreach (ModelMeshPart meshPart in mesh.MeshParts)
{
// Vertex buffer parameters
int vertexStride = meshPart.VertexBuffer.VertexDeclaration.VertexStride;
int vertexBufferSize = meshPart.NumVertices * vertexStride;
// Get vertex data as float
float[] vertexData = new float[vertexBufferSize / sizeof(float)];
meshPart.VertexBuffer.GetData<float>(vertexData);
// Iterate through vertices (possibly) growing bounding box, all calculations are done in world space
for (int i = 0; i < vertexBufferSize / sizeof(float); i += vertexStride / sizeof(float))
{
Vector3 transformedPosition = Vector3.Transform(new Vector3(vertexData[i], vertexData[i + 1], vertexData[i + 2]), worldTransform);
min = Vector3.Min(min, transformedPosition);
max = Vector3.Max(max, transformedPosition);
}
}
// Create and return bounding box
return new BoundingBox(min, max);
}
public BoundingBox FourthWall()
{
Matrix worldTransform = Matrix.CreateTranslation(Vector3.Zero);
// Initialize minimum and maximum corners of the bounding box to max and min values
Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
ModelMesh mesh = model.Meshes[3];
foreach (ModelMeshPart meshPart in mesh.MeshParts)
{
// Vertex buffer parameters
int vertexStride = meshPart.VertexBuffer.VertexDeclaration.VertexStride;
int vertexBufferSize = meshPart.NumVertices * vertexStride;
// Get vertex data as float
float[] vertexData = new float[vertexBufferSize / sizeof(float)];
meshPart.VertexBuffer.GetData<float>(vertexData);
// Iterate through vertices (possibly) growing bounding box, all calculations are done in world space
for (int i = 0; i < vertexBufferSize / sizeof(float); i += vertexStride / sizeof(float))
{
Vector3 transformedPosition = Vector3.Transform(new Vector3(vertexData[i], vertexData[i + 1], vertexData[i + 2]), worldTransform);
min = Vector3.Min(min, transformedPosition);
max = Vector3.Max(max, transformedPosition);
}
}
// Create and return bounding box
return new BoundingBox(min, max);
}
public void Draw(Camera camera)
{
model.Draw(Matrix.CreateTranslation(Vector3.Zero), camera.View, camera.Projection);
}
}
}
I haven't implemented the paddles until I fix collision detection problem. If I am missing a piece of information please leave a comment, I have tried everything I can think of.
I have changed it so there is one function for each wall's bounds.
public BoundingBox GetWallBounds(int index)
{
Matrix worldTransform = Matrix.CreateTranslation(Vector3.Zero);
// Initialize minimum and maximum corners of the bounding box to max and min values
Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
ModelMesh mesh = model.Meshes[index];
foreach (ModelMeshPart meshPart in mesh.MeshParts)
{
// Vertex buffer parameters
int vertexStride = meshPart.VertexBuffer.VertexDeclaration.VertexStride;
int vertexBufferSize = meshPart.NumVertices * vertexStride;
// Get vertex data as float
float[] vertexData = new float[vertexBufferSize / sizeof(float)];
meshPart.VertexBuffer.GetData<float>(vertexData);
// Iterate through vertices (possibly) growing bounding box, all calculations are done in world space
for (int i = 0; i < vertexBufferSize / sizeof(float); i += vertexStride / sizeof(float))
{
Vector3 transformedPosition = Vector3.Transform(new Vector3(vertexData[i], vertexData[i + 1], vertexData[i + 2]), worldTransform);
min = Vector3.Min(min, transformedPosition);
max = Vector3.Max(max, transformedPosition);
}
}
// Create and return bounding box
return new BoundingBox(min, max);
}
General Tip: In XNA you shouldn't do collision detection in the Draw method. This method might be called less times than 60 frames per second. You should do it in the Update method of your class. See here for the long explanation.
I think your collision detection is incorrect. You need to reflect the direction angle not just pick a random direction.
Also you need to add a velocity vector that will be added to your current position. It's a lot easier to work with such vector instead of trying to work with the position directly.
There are tons of tutorials for implementing Pong in C#. This is just a sample:
http://www.freewebs.com/campelmxna/XNATutorials/XNATut4.htm
Note that there you have two different members, one represents the position and the other one the speed that will be added to the position each Update.
Other than that it might be that the delta that you are adding to your ball position each time is too much so it's "skipping" the bounding walls. You can 1) decrease the delta or 2) make your bounding walls wider.