Сфера модели 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);
        }

person Daniel Lopez    schedule 12.12.2011    source источник


Ответы (2)


Общий совет: В XNA не следует выполнять обнаружение столкновений в методе Draw. Этот метод может вызываться реже, чем 60 кадров в секунду. Вы должны сделать это в методе Update вашего класса. См. здесь длинное объяснение.

Я думаю, что ваше обнаружение столкновений неверно. Вам нужно отразить угол направления, а не просто выбрать случайное направление.

Также вам нужно добавить вектор скорости, который будет добавлен к вашей текущей позиции. С таким вектором намного проще работать, чем пытаться работать с позицией напрямую.

Существует множество руководств по реализации Pong на C#. Это просто образец:

http://www.freewebs.com/campelmxna/XNATutorials/XNATut4.htm

Обратите внимание, что у вас есть два разных члена, один представляет позицию, а другой скорость, которая будет добавляться к позиции при каждом обновлении.

Помимо этого, может случиться так, что дельта, которую вы каждый раз добавляете к положению мяча, слишком велика, поэтому она «пропускает» ограничивающие стены. Вы можете 1) уменьшить дельту или 2) сделать ваши ограничивающие стены шире.

person krolth    schedule 12.12.2011

Для меня это похоже на задание в школе/колледже ;)

Советы:

1) Во-вторых, вам придется писать одну и ту же функцию более одного раза, останавливаться и пересматривать. Если ваша функция зависит только от одной/небольшого числа переменных/значений, напишите одну функцию, которая принимает переменные в качестве параметров. 2) Если вы сделаете 1), выше, должно быть довольно легко пройтись по коду и понять, почему ваши вычисления теста на попадание терпят неудачу. Затем вы можете применить исправление в одном месте.

person Rich Turner    schedule 12.12.2011
comment
Это хобби-проект, поверьте мне. Вы правы, у меня должна быть одна функция, которая получает границы для каждой стены. Я исправил это, но это все еще не отвечает на вопрос, почему мяч не сталкивается. Я обновляю положение модели для ограничивающей сферы и смотрю значения отладки, но все выглядит нормально. Может быть, радиус слишком мал, но он больше нуля. - person Daniel Lopez; 13.12.2011
comment
+1 за предложение перегрузок. Они действительно очищают код и значительно облегчают отладку. - person piebie; 13.12.2011
comment
Подход с ограничивающей рамкой — отличный способ быстро и дешево проверить, пересекает ли мяч стену. Должно быть довольно просто взломать отладчик, когда мяч приближается к стене, а затем пройтись по коду, наблюдая за вычислениями, чтобы определить, когда ограничивающая рамка мяча пересекает стену. Если вы заархивируете и прикрепите свой проект, мы также можем попробовать запустить и отладить ваш код. - person Rich Turner; 14.12.2011