2008-11-06 13 views
43

Przeczytałem kilka tutoriali z udziałem XNA (i jego różnych wersji) i nadal jestem trochę zdezorientowany w rysowaniu prymitywów. Wszystko wydaje się być naprawdę zawiłe.Jak rysować linie za pomocą XNA?

Czy ktoś może mi pokazać, używając kodu, najprostszą implementację XNA do rysowania jednej lub dwóch linii na ekranie? Być może po krótkim objaśnieniu (w tym również na karcie podstawowej)?

Nie jestem programistą gier i mam niewielkie doświadczenie XNA. Moim ostatecznym celem jest narysowanie niektórych linii na ekranie, które ostatecznie przekształcę za pomocą rotacji itp. (Ręcznie). Jednak w tym pierwszym kroku ... Muszę po prostu narysować linie! Pamiętam w moich dawnych czasach OpenGL, że było to dość proste przy rysowaniu linii za pomocą kilku wywołań metod. Czy powinienem po prostu powrócić do korzystania z niezarządzanych połączeń DirectX?

Odpowiedz

19

Podczas pracy z XNA wszystko (nawet 2d prymitywy) musi być wyrażone w sposób zrozumiały dla karty 3d, co oznacza, że ​​linia jest tylko zbiorem wierzchołków.

MSDN ma dość dobrą instruktażu tutaj:

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

Znajdziesz że potrzeba więcej kodu do renderowania prymitywną linię niż zajęłoby tylko setup teksturą quad i obracać, że skoro w istocie, robisz to samo podczas renderowania linii.

+1

@ Jonathan Holland, Dzięki za link i wyjaśnienie. – mmcdole

6

Cóż, możesz to zrobić w bardzo prosty sposób, nie dostając się do trójwymiarowych, strasznych rzeczy wektorowych.

Wystarczy stworzyć krótki fakturę, na przykład:

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

A potem po prostu narysować linię przy użyciu tego Tekstura:

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

Mam nadzieję, że to pomoże

+1

Działa tylko dla linii poziomych i pionowych, oczywiście. OP chce je obracać, więc to prawdopodobnie mu nie pomoże. –

16

obserwuję Odpowiedź NoHayProblema (nie mogę jeszcze komentować).

Ta odpowiedź, mimo że poprawna dla tego starego pytania, jest niekompletna. Konstruktor Texture2D zwraca niezainicjowaną teksturę, która nigdy nie jest pomalowana na ekranie. W celu wykorzystania tego podejścia, trzeba ustawić dane fakturze jest tak:

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

Weź pod uwagę, że sposób można napisać dane do piksela musi być zgodna z SurfaceFormat fakturze za.Przykład działa, ponieważ tekstura jest formatowana jako RGB. Obroty mogą być stosowane w spriteBatch.Draw tak:

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

Prostsze niż edycja danych pikseli jako wartość szesnastkowa jest użycie koloru jako danych tekstury. Coś takiego: var txPixel = new Texture2D (GraphicsDevice, 1, 1); txPixel.SetData (nowy kolor [1] {Color.White}); – krolth

+3

jeszcze lepiej: 'txPixel.SetData (nowy [] {Color.White});' –

6

znaleźć samouczek dla tego http://www.bit-101.com/blog/?p=2832

jego użyciem BasicEffect (shader) i wbudowaną obsługą dobieranego prymitywny w XNA 4.0

niektóre próbki kodu i znaleźć pomocne:

metoda zawartość obciążenie

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

metoda draw

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

bawić i głosować w górę, jeśli to ci pomogła. również odwiedź tutorial, z którego to zrobiłem.

4

Najprostszym najlepszy sposób, jak sądzę, jest uzyskanie obrazu tylko biały piksel następnie rozciągnąć ten piksel w prostokącie wyglądać linii

Zrobiłem klasę linia,

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

Nadzieja to pomaga

+0

UpVote! Ta klasa jest bardzo pomocna podczas debugowania. Dzięki! – Vlad

0

Oto prosty sposób, że mogę używać, aby linie przez podanie współrzędnych początek, koniec współrzędnych, szerokość i kolor z nich:

UWAGA: musi dodać plik o nazwie "dot" do katalogu zawartości (linia zostanie utworzona z tych).

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 



    } 
} 
0

Napotkałem ten problem na siebie i zdecydowałem się stworzyć klasę o nazwie LineBatch. LineBatch będzie rysował linie bez potrzeby stosowania spriteBatch lub kropek. Klasa jest poniżej.

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

Wystarczy rozciągnąć biały piksel.

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

Chciałem narysować promienie, aby móc debugować promienie powstałe w wyniku eksplozji i gdzie przecinają się obiekty. Spowoduje to narysowanie cienkiej linii pojedynczego piksela między dwoma punktami. Oto co zrobiłem:

Klasa do przechowywania prostych danych promienia. Domyślna klasa XNA ray może działać, ale nie przechowuje długości promienia do przecięcia.

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

Lista przechowywać promienie, które mają być sporządzone:

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

Tworzenie BasicEffect i przekazać go „Matrix.CreateOrthographicOffCenter” projekcję z żądaną rozdzielczość w metodzie LoadContent.

Następnie uruchom to w metodzie 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(); 
} 

Więc kiedy wybuch dzieje się w mojej grze robi to (Psuedocode):

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

To dość proste (To prawdopodobnie wygląda sposób bardziej skomplikowany niż jest) i łatwo byłoby umieścić go w osobnej klasie, o której już nigdy nie będziesz musiał myśleć. Pozwala także narysować wiele linii naraz.

Powiązane problemy