How do I draw lines using XNA?

Viewed 63927

I've read a bunch of tutorials involving XNA (and it's various versions) and I still am a little confused on drawing primitives. Everything seems to be really convoluted.

Can someone show me, using code, the simplest XNA implementation of drawing one or two lines on to the screen? Perhaps with a brief explanation (including the boilerplate)?

I'm not a games programmer and I have little XNA experience. My ultimate goal is to draw some lines onto the screen which I will eventually transform with rotations, etc (by hand). However, for this first step.. I need to simply draw the lines! I remember back in my ancient OpenGL days it was fairly straightforward when drawing a line with a few method calls. Should I simply revert to using unmanaged directx calls?

10 Answers

When working with XNA, everything (even 2d primitives) have to be expressed in a way that a 3d card can understand, which means that a line is just a set of vertices.

MSDN has a pretty good walkthrough here:

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

You'll find that it takes more code to render a primitive line than it would take to just setup a textured quad and rotate that, since in essence, your doing the same thing when rendering a line.

Following NoHayProblema's answer (I cannot comment yet).

That answer, although the correct one for this old question, is incomplete. Texture2D constructor returns an uninitialized texture, which is never painted on screen. In order to use that approach, you need to set the texture's data like this:

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

Take into account that the way you write the data into pixel must be consistent with the texture's SurfaceFormat. The example works because the texture is being formatted as RGB. Rotations can be applied in spriteBatch.Draw like this:

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

The simplest best way, I think, is to get the image of just a white pixel then stretch that pixel in a rectangle to look like a line

I made a Line class,

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

Hope this helps

Here is a simple way that I use to make lines by specifying a start coordinate, an end coordinate, width, and color of them:

NOTE: you must add a file named "dot" to the content directory (the line will be made out of these).

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



    }
}
Related