【问题标题】:How do I draw lines using XNA?如何使用 XNA 画线?
【发布时间】:2010-09-21 04:08:32
【问题描述】:

我已经阅读了一堆涉及 XNA 的教程(并且它是各种版本),但我仍然对绘制图元感到有些困惑。一切似乎都很复杂。

谁能告诉我,使用代码,在屏幕上绘制一两条线的最简单的 XNA 实现?也许有一个简短的解释(包括样板文件)?

我不是游戏程序员,也没有多少 XNA 经验。我的最终目标是在屏幕上绘制一些线条,最终我将通过旋转等(手动)进行转换。然而,对于这第一步..我需要简单地画线!我记得在我古老的 OpenGL 时代,用几个方法调用画一条线是相当简单的。我应该简单地恢复使用非托管的 directx 调用吗?

【问题讨论】:

    标签: drawing xna lines primitive shapes


    【解决方案1】:

    这是我用来制作线条的一种简单方法,通过指定线条的起始坐标、结束坐标、宽度和颜色:

    注意:您必须在内容目录中添加一个名为“dot”的文件(该行将由这些组成)。

    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 Xna.LineHelper
    {
        public class LineManager
        {
            int loopCounter;
            int lineLegnth;
            Vector2 lineDirection;
            Vector2 _position;
            Color dotColor;
            Rectangle _rectangle;
            List<Texture2D> _dots = new List<Texture2D>();
            FunctionsLibrary functions = new FunctionsLibrary();
    
            public void CreateLineFiles(Vector2 startPosition, Vector2 endPosition, int width, Color color, ContentManager content)
            {
                dotColor = color;
                _position.X = startPosition.X;
                _position.Y = startPosition.Y;
                lineLegnth = functions.Distance((int)startPosition.X, (int)endPosition.X, (int)startPosition.Y, (int)endPosition.Y);
                lineDirection = new Vector2((endPosition.X - startPosition.X) / lineLegnth, (endPosition.Y - startPosition.Y) / lineLegnth);
                _dots.Clear();
                loopCounter = 0;
                _rectangle = new Rectangle((int)startPosition.X, (int)startPosition.Y, width, width);
                while (loopCounter < lineLegnth)
                {
                    Texture2D dot = content.Load<Texture2D>("dot");
                    _dots.Add(dot);
    
                    loopCounter += 1;
                }
    
            }
    
            public void DrawLoadedLine(SpriteBatch sb)
            {
                foreach (Texture2D dot in _dots)
                {
                    _position.X += lineDirection.X;
                    _position.Y += lineDirection.Y;
                    _rectangle.X = (int)_position.X;
                    _rectangle.Y = (int)_position.Y;
                    sb.Draw(dot, _rectangle, dotColor);
                }
            }
        }
    
        public class FunctionsLibrary
        {
            //Random for all methods
            Random Rand = new Random();
    
            #region math
            public int TriangleArea1(int bottom, int height)
            {
                int answer = (bottom * height / 2);
                return answer;
            }
    
            public double TriangleArea2(int A, int B, int C)
            {
                int s = ((A + B + C) / 2);
                double answer = (Math.Sqrt(s * (s - A) * (s - B) * (s - C)));
                return answer;
            }
            public int RectangleArea(int side1, int side2)
            {
                int answer = (side1 * side2);
                return answer;
            }
            public int SquareArea(int side)
            {
                int answer = (side * side);
                return answer;
            }
            public double CircleArea(int diameter)
            {
                double answer = (((diameter / 2) * (diameter / 2)) * Math.PI);
                return answer;
            }
            public int Diference(int A, int B)
            {
                int distance = Math.Abs(A - B);
                return distance;
            }
            #endregion
    
            #region standardFunctions
    
            public int Distance(int x1, int x2, int y1, int y2)
            {
                return (int)(Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)));
            }
    
            #endregion
    
    
    
        }
    }
    

    【讨论】:

      【解决方案2】:

      我想绘制光线,以便调试由爆炸产生的光线以及它们与对象相交的位置。这将在两点之间绘制一条像素细线。这就是我所做的:

      类来存储一些简单的光线数据。 XNA 默认射线类可以工作,但它不存储射线到交点的长度。

      public class myRay
      {
          public Vector3 position, direction;
          public float length;
      }   
      

      存储要绘制的光线的列表:

      List<myRay> DebugRays= new List<myRay>();
      

      创建一个 BasicEffect 并在 LoadContent 方法中使用所需的分辨率向其传递“Matrix.CreateOrthographicOffCenter”投影。

      然后在draw方法中运行这个:

      private void DrawRays()
      {
          spriteBatch.Begin();
      
          foreach (myRay ray in DebugRays)
              {
                  //An array of 2 vertices - a start and end position
                  VertexPositionColor[] Vertices = new VertexPositionColor[2];
                  int[] Indices = new int[2];
      
                  //Starting position of the ray
                  Vertices[0] = new VertexPositionColor()
                  {
                      Color = Color.Orange,
                      Position = ray.position
                  };
      
                  //End point of the ray
                  Vertices[1] = new VertexPositionColor()
                  {
                      Color = Color.Orange,
                      Position = ray.position + (ray.direction * ray.length)
                  };
      
                  Indices[0] = 0;
                  Indices[1] = 1;
      
                  foreach (EffectPass pass in BasicEffect.CurrentTechnique.Passes)
                  {
                      pass.Apply();
                      GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.LineStrip, Vertices, 0, 2, Indices, 0, 1, VertexPositionColorTexture.VertexDeclaration);
                  }
              }
      
          spriteBatch.End();
      }
      

      所以当我的游戏发生爆炸时,它会这样做(伪代码):

      OnExplosionHappened()
      {
          DebugRays.Clear()
      
          myRay ray = new myRay()
                          {
                              position = explosion.Position,
                              direction = GetDirection(explosion, solid),
                              //Used GetValueOrDefault here to prevent null value errors
                              length = explosionRay.Intersects(solid.BoundingBox).GetValueOrDefault()
                          };
      
          DebugRays.Add(ray);
      }
      

      它非常简单(它可能看起来比实际复杂得多)并且很容易将它放入一个单独的类中,您无需再考虑。它还可以让您一次绘制大量线条。

      【讨论】:

        【解决方案3】:

        只需拉伸一个白色像素。

                point = game.Content.Load<Texture2D>("ui/point");
        
                public void DrawLine(Vector2 start, Vector2 end, Color color)
                {
                    Vector2 edge = end - start;
                    float angle = (float)Math.Atan2(edge.Y, edge.X);
        
                    spriteBatch.Begin();
                    spriteBatch.Draw(point,
                        new Rectangle((int)start.X, (int)start.Y, (int)edge.Length(), 1),
                        null, 
                        color, 
                        angle,
                        new Vector2(0, 0),
                        SpriteEffects.None,
                        0);
                    spriteBatch.End();
                }
        

        【讨论】:

          【解决方案4】:

          我自己也遇到了这个问题,并决定创建一个名为 LineBatch 的类。 LineBatch 将绘制线条而不需要 spriteBatch 或点。 课程如下。

          public class LineBatch
          {
              bool cares_about_begin_without_end;
              bool began;
              GraphicsDevice GraphicsDevice;
              List<VertexPositionColor> verticies = new List<VertexPositionColor>();
              BasicEffect effect;
              public LineBatch(GraphicsDevice graphics)
              {
                  GraphicsDevice = graphics;
                  effect = new BasicEffect(GraphicsDevice);
                  Matrix world = Matrix.Identity;
                  Matrix view = Matrix.CreateTranslation(-GraphicsDevice.Viewport.Width / 2, -GraphicsDevice.Viewport.Height / 2, 0);
                  Matrix projection = Matrix.CreateOrthographic(GraphicsDevice.Viewport.Width, -GraphicsDevice.Viewport.Height, -10, 10);
                  effect.World = world;
                  effect.View = view;
                  effect.VertexColorEnabled = true;
                  effect.Projection = projection;
                  effect.DiffuseColor = Color.White.ToVector3();
                  cares_about_begin_without_end = true;
              }
              public LineBatch(GraphicsDevice graphics, bool cares_about_begin_without_end)
              {
                  this.cares_about_begin_without_end = cares_about_begin_without_end;
                  GraphicsDevice = graphics;
                  effect = new BasicEffect(GraphicsDevice);
                  Matrix world = Matrix.Identity;
                  Matrix view = Matrix.CreateTranslation(-GraphicsDevice.Viewport.Width / 2, -GraphicsDevice.Viewport.Height / 2, 0);
                  Matrix projection = Matrix.CreateOrthographic(GraphicsDevice.Viewport.Width, -GraphicsDevice.Viewport.Height, -10, 10);
                  effect.World = world;
                  effect.View = view;
                  effect.VertexColorEnabled = true;
                  effect.Projection = projection;
                  effect.DiffuseColor = Color.White.ToVector3();
              }
              public void DrawAngledLineWithRadians(Vector2 start, float length, float radians, Color color)
              {
                  Vector2 offset = new Vector2(
                      (float)Math.Sin(radians) * length, //x
                      -(float)Math.Cos(radians) * length //y
                      );
                  Draw(start, start + offset, color);
              }
              public void DrawOutLineOfRectangle(Rectangle rectangle, Color color)
              {
                  Draw(new Vector2(rectangle.X, rectangle.Y), new Vector2(rectangle.X + rectangle.Width, rectangle.Y), color);
                  Draw(new Vector2(rectangle.X, rectangle.Y), new Vector2(rectangle.X, rectangle.Y + rectangle.Height), color);
                  Draw(new Vector2(rectangle.X + rectangle.Width, rectangle.Y), new Vector2(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height), color);
                  Draw(new Vector2(rectangle.X, rectangle.Y + rectangle.Height), new Vector2(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height), color);
              }
              public void DrawOutLineOfTriangle(Vector2 point_1, Vector2 point_2, Vector2 point_3, Color color)
              {
                  Draw(point_1, point_2, color);
                  Draw(point_1, point_3, color);
                  Draw(point_2, point_3, color);
              }
              float GetRadians(float angleDegrees)
              {
                  return angleDegrees * ((float)Math.PI) / 180.0f;
              }
              public void DrawAngledLine(Vector2 start, float length, float angleDegrees, Color color)
              {
                  DrawAngledLineWithRadians(start, length, GetRadians(angleDegrees), color);
              }
              public void Draw(Vector2 start, Vector2 end, Color color)
              {
                  verticies.Add(new VertexPositionColor(new Vector3(start, 0f), color));
                  verticies.Add(new VertexPositionColor(new Vector3(end, 0f), color));
              }
              public void Draw(Vector3 start, Vector3 end, Color color)
              {
                  verticies.Add(new VertexPositionColor(start, color));
                  verticies.Add(new VertexPositionColor(end, color));
              }
              public void End()
              {
                  if (!began)
                      if (cares_about_begin_without_end)
                          throw new ArgumentException("Please add begin before end!");
                      else
                          Begin();
                  if (verticies.Count > 0)
                  {
                      VertexBuffer vb = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), verticies.Count, BufferUsage.WriteOnly);
                      vb.SetData<VertexPositionColor>(verticies.ToArray());
                      GraphicsDevice.SetVertexBuffer(vb);
          
                      foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                      {
                          pass.Apply();
                          GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, verticies.Count / 2);
                      }
                  }
                  began = false;
              }
              public void Begin()
              {
                  if (began)
                      if (cares_about_begin_without_end)
                          throw new ArgumentException("You forgot end.");
                      else
                          End();
                  verticies.Clear();
                      began = true;
              }
          }
          

          【讨论】:

            【解决方案5】:

            我认为最简单的最好方法是获取一个白色像素的图像,然后将该像素拉伸成一个矩形,使其看起来像一条线

            我做了一个 Line 类,

            class Line
            {
                Texture pixel = ((set this to a texture of a white pixel with no border));
                Vector2 p1, p2; //this will be the position in the center of the line
                int length, thickness; //length and thickness of the line, or width and height of rectangle
                Rectangle rect; //where the line will be drawn
                float rotation; // rotation of the line, with axis at the center of the line
                Color color;
            
            
                //p1 and p2 are the two end points of the line
                public Line(Vector2 p1, Vector2 p2, int thickness, Color color)
                {
                    this.p1 = p1;
                    this.p2 = p2;
                    this.thickness = thickness;
                    this.color = color;
                }
            
                public void Update(GameTime gameTime)
                {
                    length = (int)Vector2.Distance(p1, p2); //gets distance between the points
                    rotation = getRotation(p1.X, p1.Y, p2.X, p2.Y); //gets angle between points(method on bottom)
                    rect = new Rectangle((int)p1.X, (int)p1.Y, length, thickness)
            
                    //To change the line just change the positions of p1 and p2
                }
            
                public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
                {
                    spriteBatch.Draw(pixel, rect, null, color, rotation, new Vector2.Zero, SpriteEffects.None, 0.0f);
                }
            
                //this returns the angle between two points in radians 
                private float getRotation(float x, float y, float x2, float y2)
                {
                    float adj = x - x2;
                    float opp = y - y2;
                    float tan = opp / adj;
                    float res = MathHelper.ToDegrees((float)Math.Atan2(opp, adj));
                    res = (res - 180) % 360;
                    if (res < 0) { res += 360; }
                    res = MathHelper.ToRadians(res);
                    return res;
                }
            

            希望对你有帮助

            【讨论】:

            • 点赞!这个类对我的调试很有帮助。谢谢!
            【解决方案6】:

            找到了一个教程 http://www.bit-101.com/blog/?p=2832

            它使用 BasicEffect(着色器) 以及 XNA 4.0 中内置的绘图用户原语

            一些我觉得有用的代码示例:

            加载内容方法

            basicEffect = new BasicEffect(GraphicsDevice);
            basicEffect.VertexColorEnabled = true;
            basicEffect.Projection = Matrix.CreateOrthographicOffCenter
            (0, GraphicsDevice.Viewport.Width,     // left, right
            GraphicsDevice.Viewport.Height, 0,    // bottom, top
            0, 1);   
            

            绘制方法

            basicEffect.CurrentTechnique.Passes[0].Apply();
            var vertices = new VertexPositionColor[4];
            vertices[0].Position = new Vector3(100, 100, 0);
            vertices[0].Color = Color.Black;
            vertices[1].Position = new Vector3(200, 100, 0);
            vertices[1].Color = Color.Red;
            vertices[2].Position = new Vector3(200, 200, 0);
            vertices[2].Color = Color.Black;
            vertices[3].Position = new Vector3(100, 200, 0);
            vertices[3].Color = Color.Red;
            
            GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, vertices, 0, 2);
            

            如果这对您有帮助,请玩得开心并投票。还请访问我从中获得的教程。

            【讨论】:

              【解决方案7】:

              按照 NoHayProblema 的回答(我还不能发表评论)。

              这个答案虽然是这个老问题的正确答案,但并不完整。 Texture2D 构造函数返回一个未初始化的纹理,它永远不会在屏幕上绘制。 为了使用这种方法,您需要像这样设置纹理的数据:

              Texture2D SimpleTexture = new Texture2D(GraphicsDevice, 1, 1, false,
                  SurfaceFormat.Color);
              
              Int32[] pixel = {0xFFFFFF}; // White. 0xFF is Red, 0xFF0000 is Blue
              SimpleTexture.SetData<Int32> (pixel, 0, SimpleTexture.Width * SimpleTexture.Height);
              
              // Paint a 100x1 line starting at 20, 50
              this.spriteBatch.Draw(SimpleTexture, new Rectangle(20, 50, 100, 1), Color.Blue);
              

              请注意,将数据写入像素的方式必须与纹理的 SurfaceFormat 一致。该示例有效,因为纹理被格式化为 RGB。 可以像这样在 spriteBatch.Draw 中应用旋转:

              this.spriteBatch.Draw (SimpleTexture, new Rectangle(0, 0, 100, 1), null,
                  Color.Blue, -(float)Math.PI/4, new Vector2 (0f, 0f), SpriteEffects.None, 1f);
              

              【讨论】:

              • 使用颜色作为纹理数据比将像素数据编辑为十六进制值更简单。像这样的东西: var txPixel = new Texture2D(GraphicsDevice, 1, 1); txPixel.SetData(new Color[1] { Color.White });
              • 更好:txPixel.SetData&lt;Color&gt;(new []{ Color.White });
              【解决方案8】:

              嗯,你可以用一种非常简单的方式来做这件事,而不必陷入 3D 可怕的矢量内容。

              只需创建一个快速纹理,例如:

              Texture2D SimpleTexture = new Texture2D(GraphicsDevice, 1, 1, false, SurfaceFormat.Color);

              然后使用该纹理画一条线:

              this.spriteBatch.Draw(SimpleTexture, new Rectangle(100, 100, 100, 1), Color.Blue);

              希望对你有帮助

              【讨论】:

              • 当然只适用于水平线和垂直线。 OP 想要轮换它们,所以这可能对他没有帮助。
              • @TorHaugen spriteBatch.Draw 支持以一定角度绘制精灵
              • 直到我添加 SimpleTexture.SetData(new[] { Color.Black });但是,是的,这比其他提到的方法要容易得多:D
              【解决方案9】:

              还有“manders”在CodePlex上放出的“圆线”代码:


              这是关于它的博客文章:

              【讨论】:

                【解决方案10】:

                在使用 XNA 时,所有内容(甚至是 2d 图元)都必须以 3d 卡可以理解的方式表示,这意味着一条线只是一组顶点。

                MSDN 在这里有一个很好的演练:

                http://msdn.microsoft.com/en-us/library/bb196414.aspx#ID2EEF

                您会发现,与仅设置纹理四边形并旋转它所需的代码相比,渲染一条原始线需要更多的代码,因为本质上,您在渲染一条线时会做同样的事情。

                【讨论】:

                • @Jonathan Holland,感谢您的链接和解释。
                • 链接失效
                猜你喜欢
                • 1970-01-01
                • 2013-02-17
                • 2012-01-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-04-02
                相关资源
                最近更新 更多