如何使用XNA绘制线条?

问题描述:

我读过一堆涉及XNA的教程(以及它的各种版本),我仍然对绘制原语有点困惑。一切似乎都非常复杂。如何使用XNA绘制线条?

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

我不是一名游戏程序员,而且我有很少的XNA经验。我的最终目标是在屏幕上绘制一些线条,我最终将通过旋转等手段进行转换。然而,对于这第一步..我需要简单地画线!我记得在我古老的OpenGL日子里,用几个方法调用绘制一条线就相当简单。我应该简单回复使用非托管directx呼叫吗?

使用XNA时,一切(甚至2d原语)都必须以3d卡可以理解的方式表示,这意味着一行只是一组顶点。

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

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

你会发现,它需要更多的代码来渲染一种原始的线比它会采取只设置一个纹理四和旋转,由于从本质上讲,你在渲染一条线时做同样的事情。

+1

@Jonathan Holland,感谢您的链接和解释。 – mmcdole 2008-11-12 01:23:29

也有 “轮行” 代码 “曼德斯” 已经发布在CodePlex上:


这里是博客文章吧:

那么,你可以做一个非常简单的方法,而不会进入3D恐怖的矢量东西。

只需创建一个快速的质感,例如:

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

我希望这有助于

+1

当然,只适用于水平线和垂直线。 OP想旋转它们,所以这可能不会帮助他。 – 2013-05-30 22:54:48

以下:

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

,然后使用该纹理只画一条线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); 
+3

将像素数据编辑为十六进制值比使用Color作为纹理数据更简单。像这样: var txPixel = new Texture2D(GraphicsDevice,1,1); txPixel.SetData (new Color [1] {Color.White}); – krolth 2011-08-08 06:39:26

+3

甚至更​​好:`txPixel.SetData (new [] {Color.White});` – 2012-11-03 14:43:16

发现一个教程对于 http://www.bit-101.com/blog/?p=2832

其使用BasicEffect(着色)

和XNA 4.0

内置的拉伸用户原始

一些代码样品I找到有用的:

负载的内容的方法

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); 

有乐趣,投票了,如果这有助于你。还请访问我从这里得到的教程。

最简单最好的办法,我认为,就是要得到的只是一个白色像素的图像,然后伸展该像素在矩形看起来像一条线

我做了一个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; 
    } 

希望这有助于

这里是我使用通过指定起始坐标,使线条简单的方式,最终的坐标,宽度,并将它们的颜色:

注:必须将一个名为“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 RollDice(int sides) 
     { 

      int result = (Rand.Next(1, sides + 1)); 
      return result; 
     } 
     public void ConsoleWelcomeMessage(string gameName, string playerName = "\b") 
     { 
      Console.WriteLine("Welcome " + playerName + " to " + gameName + "!"); 

     } 
     public string ConsoleGetName() 
     { 
      Console.WriteLine(); 
      Console.Write("Type your name: "); 
      string name = Console.ReadLine(); 
      Console.WriteLine("Your name will be: " + name); 
      return name; 
     } 
     public int ConsoleGetDifficulty(int min, int max) 
     { 
      bool done = false; 
      int difficulty = 1; 

      Console.WriteLine(); 
      Console.Write("Choose your difficulty from " + min + " to " + max + ": "); 
      while (done == false) 
      { 
       try 
       { 
        string input = Console.ReadLine(); 
        difficulty = int.Parse(input); 
        if (difficulty < max + 1 && difficulty > min - 1) 
        { 
         done = true; 
        } 
        else 
        { 
         //Ends the try block with an impossible action (bool.Parse) 
         bool tester = bool.Parse(input); 

        } 
       } 
       catch 
       { 
        Console.Write("Enter a valid number: "); 
       } 
      } 
      Console.WriteLine("Your difficulty will be: " + difficulty); 
      return difficulty; 
     } 

     public int Distance(int x1, int x2, int y1, int y2) 
     { 
      return (int)(Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))); 
     } 

     public void Test() 
     { 

     } 
     #endregion 



    } 
} 

我遇到了这个问题,我自己决定做一个名为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; 
    } 
} 

只是拉伸白色像素。

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

我想绘制光线,这样我就可以调试由爆炸创建的光线以及它们与物体相交的位置。这将在两点之间绘制单个像素细线。这是我做的:

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

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

列表来存储要绘制的射线:

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

创建一个BasicEffect,并在LoadContent方法所需的分辨率传递给它一个“Matrix.CreateOrthographicOffCenter”投影。所以当爆炸在我的游戏,会发生

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); 
} 

这是很简单的(它可能看起来方式更比它复杂),并且很容易将它放入一个单独的课程中,您再也不必考虑。它还可以让你一次绘制大量的线条。