c# - XNA 模型球体不会与四个壁面网格中的一个或全部发生碰撞

标签 c# 3d xna collision-detection pong

正如您从下面的屏幕截图中看到的,我希望的行为是:

  • 球体从竞技场中间开始
  • 当你按下回车键时,它会随机移动
  • 当它与四面墙之一碰撞时,它会以 0 到 45 度之间的随机角度弹回相反的方向

下图中没有显示的问题是球直接穿过任何墙壁,而不是碰撞或弹跳。

我已经尝试更新球体的模型位置,但它仍然无法切割。我曾尝试将球的半径设置为很大的数字,例如 100 - 但它在开始移动时甚至在撞到墙壁并开始振动之前就发生了碰撞。

Visual Screenshot Of World Space

源代码(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);
        }

最佳答案

一般提示:在 XNA 中,您不应在 Draw 方法中进行碰撞检测。此方法的调用次数可能少于每秒 60 帧。您应该在类的 Update 方法中执行此操作。参见 here详细解释。

我认为您的碰撞检测不正确。您需要反射(reflect)方向角度,而不仅仅是随机选择一个方向。

您还需要添加一个速度矢量,该矢量将添加到您的当前位置。使用这样的向量比尝试直接使用位置要容易得多。

有大量关于在 C# 中实现 Pong 的教程。这只是一个示例:

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

请注意,您有两个不同的成员,一个代表位置,另一个代表每次更新时将添加到位置的速度。

除此之外,可能是您每次添加到球位置的增量太大,因此它“跳过”了边界墙。您可以 1) 减小增量或 2) 使边界墙更宽。

关于c# - XNA 模型球体不会与四个壁面网格中的一个或全部发生碰撞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8479402/

相关文章:

opengl - DirectX/OpenGL 中的三角形绘制顺序

c# - 什么是最好的 XNA 书籍,它可以教您从基础到高级的 2d 和 3d 游戏编程?

c# - 调整整个场景的颜色

c# - 关于计算随机颜色

c# - 如何添加 JwtBearer 和 AddMicrosoftIdentityWebAppAuthentication

algorithm - 长方体的 RANSAC

c# - Entity Framework 5 更新对于每个对象/行仅有效一次

algorithm - 什么是动态世界中用于查询的 KD 树类数据结构?

c# - 将查询转换为 Lambda 表达式

c# - Linq OrderBy() 与 List.Sort() 的速度