Сфера модели XNA не столкнется с одной или всеми четырьмя сетками
Как вы можете видеть на скриншоте ниже, мое желаемое поведение:
- сфера начинается в середине арены
- когда вы нажимаете ввод, он движется в случайном направлении
- когда он сталкивается с одной из четырех стен, он отскакивает назад в противоположном направлении под случайным углом от 0 до 45 градусов
Проблема, которая не показана на рисунке ниже, состоит в том, что мяч проходит прямо через любую из стен, а не сталкивается или подпрыгивает.
Я попытался обновить модельное положение сферы шара, но это все еще не будет сокращать это. Я попытался установить радиус шара на большое число, например, 100 - но он сталкивается, когда он начинает двигаться, прежде чем он даже ударяется о стену и начинает вибрировать.
Исходный код (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);
}
}
}
Источник (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);
}
}
}
Я не реализовал весла, пока не решу проблему обнаружения столкновений. Если мне не хватает информации, пожалуйста, оставьте комментарий, я перепробовал все, что мог придумать.
Я изменил это так, что есть одна функция для границ каждой стены.
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);
}
2 ответа
Общий совет: в XNA не следует обнаруживать столкновения в методе Draw. Этот метод может вызываться менее 60 раз в секунду. Вы должны сделать это в методе Update вашего класса. Смотрите здесь для длинного объяснения.
Я думаю, что ваше обнаружение столкновения неверно. Вы должны отражать угол направления, а не просто выбирать случайное направление.
Также вам нужно добавить вектор скорости, который будет добавлен к вашей текущей позиции. С таким вектором работать намного проще, чем пытаться работать с позицией напрямую.
Существует множество обучающих программ по внедрению понга в C#. Это всего лишь пример:
http://www.freewebs.com/campelmxna/XNATutorials/XNATut4.htm
Обратите внимание, что у вас есть два разных члена, один представляет позицию, а другой - скорость, которая будет добавляться к позиции при каждом обновлении.
Кроме этого может быть так, что дельта, которую вы добавляете в положение мяча каждый раз, слишком велика, поэтому она "пропускает" ограничивающие стены. Вы можете 1) уменьшить дельту или 2) сделать ваши ограничивающие стены шире.
Похоже, что школа / колледж для меня;)
Подсказки:
1) Во-вторых, вы должны написать одну и ту же функцию более одного раза, остановить и пересмотреть. Если ваша функция изменяется только на одну / a-small-number-of-s (s) / value (s), то напишите одну функцию, которая принимает переменную (и) в качестве параметров. 2) Если вы сделаете 1) выше, вам будет довольно просто пройтись по коду и выяснить, почему ваши расчеты по тестам на хиты не дают результатов. Затем вы можете применить исправление в одном месте.