11

I'm using monogame (which uses the XNA API interface) to write my game. So far it is great, but I have hit a snag on something that should be simple.

I need to draw a 2d square. But I only want the border (No fill).

I have seen many examples that show how to do a filled one. But none that will show a border only.

I suppose I can make an image and use that. But I doubt it will resize well.

user1306322
  • 8,561
  • 18
  • 61
  • 122
Vaccano
  • 78,325
  • 149
  • 468
  • 850

4 Answers4

8

I just created an extension method for Texture2D in this way:

static class Utilities {
    public static void CreateBorder( this Texture2D texture,  int borderWidth, Color borderColor ) {
        Color[] colors = new Color[ texture.Width * texture.Height ];

        for ( int x = 0; x < texture.Width; x++ ) {
            for ( int y = 0; y < texture.Height; y++ ) {
                bool colored = false;
                for ( int i = 0; i <= borderWidth; i++ ) {
                    if ( x == i || y == i || x == texture.Width - 1 - i || y == texture.Height - 1 - i ) {
                        colors[x + y * texture.Width] = borderColor;
                        colored = true;
                        break;
                    }
                }

                if(colored == false)
                    colors[ x + y * texture.Width ] = Color.Transparent;
            }
        }

        texture.SetData( colors );
    }
}

Then I tested it:

//...

protected override void Initialize( ) {
   // TODO: Add your initialization logic here
   square = new Texture2D( GraphicsDevice, 100, 100 );
   square.CreateBorder( 5, Color.Red );

   base.Initialize( );
}

//...

protected override void Draw( GameTime gameTime ) {
   GraphicsDevice.Clear( Color.CornflowerBlue );

   // TODO: Add your drawing code here
   spriteBatch.Begin( );
   spriteBatch.Draw( square, new Vector2( 0.0f, 0.0f ), Color.White );
   spriteBatch.End( );

   base.Draw( gameTime );
}

The result is the following:

enter image description here

Blau
  • 5,742
  • 1
  • 18
  • 27
Omar
  • 16,329
  • 10
  • 48
  • 66
  • This did work but it overrides my texture drawing. So +1 because it worked but I was trying to find something that would draw a box around/over my texture so I can see where my boundries are. – Mastro Dec 01 '13 at 05:27
  • To avoid the texture overriding just draw the square first. It should work, let me know. – Omar Dec 16 '13 at 13:52
  • 1
    @Fuex [*Bat-Signal*](http://en.wikipedia.org/wiki/Bat-Signal): Sam on Game Dev SE [seems to have](http://gamedev.stackexchange.com/questions/82847/issue-with-drawing-an-outline-around-a-sprite-in-xna) picked up your code and found an issue with it. I haven't got XNA installed so I can't play with it, but if you have time, could you have a peek? – Anko - inactive in protest Sep 02 '14 at 23:22
  • @Anko Pixel color addresing is bad, should be x + y*Width. it works on a square but not on a rectangle – Blau Sep 04 '14 at 08:03
  • @Blau For a square it doesn't create problems because height and width are equals. Althought, fixed it for a rectangle, thank you. – Omar Sep 09 '14 at 17:47
8

Maybe you can use a 1x1 pixel texture to draw the individual edges with a sprite batch. In XNA, you could do this:

class RectangleSprite
{
    static Texture2D _pointTexture;
    public static void DrawRectangle(SpriteBatch spriteBatch, Rectangle rectangle, Color color, int lineWidth)
    {
        if (_pointTexture == null)
        {
            _pointTexture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1);
            _pointTexture.SetData<Color>(new Color[]{Color.White});
        }

        spriteBatch.Draw(_pointTexture, new Rectangle(rectangle.X, rectangle.Y, lineWidth, rectangle.Height + lineWidth), color);
        spriteBatch.Draw(_pointTexture, new Rectangle(rectangle.X, rectangle.Y, rectangle.Width + lineWidth, lineWidth), color);
        spriteBatch.Draw(_pointTexture, new Rectangle(rectangle.X + rectangle.Width, rectangle.Y, lineWidth, rectangle.Height + lineWidth), color);
        spriteBatch.Draw(_pointTexture, new Rectangle(rectangle.X, rectangle.Y + rectangle.Height, rectangle.Width + lineWidth, lineWidth), color);
    }     
}

