xna
Milkstone Studios featured on”Around The World in 80 Games”
We’ve been featured on the “Around The World in 80 Games” spotlight, where the XNA team interviews developers from different countries.
Creating 2D geometry from a line strip
During development of an old prototype that never saw the light, I found the need to create some 2D geometry from a set of points that form a line.
After searching for any algorithm that could make what I needed, with no success, I decided to develop my own.
The algorithm is very simple, it just takes a rope and a width and creates a set of vertices tha form a TriangleStrip to be rendered on screen.
/// <summary>
/// Triangulates a line, returning a list of vertices that form a triangle strip
/// </summary>
/// <param name="contour">List of points that form the line</param>
/// <param name="width">Desired with of the geometry</param>
/// <returns>list of vertices that form a triangle strip</returns>
public static List<Vector2> Process(ReadOnlyCollection<Vector2> contour,float width)
{
List<Vector2> result = new List<Vector2>();
Vector2 normal = Vector2.Zero ;
// Generate vertices for each point in the line
for (int i = 0; i < contour.Count; i++)
{
// First and last are special cases, the normal is calculated right away
if (i == contour.Count - 1)
normal = GameMath.Normal(contour[i] - contour[i - 1]);
else if (i == 0)
normal = GameMath.Normal(contour[1] - contour[0]);
else
{
// For the rest of points, determine the normal as the middle angle between the direction of the previous and next segments.
Vector2 delta1 = contour[i] - contour[i - 1];
Vector2 delta2 = contour[i + 1] - contour[i];
if ((delta1.Length()!=0)&&(delta2.Length()!=0))
{
delta1.Normalize();
delta2.Normalize();
normal = GameMath.Normal(delta1 + delta2);
}
}
// Add two vertices to the vertex list
result.Add(contour[i] - normal * width / 2f);
result.Add(contour[i] + normal * width / 2f);
}
return result;
}
/// <summary>
/// Returns one normal of the 2D vector
/// </summary>
/// <param name="vector"></param>
/// <returns></returns>
public static Vector2 Normal(Vector2 line)
{
float x = line.X; float y = line.Y;
Vector2 normal = new Vector2();
if (x != 0)
{
normal.X = -y / x;
normal.Y = 1 / (float)Math.Sqrt(normal.X * normal.X + 1);
normal.Normalize();
}
else if (y != 0)
{
normal.Y = 0;
normal.X = (line.Y<0) ? 1 : -1;
}
else
{
normal.X = 1;
normal.Y = 0;
}
if (x < 0)
{
normal *= -1;
}
return normal;
}
That code is well enough to generate a credible line in almost all situations. As for UVs, what I did was assign the even vertices the value (X,0) and odd vertices (X,1), where X is the distance elapsed since the line start.
Plain XML content in XNA Content Pipeline
During the first stages of Little Racers development, I found the need of parsing XML data during the game load. The XNA Content Pipeline system includes XML Parsing, but it uses it in a very particular way. I won’t go much further in the way XNA parses XML because, to be sincere, I’ve not tried it enough to test wether it’s good or not.
My first impression was that it was too strict (probably due to optimization and type safe issues), moreover during parse I would need access to some data structures that only were available at runtime, so I preferred to store plain XML and parse it during game load.
This can be done by extending the content pipeline and adding a new type. To do this we’ll need to add two new projects to our solution.
XmlSource project
The first project we’ll create is the one which has the class that stores our xml plain text. I’ve called it XmlSource.It will have two classes in total.
XmlSource
This class is a very simple one. It simply has a string with the xml plain text:
public class XmlSource
{
public XmlSource(string xmlCode)
{
this.xmlCode = xmlCode;
}
private string xmlCode;
public string XmlCode { get { return xmlCode; } }
}
XmlSourceReader
This class extends ContentTypeReader and simply creates an XmlSource instance from its binary representation. In our case, the binary representation wil be a simple string, so the read is straightforward.
public class XmlSourceReader : ContentTypeReader<XmlSource>
{
/// <summary>
/// Loads an imported shader.
/// </summary>
protected override XmlSource Read(ContentReader input, XmlSource existingInstance)
{
string xmlData = input.ReadString();
return new XmlSource(xmlData);
}
}
With this, our first project is finished. This project is platform-independant (The Xbox needs to read the binary data) and will have to be referenced from every project that uses the XmlSource class, including the second project we’re making:
XmlSourceImporter
The second project(I called it XmlDocumentImporter) is a project of type “Content Pipeline Extension Library”. This project is referenced from the Content projects, and converts the source text file to binary data that will be saved in the .xnb files.
This project has 2 files:
XmlSourceImporter.cs
This file converts the XML source file into an XmlSource instance, that will be further converted into binary data
[ContentImporter(".xml", DisplayName = "Xml Source Importer")]
class XmlSourceImporter : ContentImporter<XmlSource>
{
public override XmlSource Import(string filename, ContentImporterContext context)
{
string sourceCode = System.IO.File.ReadAllText(filename);
return new XmlSource(sourceCode);
}
}
XmlSourceWriter.cs
This file converts an XmlSource into binary data, as opposed to XmlSourceReader
[ContentTypeWriter]
class XmlSourceWriter : ContentTypeWriter<XmlSource>
{
protected override void Write(ContentWriter output, XmlSource value)
{
/*StringWriter sw=new StringWriter();
value.Save(sw);
string content = sw.ToString();*/
output.Write(value.XmlCode);
}
public override string GetRuntimeType(TargetPlatform targetPlatform)
{
return typeof(XmlDocument).AssemblyQualifiedName;
}
public override string GetRuntimeReader(TargetPlatform targetPlatform)
{
return typeof(XmlDocumentReader).AssemblyQualifiedName;
}
}
Sample of use
Once both projects set up (remember to add reference to XmlSourceImporter from the Content project), we can add .xml files to the content project. Under “Content importer”, we’ll need to select “Xml Source importer”, instead of the default XNA one.
Once done this, the program sould compile ok. Loading XML from the program now is very easy:
XmlSource xs = Content.Load<XmlSource>(AssetName); XmlDocument xd = new XmlDocument(); xd.LoadXml(xs.XmlCode);
I know this can be a little confusing, i’ll upload some binaries if someone asks for them.
PS: Here they are: Plain Xml Content Importer
Follow us
Games
- Retro game of the week: Duke Nukem 1
http://www.youtube.com/watch?v=c4a3_wazRPA2010/09/06 06:59 - For those who didn't notice, our games Wool and MotorHEAT are finalists in 3 categories at the Hó Play International Videogame Festival!2010/09/02 08:27
- Check out this nice review of MotorHEAT!
http://fb.me/FdZoypY22010/09/02 08:34 - Our games Wool and MotorHEAT are nominees at the Hó Play International Festival!
http://twtn.gs/bkr2010/08/31 09:01 - Retro Game of the Week: Jumping Flash! by Exact and Ultra (PlayStation)
http://www.youtube.com/watch?v=N2hukvZ0zBw2010/08/30 12:35 - Retro Game of the Week: Transport Tycoon Deluxe
http://www.youtube.com/watch?v=HluOjdtxmNU2010/08/23 06:05 - Congratulations to @brenes, he's the lucky winner of the 1600MP code!2010/08/21 12:11
- Check out Avatar Ninja, a highly rated Indie game for Xbox 360! - RT & Follow for a chance to win 1600MP!
http://bit.ly/acaB7W2010/08/20 20:40 - Check out Avatar Ninja, a highly rated Indie game for Xbox 360! RT & Follow for a chance to win 1600MP!
http://bit.ly/acaB7W2010/08/20 13:04 - Retro game of the week: Rise of the Triad, a First Person Shooter by Apogee
http://www.youtube.com/watch?v=eMf4KzlbzI82010/08/20 06:51







