Showing posts with label Tutorials. Show all posts
Showing posts with label Tutorials. Show all posts

11/17/07

VB.NET XNA Tutorial 8: Text

[GSE 1.0 Refresh]

Text as sprites

In XNA, text gets drawn to the screen in much the same way everything else in our 2D engine is being drawn to the screen: by having SpriteBatch draw sprites.

In order to put "Hello world!" on the screen, we'll need 8 regular sprites that just happen to look like characters. Specifically, we'll need the following sprites that look like:

  • H
  • e
  • l (we can use this one 3 times)
  • o (can be used twice)
  • w
  • r
  • d
  • !
Once we've made those sprites (and loaded them into the game), the next step will be to meticulously draw each one, in the right order, in the right place with perfect spacing, so when we're done we end up with words (and don't forget to offset "world!" from "Hello" to simulate a space character between them).

Sound tedious? How about an RPG with ten thousand lines of dialog. Sound daunting? If so, good! You're ready to appreciate the fact XNA can do all of the aforementioned work for you. Not only will it line up the little sprites on the screen for you, it can even create all the sprites for you.

MSBuild & SpriteFont

All we've asked MSBuild to do for us so far is convert our texture content (.png or .jpg, etc.) into .xnb files. The .xnb file format is a "cross-platform" file that can be used on either Windows or the Xbox360. In light of the known compatibility issues with VB.NET and the .NET framework found on the XBox360, why are we bothering to use MSBuild to produce a "cross-platform" file, anyway?

If you're making an XNA app exclusively for Windows, ending up with "cross-platform" content is somewhat incidental (a side-effect of the process). On the other hand, a distinct advantage to using MSBuild and the ContentManager can be found in SpriteFonts.

A SpriteFont starts out life as a small XML file with a ".spritefont" extension that you place in your content folder. The same MSBuild process that takes our texutre images and creates .xnb files can also take a .spritefont file to create another .xnb file - one that contains the characters of a specific font, at a specific size, as described by the contents of the .spritefont XML file. Here's an example of a .spritefont XML file:

<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains an xml description of a font, and will be read by the XNA
Framework Content Pipeline. Follow the comments to customize the appearance
of the font in your game, and to change the characters which are available to draw
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">

<!--
Modify this string to change the font that will be imported.
-->
<FontName>Arial</FontName>

<!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
<Size>14</Size>

<!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
<Spacing>2</Spacing>

<!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
<Style>Regular</Style>

<!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
<CharacterRegions>
<CharacterRegion>
<Start>&#32;</Start>
<End>&#126;</End>
</CharacterRegion>
</CharacterRegions>
</Asset>
</XnaContent>

The C# Express IDE has native support for creating new .spritefont files, you just right-click the project and add one like you were adding a class file. With VB.NET, it's pretty much just as easy. There's no magic here, it's just an XML file. You can bring in an existing .spritefont file from disk, or just make a new text document item in your project, paste in the above code, and rename the file so it has a .spritefont extension. If you place it somewhere in your content folder, the VBContentManager understands to harvest .spritefont files and send them along to MSBuild. Similar to Textures, SpriteFonts are processed by MSBuild and the result is stored onto the VBContentManager as a SpriteFont object in the Fonts hashtable.

Implementation with the VBContentManager

Ok, so how do we use this "SpriteFont object" to get text onto the screen? It's a lot like drawing a texture; the SpriteBatch has a Draw() method for drawing textures, and it has a DrawString() method for drawing text to the screen. The DrawString() method accepts your SpriteFont object as a parameter (as well the text to render using the font, where to render it at, and what color to make it)


 'Draws "Hello world!" to the screen, using whatever font definition is found in
 'the file "test.spritefont" from the content folder.  Draws the string at the
 'upper-left corner of the screen in yellow.

 Me.objSB.Begin()

 Me.objSB.DrawString(Me.objCM.Fonts("test"), "Hello world!", Vector2.Zero, Color.Yellow)

 Me.objSB.End()


Without too much fuss, you'll just get the result you expect.

SpriteFont objects feature the MeasureString() method, which you may be familiar with already as it appears elsewhere in .NET, outside of XNA. MeasureString() is just a way to get the width and height of any string (but in a particular font and size), which can be handy for string-size related math (like centering text on the screen, for example).

In the screenshot below, I've placed a sprite font file in my project's Content\Fonts folder called test.spritefont. Test.spritefont specifies Arial at a size of 30. In the main draw loop of the game, I retrieve the SpriteFont object from the game's VBContentManager.Fonts hashtable, and use it's MeasureString method (in conjunction with the BackBufferWidth and BackBufferHeight) to center the text on the screen when calling SpriteBatch.DrawString:





A few quick extras re: the creation of SpriteFont files:

Mono-spaced fonts are not supported by Game Studio Express 1.0 (refresh) (the current version when I wrote this article). Last I heard, mono-spaced font support should make it into Game Studio 2.0.

As an aside, it's probably a reasonable speculation to guess that, due to Game Studio 2.0's compatibility with Visual Studio, it may be possible to simply add a SpriteFont file directly to a VB.NET XNA project after GS 2.0 is released (rather than having to "make your own"). Either way, we would continue to rely on something like the VBContentManager to invoke the MSBuild process in order to get the .xnb file created for us.

And finally, if you are interested in going beyond using the plain old fonts installed on your system, read this. My VBContentManager doesn't support what's discussed there, but you could certainly perform a quick & dirty modification along the lines of "if you find a texture in the fonts folder..." sort of thing.

11/16/07

VB.NET XNA Tutorial 7: Sprites as Game Objects

[GSE 1.0 Refresh]

Taking a second look at what was designed in Tutorial 6, Tutorial 7 explores how the Sprite class can be more than just a texture on the screen. We'll create a Ship class that derives from Sprite, and end up with something that's more of a "game object" than a "sprite".

This tutorial is only about 14 minutes and is pretty easy stuff; the main focus is basically re-usability of code via object-oriented design. Creating reusable game objects is a concept that will lend itself to things like UI components and Screens and eventually game state management (in future tutorials).

For now, here's Tutorial 7: Sprites as Game Objects.

11/15/07

VB.NET XNA Tutorial 6: The PointAt method

[GSE 1.0 Refresh]

In the comments for tutorial 5.3, Matt asked a practical question about how to solve a real-world problem using all this matrix cascade stuff; if a ship sprite had a "child" gun turret, how could we get that gun turret to aim at some other sprite on the screen?

Here's a method written to accomplish this task, called PointAt. Accepting two sprites as arguments, it will rotate the first in order to point at the second. This will cut through all the matrix clutter by turning that clutter around and using it to our advantage.

 ''' <summary>
 ''' Point one sprite's origin at another sprite's origin
 ''' </summary>
 ''' <param name="thePointer">The sprite you want to rotate</param>
 ''' <param name="theTarget">The sprite you want thePointer to point at</param>
 ''' <remarks></remarks>
 Public Sub PointAt(ByVal thePointer As Sprite, ByVal theTarget As Sprite)

 Dim objTargetWorldPosition As Vector2

 If theTarget.Parent IsNot Nothing Then
 objTargetWorldPosition = Vector2.Transform(theTarget.Location, theTarget.Parent.DrawMatrix)
 Else
 objTargetWorldPosition = theTarget.Location
 End If

 thePointer.Rotation = 0

 With Vector2.Transform(objTargetWorldPosition, Matrix.Invert(thePointer.DrawMatrix)) - thePointer.Origin

 thePointer.Rotation = (CSng(Math.Atan2(.Y, .X)) + (Math.PI / 2))

 End With

 End Sub

It looks simple, doesn't it? There's some new stuff in there (Matrix.Invert?) so I went ahead and made a video demonstrating what this "simple" method does, adding some slight difficulty for the PointAt method to overcome. The PointAt method is nice because it doesn't really care what we throw at it. It should be able to point any sprite at any other sprite, no matter where either sprite is or how far down any given matrix cascade either sprite happens to be.

Here is the link to Tutorial 6: The PointAt method on Vimeo.

In the video, the steps shown in Photoshop for inverting the matrix are oversimplified a little bit; in actuality, inverting the DrawMatrix of the cannon sprite would also involve un-doing the rotation of the PlayersShip (which would require a lot more steps to simulate in PhotoShop) but the net result is the same.

11/14/07

VB.NET XNA Tutorial 5.3: The Parent/Child Matrix Cascade

[GSE 1.0 Refresh]

While tutorial 5.3 encodes (...it really takes forever), I'll write a little preface. Things get a little "weird" in tutorial 5.3, so hopefully I'll be making sense.

In the previous tutorial we saw how a sprite can use a matrix to position itself on the screen. We drew kermit to the screen by supplying his matrix to the Begin method of the SpriteBatch, and then supplied his origin to the Draw method. These two separate pieces of data (the matrix and the origin) passed to those two methods worked together to put kermit where we wanted on the screen, and make him rotate how we wanted about his origin.

In tutorial 5.3, we're going merge origin information into the matrix, so that we no longer have our matrix and origin supplied as separate data pieces when we draw; we'll be able to pass a single matrix to Begin without supplying an origin value to Draw.

By incorporating the origin information into the matrix, the matrix becomes a more comprehensive/accurate/complete description of how to place a sprite on the screen. In turn, we can use that matrix to transform other things in addition to the sprite itself (such as the sprite's children).

So we're going to create a simple Parent/Child system, whereby each sprite has a reference to a parent sprite as well as a list of children sprites. This will give us a way to organize groups of sprites together into logical objects, and at the same time it will give us a vehicle for the "matrix cascade". Here's what a "matrix cascade" is designed to accomplish:

Dinner on a Plate

Dinner on a Plate demonstrates the net result of what our engine can do after tutorial 5.3. I thought it might be a good idea to get a clear picture of our goal, without which tutorial 5.3 can be, unfortunately, "a long trip through abstract city".

As you can see in Dinner on a Plate, it's starting to become pretty easy to build a new application that, if nothing else, demonstrates how a mildly complicated positioning exercise can be greatly simplified. In the video, a cookie and a piece of pizza are "put onto a dinner plate", and when the plate is rotated, the pizza and cookie behave as if they are intuitively attached to the plate. There are no laws of physics at work here, the explanation is simply that, as children of the dinner plate, the cookie and the pizza are subject to the dinner plates matrix cascade. This means that every time the dinner plate "changes" (location, rotation, origin or (eventually) scale), the cookie and pizza slice are automatically incorporating the new dinner plate matrix into their own matrices when they draw.

The "exciting" details can be found in Tutorial 5.3 - The Parent/Child Matrix Cascade.

11/5/07

VB.NET XNA Tutorial 5: Sprites

[GSE 1.0 Refresh]

Before we get into Screens, ScreenManagers and scripting interfaces, we have to create a Sprite class. It will be the base class that all of those things either inherit from, or are made to manipulate.

I've split the Sprite tutorial into a series of videos, since as it turns out there's a bit to cover. It's not too complicated, but since sprites are the heart & soul of a 2D game, making the Sprite class is a pretty important step.

In the 5.x series of videos below, we'll create a Sprite class from the ground up, and give it the ability to move around. Later on, we'll create Screen objects from this Sprite class, as well as hooking up a scripting interface that can move a Sprite or a Screen. For now, here are the Sprite class tutorials:

  • Tutorial 5.0: A Sprite class
    The basics; adding the Sprite class to the 2D Engine project. This one's pretty easy.

  • Tutorial 5.1: Implementing a transform matrix
    Incorporating a transform matrix into the Sprite class, and using it to put the sprite at various places on the screen. Doesn't go into much detail regarding matrices.

  • Tutorial 5.2: Coordinate spaces, Origins, and Matrices
    I take a crack at explaining how transformation matrices, screen coordinates, and sprite origin all come together for the 2D sprite class. Not necessarily specific to VB, I just think easy-to-follow, non-mathematical tutorials on this subject are in short supply so hopefully this one makes sense and helps someone out. And hopefully I'm not making too many errors...

  • Tutorial 5.3: Creating a parent/child system
    This link takes you to a subsequent post dedicated to the subject.
That's about all I can muster for the weekend. This sprite class gives us a very strong foundation upon which to grow and add some very cool features, so we're not done yet; more to come in the weeks ahead.

10/21/07

VB.NET XNA Tutorial 4: Starting an engine, and the VBContentManager

[GSE 1.0 Refresh]

Here's the link to Tutorial 4: Starting an engine on Vimeo.

This tutorial is a little bit longer and a little bit more involved than the past tutorials. We'll start down the road to separating game logic from game logistics by creating a new project called XNA 2D Engine and moving the "MyGame" class into it. We'll turn the MyGame into a base class to be used with the 2D engine.

We'll introduce the VBContentManager class, my implementation of a ContentManager extended with our "VB Content Pipeline hack" code (there's lots more on that, below). We'll then go on to making a primitive sprite in Photoshop and drawing it to the screen with a SpriteBatch object.

Here is some companion text to go with the video, that describes in detail some code I'll be importing but not spending a lot of time describing in the video:



In a standard C# XNA project, the default game class is created for you with a GraphicsDeviceManager and a ContentManager both instantiated and ready to go. As we saw in Tutorial 3, it's fairly easily make our own class that inherits from the base XNA Game class, create our own GraphicsDeviceManager and quickly reproduce what the C# template generates automatically.

What tutorial 3 did not touch on was the ContentManager, because for us VB'ers, the ContentManager is really the point where the lack of VB support in XNA requires us to do a little bit of legwork, so I held off on the subject until now.

In C#, the ContentManager class loads cross-platform file types (like .xnb files) that MSBuild creates automatically from things like .png or .jpg files that get included in the C# project. The primary issue when using a ContentManager with VB is getting these .xnb files created from our .png or .jpg files, because there is no automatic conversion process built into the IDE.

My approach to solving this problem was to borrow largely what's been documented by Alan Phipps, and from that, create my own ContentManager class called the VBContentManager class. Here's is the VBContentManager class, in it's entirety:

Public Class VBContentManager
 Inherits ContentManager

 Public Fonts As Hashtable(Of SpriteFont)
 Public Textures As Hashtable(Of Texture2D)
 Public SubTextures As Hashtable(Of Hashtable(Of Rectangle))
 Public Sounds As Hashtable(Of SoundBank)

 Private strFakeCSharpProject As String
 Private strContentRootFolder As String
 Private strPipelineRootFolder As String
 Private objContent As List(Of ContentFile)
 Private intDupeSound, intDupeTexture, intDupeFont As Integer

 Public Enum ContentTypes
 Textures = 0
 Fonts = 1
 Sounds = 2
 End Enum

 Private Enum ImporterName
 TextureImporter = 0
 FontDescriptionImporter = 1
 XactImporter = 2
 End Enum

 Private Enum ProcessorName
 SpriteTextureProcessor = 0
 FontDescriptionProcessor = 1
 XactProcessor = 2
 End Enum

 Public Structure ContentFile

 Public ContentType As ContentTypes
 Public File As System.IO.FileInfo
 Public Name As String

 Public Sub New(ByVal ContentType As ContentTypes, ByVal ContentFile As System.IO.FileInfo, ByVal Name As String)
 Me.ContentType = ContentType
 Me.File = ContentFile
 Me.Name = Name
 End Sub

 End Structure

 ''' <summary>
 ''' Creates a new ContentManager which has been extended with functionality to support a Visual Basic (or other non-C#) project
 ''' </summary>
 ''' <param name="ServiceProvider">Your game has a .Services property; send that in here.</param>
 ''' <param name="ContentRootFolder">The root of your content folder, relative to your projects bin\Debug folder.</param>
 ''' <remarks></remarks>
 Public Sub New(ByVal ServiceProvider As IServiceProvider, ByVal ContentRootFolder As String)

 MyBase.New(ServiceProvider)

 Me.strContentRootFolder = ContentRootFolder
 Me.strPipelineRootFolder = ContentRootFolder.Replace("../""")

 Me.Fonts = New Hashtable(Of SpriteFont)
 Me.Textures = New Hashtable(Of Texture2D)
 Me.SubTextures = New Hashtable(Of Hashtable(Of Rectangle))
 Me.Sounds = New Hashtable(Of SoundBank)

 End Sub

 ''' <summary>
 ''' Load all assets that have been compiled by a VBContentManager.
 ''' </summary>
 ''' <param name="Compile">Whether or not to have MSBuild compile your game content before attempting to load it</param>
 ''' <remarks></remarks>
 Public Sub LoadPipeline(ByVal Compile As Boolean)

 If Compile Then
 Me.CompileContent()
 End If

 For Each obj As ContentFile In Me.objContent

 Try

 Select Case obj.ContentType

 'The pipeline will be compiled into either debug or release, depending upon
 'which option is chosen in the IDE.  We don't want to use the My namespace
 'though, because it does not work with the xbox 360.

 Case ContentTypes.Fonts

 With obj.File.FullName.ToLower
 Me.Fonts.Add(obj.Name, Me.Load(Of SpriteFont)(.Substring(.LastIndexOf("\" & Me.strPipelineRootFolder.ToLower) + 1).Replace(obj.File.Extension.ToLower, "")))
 End With

 Case ContentTypes.Sounds

 With obj.File.FullName.ToLower
 Me.Sounds.Add(obj.Name, Me.Load(Of SoundBank)(.Substring(.LastIndexOf("\" & Me.strPipelineRootFolder.ToLower) + 1).Replace(obj.File.Extension.ToLower, "")))
 End With

 Case ContentTypes.Textures

 With obj.File.FullName.ToLower
 Me.Textures.Add(obj.Name, Me.Load(Of Texture2D)(.Substring(.LastIndexOf("\" & Me.strPipelineRootFolder.ToLower) + 1).Replace(obj.File.Extension.ToLower, "")))
 End With

 End Select

 Catch ex As Exception

 'msgBox("While loading the pipeline, a piece of content called " & obj.Name & " could not be found, and was skipped")

 'A new piece of content has been added, but the programmer failed
 'to instruct the VBContentPipeline to compile it.  So the content
 'will not be available during this instance of the Game.

 End Try

 Next

 End Sub

 Private Sub CompileContent()

 Dim objProcess As Process

 Me.objContent = New List(Of ContentFile)

 Me.HarvestContent()

 If Me.objContent.Count > 0 Then

 Me.StartTheFakeCSharpFile()

 Me.AddContentToTheFakeCSharpFile()

 Me.EndTheFakeCSharpFile()

 Try 'to write the fake C# project file to disk

 Using TempStreamWriter As System.IO.StreamWriter = New System.IO.StreamWriter(Me.strContentRootFolder & "\VBContentPipeline.csproj"False)

 With TempStreamWriter

 .Write(Me.strFakeCSharpProject)
 .Close()

 End With

 End Using

 Try 'to shell out to MSBuild.exe, sending it our fake c# project file

 objProcess = System.Diagnostics.Process.Start("""" & Environment.GetEnvironmentVariable("Windir") & _
 "\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe""""""" & Me.strContentRootFolder & _
 "\VBContentPipeline.csproj"" /verbosity:d /l:FileLogger,Microsoft.Build.Engine;logfile=""" & _
 Me.strContentRootFolder & "\VBPipelineBuild.log""")

 'wait for that to finish

 While Not objProcess.HasExited
 Threading.Thread.Sleep(100)
 End While

 Catch ex As Exception

 MsgBox("When building the pipeline and trying to run MSBuild.exe, " & ex.Message)

 End Try

 Catch ex As Exception

 MsgBox("When building the pipeline and trying to create the fake C# file """ & Me.strContentRootFolder & "\VBContentPipeline.csproj"", " & ex.Message)

 End Try

 End If

 End Sub

 Private Sub HarvestContent()

 Dim objRoot As System.IO.DirectoryInfo = New System.IO.DirectoryInfo(Me.strContentRootFolder)

 For Each sound As System.IO.FileInfo In objRoot.GetFiles("*.xab", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Sounds, sound, sound.Name.Replace(sound.Extension, ""))) Then
 Me.intDupeSound += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Sounds, sound, sound.Name.Replace(sound.Extension, "") & Me.intDupeSound.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Sounds, sound, sound.Name.Replace(sound.Extension, "")))
 End If
 Next

 For Each texture As System.IO.FileInfo In objRoot.GetFiles("*.png", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, ""))) Then
 Me.intDupeTexture += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "") & Me.intDupeSound.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "")))
 End If
 Next

 For Each texture As System.IO.FileInfo In objRoot.GetFiles("*.jpg", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, ""))) Then
 Me.intDupeTexture += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "") & Me.intDupeSound.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "")))
 End If
 Next

 For Each texture As System.IO.FileInfo In objRoot.GetFiles("*.gif", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, ""))) Then
 Me.intDupeTexture += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "") & Me.intDupeSound.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Textures, texture, texture.Name.Replace(texture.Extension, "")))
 End If
 Next

 For Each font As System.IO.FileInfo In objRoot.GetFiles("*.spritefont", IO.SearchOption.AllDirectories)
 If Me.objContent.Contains(New ContentFile(ContentTypes.Fonts, font, font.Name.Replace(font.Extension, ""))) Then
 Me.intDupeFont += 1
 Me.objContent.Add(New ContentFile(ContentTypes.Fonts, font, font.Name.Replace(font.Extension, "") & Me.intDupeSound.ToString))
 Else
 Me.objContent.Add(New ContentFile(ContentTypes.Fonts, font, font.Name.Replace(font.Extension, "")))
 End If
 Next

 End Sub

 Private Sub StartTheFakeCSharpFile()

 Me.strFakeCSharpProject = _
 "<Project DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">" & vbCrLf & _
 "<PropertyGroup>" & vbCrLf & _
 "<Configuration Condition="" '$(Configuration)' == '' "">Debug</Configuration>" & vbCrLf & _
 "<Platform Condition="" '$(Platform)' == '' "">x86</Platform>" & vbCrLf & _
 "<OutputType>WinExe</OutputType>" & vbCrLf & _
 "<AppDesignerFolder>Properties</AppDesignerFolder>" & vbCrLf & _
 "<AssemblyName>" & My.Application.Info.AssemblyName & "</AssemblyName>" & vbCrLf & _
 "<XnaPlatform>Windows</XnaPlatform>" & vbCrLf & _
 "<XnaFrameworkVersion>v1.0</XnaFrameworkVersion>" & vbCrLf & _
 "<XNAGlobalContentPipelineAssemblies>Microsoft.Xna.Framework.Content.Pipeline.EffectImporter.dll;" & _
 "Microsoft.Xna.Framework.Content." & _
 "Pipeline.FBXImporter.dll;Microsoft.Xna.Framework.Content.Pipeline.TextureImporter.dll;" & _
 "Microsoft.Xna.Framework.Content.Pipeline.XImporter.dll</XNAGlobalContentPipelineAssemblies>" & vbCrLf & _
 "</PropertyGroup>" & vbCrLf & _
 "<PropertyGroup Condition="" '$(Configuration)|$(Platform)' == 'Debug|x86' "">" & vbCrLf & _
 "<OutputPath>" & My.Application.Info.DirectoryPath & "\" & Me.strPipelineRootFolder & "</OutputPath>" & vbCrLf & _
 "</PropertyGroup>" & vbCrLf & _
 "<ItemGroup>"

 End Sub

 Private Sub AddContentToTheFakeCSharpFile()

 For Each content As ContentFile In Me.objContent

 Me.strFakeCSharpProject &= vbCrLf & _
 "<Content Include=""" & content.File.FullName & """>" & vbCrLf & _
 "<XNAUseContentPipeline>true</XNAUseContentPipeline>" & vbCrLf & _
 "<Importer>" & CType(content.ContentType, ImporterName).ToString & "</Importer>" & vbCrLf & _
 "<Processor>" & CType(content.ContentType, ProcessorName).ToString & "</Processor>" & vbCrLf & _
 "<Name>" & content.Name & "</Name>" & vbCrLf & _
 "</Content>"
 Next

 End Sub

 Private Sub EndTheFakeCSharpFile()

 Me.strFakeCSharpProject &= _
 "</ItemGroup>" & vbCrLf & _
 "<Import Project=""$(MSBuildBinPath)\Microsoft.CSharp.targets"" />" & vbCrLf & _
 "<Import Project=""$(MSBuildExtensionsPath)\Microsoft\XNA\Game Studio Express\v1.0\Microsoft.Xna.ContentPipeline.targets"" />" & vbCrLf & _
 "<Import Project=""$(MSBuildExtensionsPath)\Microsoft\XNA\Game Studio Express\v1.0\Microsoft.Xna.Common.targets"" />" & vbCrLf & _
 "</Project>"

 End Sub

End Class


In a nutshell, the VBContentManager class uses a supplied directory as a root folder which it scans for content (texture files, XACT sound projects, and spritefonts) in the HarvestContent method. It also has the ability to produce a C# project file that includes information about the harvested content, which it can pass to MSBuild in order to have the corresponding .xnb (et al) files created on your behalf.

To use the VBContentManager class, all you have to do is call the LoadPipeline method in your game's LoadGraphicsContent sub, indicating whether or not the content is already compiled (false) or whether you want to execute the C# hack process (true).

The last thing the LoadPipeline method does for you is it uses the Load method of the underlying ContentManager to import the texture, sound and font files into corresponding typed hash tables, which reside as public members of the VBContentManager. The resources are keyed by filename (no extension) in the respective hash table.

Here is an example of a simple base game class that makes use of the VBContentManager class (you can also watch the video tutorial to see this class being used by a game project).

Public Class MyBaseGame
 Inherits Game

 Protected objGDM As GraphicsDeviceManager
 Protected objCM As VBContentManager
 Protected objSB As SpriteBatch

 ''' <summary>
 ''' Starts a new game for the XNA 2D Engine
 ''' </summary>
 ''' <param name="WindowTitle">The window title when the game is not full-screen</param>
 ''' <param name="MouseIsVisible">Whether or not the mouse is visible by default</param>
 ''' <param name="ContentRootFolder">The root of your content folder, relative to your projects bin\debug (or release) folder</param>
 ''' <remarks></remarks>
 Public Sub New(ByVal WindowTitle As String, ByVal MouseIsVisible As BooleanByVal ContentRootFolder As String)

 Me.objGDM = New GraphicsDeviceManager(Me)
 Me.objCM = New VBContentManager(Me.Services, ContentRootFolder)

 Me.Window.Title = WindowTitle

 Me.IsMouseVisible = MouseIsVisible

 End Sub

 Protected Overrides Sub LoadGraphicsContent(ByVal loadAllContent As Boolean)
 MyBase.LoadGraphicsContent(loadAllContent)

 Me.objCM.LoadPipeline(True)

 End Sub

 Protected Overrides Sub Initialize()
 MyBase.Initialize()

 Me.objSB = New SpriteBatch(Me.objGDM.GraphicsDevice)

 End Sub

End Class



Here is a game that inherits from the above base game class. The root content folder for this game is a peer folder of the project's bin folder. Once you've got all the other stuff sitting in an engine somewhere, this is essentially all you need to do to spawn a new game with a vb content manager, effectively simulating VB support for XNA.

Public Class XNAGameForWindows
 Inherits MyBaseGame

 Public Sub New()
 MyBase.New("XNA Game for Windows"True"../../Content")
 End Sub

End Class


And here's the typed hash table. I wouldn't grab textures & the like out of a hash tables directly from a draw routine, but at the top of your program you can pull them out into discrete references or put them into lists, or whatever. The VBContentManager just makes them available in the hash in a way that's easy for humans to find them (ie. by file name), and you can do whatever you want with them from there.

Public Class Hashtable(Of Type)
 Inherits DictionaryBase

 Public ReadOnly Property Keys() As ICollection
 Get
 Return MyBase.Dictionary.Keys
 End Get
 End Property

 Default Public Property Item(ByVal key As String) As Type
 Get
 Return CType(MyBase.Dictionary(key), Type)
 End Get
 Set(ByVal value As Type)
 MyBase.Dictionary(key) = value
 End Set
 End Property

 Public Overloads Sub Add(ByVal key As String, ByVal obj As Type)
 MyBase.Dictionary.Add(key, obj)
 End Sub

 Public ReadOnly Property Values() As ICollection
 Get
 Return Dictionary.Values
 End Get
 End Property

 Public Function Contains(ByVal key As String) As Boolean
 Return Dictionary.Contains(key)
 End Function

 Public Sub Remove(ByVal key As String)
 Dictionary.Remove(key)
 End Sub

End Class

10/17/07

VB.NET XNA Tutorial 3: Creating a Game class

[GSE 1.0 Refresh]

There is now a Visual Studio template for starting a VB.NET XNA Windows Game project. This template automates the steps shown in the video.



First, read this!
  • Video tutorials are now hosted at Vimeo in High Definition

  • Be sure to watch them in Full mode

  • If your video quality goes all wacky, look in the lower right for a Scaling button and turn Scaling off (this will make life easier on the player and allow the quality to go back up)

  • You can't embed HD vids from Vimeo to your website, otherwise I would!
Ok, that's it. Here's the link to Tutorial 3: Creating a Game class on Vimeo.

10/13/07

VB.NET XNA Tutorial 2: Creating an XNA Windows Game project

[GSE 1.0 Refresh]

There is now a Visual Studio template for starting a VB.NET XNA Windows Game project. This template automates the steps shown in the video.



Here is a video tutorial that describes very simply how to setup a "game" project for Windows using VB.NET and Visual Studio 2005.


Tutorial 2: Creating an XNA Windows Game project from emachine on Vimeo.

10/12/07

VB.NET XNA Tutorial 1: Getting started

First things first; let's get this whole XNA thing straight.

The XNA framework is a managed wrapper for DirectX. So you'll need the latest version of DirectX installed.

XNA comes to you branded in a somewhat vaguely (and soon to be confusingly) named piece of software called "Game Studio Express". The deal is, Microsoft diverted their previous Managed DirectX project into the XNA project; the XNA project grew to include not only a managed wrapper for DirectX, but also some other tools like the Content Pipeline, and XACT. All of this was then wrapped into a software suite - not called XNA - but actually called "Game Studio Express", which was designed specifically to be an "expansion pack" of sorts for Visual C# 2005 Express Edition.

So anyway... what this all means for us VB freaks is that in order to get the managed wrapper for DirectX on our development machines, and in order to have at that Content Pipeline and XACT stuff, we have to do what everybody else has to do: install Visual C# 2005 Express Edition, and then install Game Studio Express. Which is all the more counter-intuitive for us, since our goal is to avoid using C# in the first place because we're too lazy to start using curly braces and semi-colons.

Don't worry. But you'd better get crackin'; you've got some progress bars to baby sit and many a "Next" button in your future. Enjoy it, involve a beverage perhaps.

Once you've got Game Studio Express installed, your newly acquired C# IDE gains XNA-related project options that no other IDE gets. To learn a little bit about what we're going to be missing out on in terms of XNA-related IDE options, let's go ahead and poke around the C# IDE for a sec. Run the C# IDE, and start a New Project - check out the types of projects you can start:



Go ahead and start a new Windows Game project, just to look around. Pop open the solution explorer and add a new Item to the project; check out what options we have available for creating New Items in the C# IDE:



Using the C# IDE, there are nifty items available like "Sprite Font" and "Game Component":



So those are Game Studio Express IDE hooks, they're just templates for making stuff. Nothing we can't handle ourselves.

Ok, we've seen enough. Cancel all that business and let's move over to our Visual Basic IDE.

Game Studio Express isn't going to do anything for our Visual Basic IDE, so if you go looking for a Windows Game project you're going to be disappointed



Update: The remainder of this tutorial is obsolete. A VB.NET XNA Windows Game project template for use with Visual Studio 2005 and Game Studio 2.0 Beta was posted on this blog in November 2007

But, what it has done is installed the managed XNA libraries, the Content Pipeline compiler, and tools like XACT. We've got everything we need to do some serious damage, there's just a couple things we'll have to do that the C# guys don't have to:

  • 1. We will need to initiate our own project, rather than use the Game project template. And that means we'll have to create those nifty items like "Game Component" and "Sprite Font" by ourselves, too.

    In tutorial 20, Alan Phipps demonstrates how to start an XNA Windows Game using the Visual Studio .NET IDE.

    In addition to the advice he gives there, I would suggest removing the System.Drawing reference from your project, as this can create an ambiguity with items of the same name (such as Color and Rectangle) that exist in the XNA namespace.

    I also suggest you not wait until tutorial 20 to start making your XNA projects in this way, but hindsight is 20/20.


  • 2. We'll want to use the Content Pipeline, because it's fairly cool. The Content Pipeline has some rules though - mainly it wants us to convert our resources into a "cross platform format" (something that works on both Windows and XBox360). So for example, if you have a sprite texture that's a .gif file, the Content Pipeline will want that to be an .xnb file. And sounds are even more wacky. If you make a sound resource with XACT, it will actually need to be broken down into 3 files: an .xgs file, .xsb file, and .xwb file. Yea, I know. This sounds crazy already and we haven't even started yet. Hang in there, we're almost done:

    The Content Pipeline hassle does seems kind of unnecessarily complicated at first. You'll like it though, I promise (and you don't have to remember those file extensions). Here's the deal: out in your 's .NET 2.0 framework folder there is a program called MSBuild.exe. MSBuild knows how to convert textures (like "spaceship.png") and sound projects you create with XACT into all those .x-blah-blah files for the Content Pipepline. It's actually really easy; MSBuild does all the work for you. You just tell it what you want to convert (pass it file names in a batch) and it converts them all for you.

    Now here's the catch; the way you "tell MSBuild to convert stuff in a batch" is to pass it a C# project file. Yea, like "WindowsGame1.csproj". Which kinda makes sense when you remember that all this stuff was designed to work with Visual C# Express Edition projects. MSBuild takes a .csproj file and for each resource it finds described in there, it creates a Content Pipeline .x-whatever file. With C# projects, this call to MSBuild is performed at the time the project is compiled. In XNA marketing lingo they call this "automatic". For us this will be a "manual" process... but we're lazy, so we'll automate it, and at the end of the day it'll end up being just as automatic as the next guy's.

    In tutorial 21, Alan Phipps demonstrates how he simply builds a "fake" .csproj file with his resource references in it, and then feeds that file to MSBuild in order to trick it into thinking it's processing a C# XNA project.

    Not bad! So I do the same thing, but I chose to integrate this process into my engine with a "compile" flag (rather than have it be a separate project that must be run). I'll show you how I did this in the next tutorial, and between the two you can choose to do it my way, Alan's way, or say "screw this, I'm using C#"
That's it for Tutorial 1. A little bit of dry reading, but rather than do a bunch of stuff the "wrong" way then refactor it later to the "right" way, I'd rather we just start out understanding the right way to do it. And if you managed to cram all of that into one sitting, you're doing better than I did when I first tried to understand all this stuff.

In the next article, we'll check out my adaptations of the "Start a new project" and "deal with the Content Pipeline hack" and I'll probably start brushing up against the merits of putting all of this stuff into a common project you can reuse for future games (in other words, we'll start building the engine).

10/9/07

VB.NET XNA Tutorial 0: Prologue

I hesitated a little bit about doing this VB.NET related XNA blog stuff for a couple of reasons. First, there's a new version of Game Studio Express on the way out shortly (version 2.0) which has support for Visual Studio 2005. That means very soon, you will no longer be required to use Visual C# Express for XNA related IDE features.

But unfortunately, support for Visual Studio 2005 doesn't equate to support for languages other than C#. Here's a good thread on the subject over at the xna forums, the meat of which is captured in this post by Shawn Hargreaves (one of Microsoft's XNA guys)

"... we are only doing IDE work to integrate the XNA Framework into the C# project system, and not currently supporting the project systems for any other languages. In the absence of this IDE work, you miss out on exactly two things:
  • Content pipeline integration
  • The ability to deploy and debug on Xbox 360
Now, you may be just fine without those things. Maybe you're making a Windows game that doesn't use the Content Pipeline. In that case you don't care about our IDE integration, so the lack of it will not hurt you, and you can use any .NET langauge you like.

If you want to use the Content Pipeline, or deploy to Xbox, you can still do that by building your other-language code into an assembly, then using a stub C# project that links with this assembly to build your content, deploy to Xbox, and then call into your real assembly as soon as it starts up. It's a hack, but some people have reported success working this way."

So that means for the immediate to long term, we're officially "XNA hackers". I dunno about you but that makes me feel like a rebel.

Anyway, the other reason I thought twice about this was the fact that, over at Alan Phipps's website (which is permalinked right over there in the links area), a lot of this has been explained already. After following the tutorials and gaining a little bit of insight into the process, I've decided I'm going to offer you my take on it. Rather than build a specific application for a specific task (which a lot of tutorials seem to do), here we'll be building something a little more generic; more of an engine that can be used to do "whatever" in 2D. Some of it will be "here's how you do it", and some of it will be me showing up to the party 5 months late with "omg I didn't even know about render targets! Holy crap, now we have to totally rewrite the engine!"

At any rate, you'll have a couple different sources to draw from when it comes to using XNA with VB.NET, and there's really not much of that floating around the net right now.