The resulting drawing will be lineWidth pixels wider (and higher) than the passed rectangle.

enter image description here

The above was drawn with this code:

sb.Begin(); //spritebatch
RectangleSprite.DrawRectangle(sb, new Rectangle(10, 10, 50, 100), Color.Red, 1);
RectangleSprite.DrawRectangle(sb, new Rectangle(30, 20, 80, 15), Color.Green, 4);
RectangleSprite.DrawRectangle(sb, new Rectangle(70, 40, 40, 70), Color.Blue, 6);
sb.End();
neeKo
  • 4,280
  • 23
  • 31
4

I made an utility class for drawing primitives. You might find it useful

static public class PrimiviteDrawing
{
    static public void DrawRectangle(Texture2D whitePixel, SpriteBatch batch, Rectangle area, int width, Color color)
    {
        batch.Draw(whitePixel, new Rectangle(area.X, area.Y, area.Width, width), color);
        batch.Draw(whitePixel, new Rectangle(area.X, area.Y, width, area.Height), color);
        batch.Draw(whitePixel, new Rectangle(area.X + area.Width - width, area.Y, width, area.Height), color);
        batch.Draw(whitePixel, new Rectangle(area.X, area.Y + area.Height - width, area.Width, width), color);
    }
    static public void DrawRectangle(Texture2D whitePixel, SpriteBatch batch, Rectangle area)
    {
        DrawRectangle(whitePixel, batch, area, 1, Color.White);
    }
    public static void DrawCircle(Texture2D whitePixel, SpriteBatch spritbatch, IntegerVector2 center, float radius, Color color, int lineWidth = 2, int segments = 16)
    {
        Vector2[] vertex = new Vector2[segments];

        double increment = Math.PI * 2.0 / segments;
        double theta = 0.0;

        for (int i = 0; i < segments; i++)
        {
            vertex[i] = center.ToVector2() + radius * new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta));
            theta += increment;
        }

        DrawPolygon(whitePixel, spritbatch, vertex, segments, color, lineWidth);
    }
    public static void DrawPolygon(Texture2D whitePixel, SpriteBatch spriteBatch, Vector2[] vertex, int count, Color color, int lineWidth)
    {
        if (count > 0)
        {
            for (int i = 0; i < count - 1; i++)
            {
                DrawLineSegment(whitePixel, spriteBatch, vertex[i], vertex[i + 1], color, lineWidth);
            }
            DrawLineSegment(whitePixel, spriteBatch, vertex[count - 1], vertex[0], color, lineWidth);
        }
    }
    public static void DrawLineSegment(Texture2D whitePixel, SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, int lineWidth)
    {
        float angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
        float length = Vector2.Distance(point1, point2);

        spriteBatch.Draw(whitePixel, point1, null, color,
        angle, Vector2.Zero, new Vector2(length, lineWidth),
        SpriteEffects.None, 0f);
    }
}
Pacha
  • 1,438
  • 3
  • 23
  • 46
3

Here's how i'd do it:

    private void OutLine_Box(SpriteBatch sbatch, Rectangle rect)
    {
        List<Vector2> pixels = new List<Vector2>();
        for (int x = rect.X; x < rect.X + rect.Width; x++)
        {
            for (int y = rect.Y; y < rect.Y + rect.Height; y++)
            {
                if (y == rect.Y || y == rect.Y + rect.Height - 1)
                    pixels.Add(new Vector2(x, y));
                if (x == rect.X || x == rect.X + rect.Width - 1)
                    pixels.Add(new Vector2(x, y));
            }
        }

        foreach (Vector2 v in pixels)
            sbatch.Draw(PixelTexture, v, OutLineColor);
    }
Josh Siegl
  • 735
  • 1
  • 9
  • 16