2008-11-06 11 views
43

He leído un montón de tutoriales relacionados con XNA (y sus diversas versiones) y todavía estoy un poco confundido con el dibujo de primitivos. Todo parece ser complicado.¿Cómo dibujo líneas usando XNA?

¿Alguien me puede mostrar, mediante código, la implementación XNA más simple de dibujar una o dos líneas en la pantalla? Tal vez con una breve explicación (incluido el texto repetitivo)?

No soy un programador de juegos y tengo poca experiencia en XNA. Mi objetivo final es dibujar algunas líneas en la pantalla que eventualmente transformaré con rotaciones, etc. (a mano). Sin embargo, para este primer paso ... ¡necesito simplemente dibujar las líneas! Recuerdo que en mis antiguos días OpenGL era bastante sencillo cuando trazaba una línea con algunas llamadas a métodos. ¿Debería volver a usar llamadas directx no administradas?

Respuesta

19

Al trabajar con XNA, todo (incluso primitivas 2d) debe expresarse de forma que una tarjeta 3D pueda entender, lo que significa que una línea es simplemente un conjunto de vértices.

MSDN tiene un muy buen tutorial aquí:

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

Usted encontrará que se necesita algo más de código para hacer una línea primitiva de lo que se necesitaría para acaba de configurar un quad textura y rotar que, desde en esencia, haces lo mismo cuando tratas una línea.

+1

@Jonathan Holland, gracias por el enlace y la explicación. – mmcdole

6

Bueno, puede hacerlo de una manera muy sencilla sin tener que meterse en el vector horrible 3D.

Basta con crear una textura rápida, por ejemplo:

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

Y luego simplemente dibujar una línea usando esa textura:

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

espero que esto ayude a

+1

Solo funciona para líneas horizontales y verticales, por supuesto. OP quiere rotarlos, y probablemente esto no lo ayude. –

16

Siguiendo La respuesta de NoHayProblema (no puedo comentar todavía).

Esa respuesta, aunque es la correcta para esta pregunta anterior, está incompleta. El constructor Texture2D devuelve una textura no inicializada, que nunca se pinta en la pantalla. Para utilizar este enfoque, debe configurar los datos de la textura de esta manera:

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

tener en cuenta que la forma de escribir los datos en píxeles debe ser consistente con SurfaceFormat de la textura.El ejemplo funciona porque la textura se está formateando como RGB. Las rotaciones se pueden aplicar en spriteBatch.Draw así:

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

Más simple que editar los datos de píxeles como un valor hexadecimal es usar Color como los datos de textura. Algo como esto: var txPixel = new Texture2D (GraphicsDevice, 1, 1); txPixel.SetData (nuevo Color [1] {Color.White}); – krolth

+3

aún mejor: 'txPixel.SetData (nuevo [] {Color.White});' –

6

encontró un tutorial para que http://www.bit-101.com/blog/?p=2832

su utilizando un BasicEffect (shader) y el construido en el sorteo de usuario primitivo en XNA 4.0

algunos ejemplos de código me parece útil:

método contenido de la carga

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

método 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); 

divertirse y votar hasta si esto le ayudó. también haga una visita al tutorial del que obtuve esto.

4

La mejor manera simple, creo, es conseguir la imagen de sólo un píxel blanco a continuación, estirar ese píxel en un rectángulo para parecerse a una línea

Hice una clase de línea,

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

esperanza esto ayuda

+0

UpVote! Esta clase es muy útil para mi depuración. ¡Gracias! – Vlad

0

Aquí es una forma sencilla que utilizo para hacer líneas especificando una coordenada de inicio, coordinar su fin, el ancho y el color de ellos:

NOTA: se debe agregar un archivo llamado "punto" al directorio de contenido (la línea estará compuesta de estos).

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

Me encontré con este problema y decidí hacer una clase llamada LineBatch. LineBatch dibujará líneas sin necesidad de un spriteBatch o puntos. La clase está debajo.

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

Simplemente estire un píxel blanco.

 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

Quería hacer rayos para poder depurar los rayos creados por explosiones y donde se cruzan objetos. Esto dibujará una línea fina de un solo píxel entre dos puntos. Esto es lo que hice:

Clase para almacenar algunos datos simples de rayos. La clase XNA de rayos por defecto podría funcionar, pero no almacena la longitud del rayo en la intersección.

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

Una lista para almacenar los rayos que van a ser dibujado:

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

Crear una BasicEffect y pasarlo una proyección "Matrix.CreateOrthographicOffCenter" con la resolución deseada en el método LoadContent.

A continuación, ejecute esto en el método 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(); 
} 

Por eso, cuando una explosión ocurre en mi juego lo hace (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); 
} 

Es bastante simple (Posiblemente se ve mucho más complicado de lo que es) y sería fácil ponerlo en una clase aparte en la que nunca más tendrás que pensar. También te permite dibujar un montón de líneas a la vez.