Drawing 2D Lines as Rotated Quads
Feb 7th
I haven’t had much time lately with work but one question I’ve seen asked many many times is how to draw lines of different widths. So, to cut to the chase I’ll share the code I’ve used to do it.
public void DrawLine(Vector3 p1, Color c1, Vector3 p2, Color c2, int width)
{
float distance = Vector3.Distance(p1, p2);
float halfDistance = distance / 2.0f;
float halfWidth = width / 2.0f;
Vector3 difference = p2 - p1;
Vector3 destination = new Vector3(p1.X + difference.X / 2.0f, p1.Y + difference.Y / 2.0f, p1.Z + difference.Z);
// Calculate angle between two points
float angle = (float)Math.Atan2(difference.Y, difference.X);
Vector3 v1, v2, v3, v4;
v1 = new Vector3(-halfDistance, -halfWidth, 0); // Top Left
v2 = new Vector3(halfDistance, -halfWidth, 0); // Top Right
v3 = new Vector3(halfDistance, halfWidth, 0); // Bottom Right
v4 = new Vector3(-halfDistance, halfWidth, 0); // Bottom Left
Matrix m =
Matrix.Identity *
Matrix.CreateRotationZ(angle) *
Matrix.CreateTranslation(destination);
v1 = Vector3.Transform(v1, m);
v2 = Vector3.Transform(v2, m);
v3 = Vector3.Transform(v3, m);
v4 = Vector3.Transform(v4, m);
DrawQuad(v1, c1, v2, c2, v3, c2, v4, c1);
}
I’ve left a lot of fluff code out. I usually check if the line is a width of 1 and draw a normal line. I also left out the code on how to draw a quad as that can be found many other places already.
Let me google that for you
Jan 30th
Ever read anything on a forum and thought, “I know that’s one of the first results in Google”. Well, then send your response with Let me google that for you. It’s a great way to let people know that google is smarter than you and him.
Paint Wars
Jan 23rd
I was listening to “.NET Rocks” show #414 this morning and there was an interview with Chris Marinos. While in college he wrote an Xna game that uses the WiiMote as an input device. He’s recently ported the code to F#. It sounded really interesting so go check it out.
One thing that was said in the interview that I didn’t really agree with though, Xna was said to be able to “run Xbox 360 games on your PC”. This isn’t right and it portrays Xna in the wrong way to me. Xna is Microsoft’s managed DirectX solution. Although it is right now geared more toward the game community, it can still be used for other purposes.
Jon Skeet
Jan 15th
I just received my copy of C# In Depth by Jon Skeet. Jon has a great deal of knowledge on C# and if you’ve never ran into his blog I suggest you check it out here. I’ll post later more about the book but I’m pretty sure it’s a good one.
SpriteSheet Class
Jan 9th
I’ve been talking with a guy on the creator forums lately about SpriteSheets and so I decided it might be a good idea to post my SpriteSheet class.
It’s very simple. Only reads sprites from left to right and assumes all Sprites are the same width and height.
#region Using
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion
namespace Snow.Xna.Graphics
{
///
/// Spritesheet class.
///
public class SpriteSheet
{
#region Fields
string name;
Texture2D texture;
Rectangle[] rectangles;
int spriteWidth, spriteHeight;
#endregion
#region Properties
///
/// The name of this SpriteSheet.
///
public string Name
{
get { return name; }
}
///
/// The texture for this SpriteSheet.
///
public Texture2D Texture
{
get { return texture; }
}
///
/// Returns a rectangle for a sprite in the SpriteSheet.
///
///
index
///
public Rectangle this[int i]
{
get { return rectangles[i]; }
}
///
/// The number of sprites in this SpriteSheet.
///
public int Count
{
get { return rectangles.Length; }
}
///
/// The width of the texture.
///
public int Width
{
get { return texture.Width; }
}
///
/// The width of each sprite in the SpriteSheet.
///
public int SpriteWidth
{
get { return spriteWidth; }
}
///
/// The height of the texture.
///
public int Height
{
get { return texture.Height; }
}
///
/// The height of each sprite in the SpriteSheet.
///
public int SpriteHeight
{
get { return spriteHeight; }
}
#endregion
///
/// Create a new SpriteSheet and determine the number of sprites in the sheet.
///
///
///
Width of each sprite.
///
Height of each sprite.
public SpriteSheet(string name, Texture2D texture, int spriteWidth, int spriteHeight)
: this(name, texture, spriteWidth, spriteHeight, 0)
{
}
///
/// Create a new SpriteSheet.
///
///
///
Width of each sprite.
///
Height of each sprite.
///
The number of sprites in the sheet.
public SpriteSheet(string name, Texture2D texture, int spriteWidth, int spriteHeight, int count)
{
this.name = name;
this.texture = texture;
this.spriteWidth = spriteWidth;
this.spriteHeight = spriteHeight;
if(count == 0)
{
int numX = texture.Width / spriteWidth;
int numY = texture.Height / spriteHeight;
rectangles = new Rectangle[numX * numY];
}
else
{
rectangles = new Rectangle[count];
}
int x = 0, y = 0;
for(int i = 0; i < rectangles.Length; i++)
{
rectangles[i] = new Rectangle(x, y, spriteWidth, spriteHeight);
x += spriteWidth;
if(x >= texture.Width)
{
x = 0;
y += spriteHeight;
}
}
}
public static implicit operator Texture2D(SpriteSheet spriteSheet)
{
return spriteSheet.Texture;
}
}
}
You can create a new SpriteSheet and use it like this:
SpriteSheet spriteSheet = new SpriteSheet("tiles", Content.Load("tiles"), 64, 64);
spriteBatch.Begin();
spriteBatch.Draw(spriteSheet,
new Rectangle(0, 0, spriteSheet.SpriteWidth, spriteSheet.SpriteHeight),
spriteSheet[0],
Color.White);
spriteBatch.End();
Which loads a spritesheet with sprites of size 64×64. It then draws the first Sprite in the SpriteSheet. You of course wouldn’t want to load the spritesheet every frame as well.
Feel free to use this code without restriction.
Edit: I copied and pasted the second piece of code from somewhere else so I fixed two typos.
It Works
Dec 30th
Ok, so the old laptop I’m working with isn’t ideal, but it does work and does run Xna Game Studio. I’ll probably only being doing 2D stuff on it. I did look into getting a bit more ram for it though as it only has 512 right now and most of that is used up just by the OS and the minimal apps running in the background.
Colors and Hex
Dec 20th
I recently needed to write out Color(s) as an xml attribute. I wrote 2 methods to read and write the Color(s) as Hex strings. Here ya go:
namespace Snow.Xna.Graphics
{
public static class ColorHelper
{
private static char[] _hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static string ToHexString(Color color)
{
byte[] bytes = new byte[4];
bytes[0] = color.A;
bytes[1] = color.R;
bytes[2] = color.G;
bytes[3] = color.B;
char[] chars = new char[8];
for(int i = 0; i < 4; i++)
{
int b = bytes[i];
chars[i * 2] = _hexDigits[b >> 4];
chars[i * 2 + 1] = _hexDigits[b & 0xF];
}
return new string(chars);
}
private static byte HexDigitToByte(char c)
{
switch(c)
{
case '0': return (byte)0;
case '1': return (byte)1;
case '2': return (byte)2;
case '3': return (byte)3;
case '4': return (byte)4;
case '5': return (byte)5;
case '6': return (byte)6;
case '7': return (byte)7;
case '8': return (byte)8;
case '9': return (byte)9;
case 'A': return (byte)10;
case 'B': return (byte)11;
case 'C': return (byte)12;
case 'D': return (byte)13;
case 'E': return (byte)14;
case 'F': return (byte)15;
}
return (byte)0;
}
public static Color FromHexString(string hex)
{
if( hex.Length != 8 )
return Color.Black;
int a = (HexDigitToByte(hex[0]) << 4) + HexDigitToByte(hex[1]);
int r = (HexDigitToByte(hex[2]) << 4) + HexDigitToByte(hex[3]);
int g = (HexDigitToByte(hex[4]) << 4) + HexDigitToByte(hex[5]);
int b = (HexDigitToByte(hex[6]) << 4) + HexDigitToByte(hex[7]);
return new Color((byte)r, (byte)g, (byte)b, (byte)a);
}
}
}
XNExtensions
Dec 8th
Nick Gravelyn has released an open source set of C# extension methods for use with Xna. See the post here for more information.
http://www.nickontech.com/2008/12/new-open-source-project-time/
Zend_Db_Table as a Model
Jun 20th
In the Zend Framework, using Zend_Db_Table as your model class is not well advised. This practice can force you to put business logic in other places besides your model class. This may not be a big deal if your logic is simple but imagine the logic is fairly complicated. Now also imagine that the logic changes at some point. This can be a problem if you have this logic written in many different places.
There’s a solution to this problem though. You could write a Model class that uses Zend_Db_Table as a storage medium. Or, you can extend Zend_Db_Table to add your logic to the class. There is a way to simplify this process and that’s what this blog post is about.
