1/28/08

Tell Microsoft you want VB support for GS

Microsoft is offering a survey to XNA/Game Studio users for feedback on the XNA/Game Studio product. There are one or two questions on the survey which give you the opportunity to suggest the support of other, non-C# .NET languages. If you would like Game Studio to support VB, you should fill out the survey and mention it.

1/16/08

Screens & the Screen Manager

Screens

Screens are what I've been using, prior to implementing Cameras. So they may change. Here's how it all works now, and while the names may change in the future, the basic concept will probably remain mostly the same.

In Tutorial 7 we defined custom game objects as separate classes in our project. In doing so we abstracted a lot of detail out of our game class; not only the construction of the little ship & turret tree, but also the Update code that moved a ship back and forth across the screen. The primary focus on putting these things into a Ship class was to make them readily reusable - but did you notice it really helped tidy up the Game class? In part there is a philosophical benefit to abstraction in that it simply helps to keep things looking tidy, if you're into that sort of thing.

In the previous article A forest for your trees, we simulated this strategy somewhat in a hypothetical Game class that defined some make-believe game objects as members:

Public Class MyGameTutorial
 Inherits Game

 'Main menu stuff
 Private objNewGame As NewGame
 Private objGraphics As Graphics
 Private objQuit As Quit
 Private objContinue As Sprite

 'Game stuff
 Private objShip1 As BadGuyShip
 Private objShip2 As BadGuyShip
 Private objPlayer As PlayerShip
 Private objHighScore As Sprite

 'Initialization, etc.

End Sub

We ended up with 8 game objects or trees, like so:



And in theory you would manage (update/draw) all 8 of them directly from within the game class. That's not too bad, but, half of these objects are meant for the main menu game state, and the other half belong to the actual "playing the game" game state. The only way to know which ones even belong to which state is to read the comments above the member declarations! I'm no coding guru, but even I know that's turning into a sketchy situation.

If we take the time to define custom Screens, just like we defined custom game objects in Tutorial 7, we can perform yet another level of abstraction that will not only help organize us, and help us maintain game state, it will again make our game class a more tidy, simple thing to work with.

Tutorial 7 also walked us through creating custom game objects by deriving new classes from Sprite. You do something like this...

Public Class MenuObject
 Inherits Sprite

 Private ChildOption1 As Sprite
 Private ChildOption2 As Sprite

 Public Sub New()

 Me.ChildOption1 = New Sprite()
 Me.ChildOption2 = New Sprite()

 Me.AddChild(ChildOption1)
 Me.AddChild(ChildOption2)

 End Sub

 'Update, Draw etc.

End Sub

We can do the very same thing to create custom Screen objects from our base Screen class. Let's build some hypothetical screens to abstract those 8 game objects into 2 screens representing their respective states. First here's a pseudo Main Menu Screen class:

Public Class MainMenuScreen
 Inherits Screen

 Private NewGameChoice As MenuObject
 Private ContinueChoice As Sprite
 Private GraphicSettings As MenuObject
 Private Quit As MenuObject

 Public Sub New()

 Me.NewGameChoice = New MenuObject()
 Me.ContinueChoice = New MenuObject()
 Me.GraphicSettings = New MenuObject()
 Me.Quit = New MenuObject()

 Me.AddChild(NewGameChoice)
 Me.AddChild(ContinueChoice)
 Me.AddChild(GraphicSettings)
 Me.AddChild(Quit)

 End Sub

 'Update, Draw etc.

End Sub

It looks like this:


Now how about a pseudo game screen class:

Public Class TheGameItself
 Inherits Screen

 Private Ship1 As BadGuyShip
 Private Ship2 As BadGuyShip
 Private Player As PlayerShip
 Private HighScore As Sprite

 Public Sub New()

 Me.Ship1 = New BadGuyShip()
 Me.Ship2 = New BadGuyShip()
 Me.Player = New PlayerShip()
 Me.HighScore = New Sprite()

 Me.AddChild(Ship1)
 Me.AddChild(Ship2)
 Me.AddChild(Player)
 Me.AddChild(HighScore)

 End Sub

 'Update, Draw etc.

End Sub

There we go:


Nevermind the naming mis-match on the diagram; the "game" node represents the "TheGameItself".

Now instead of adding 8 game objects to our game class and organizing them with code comments, we can use two Screens with names that convey their purpose:

Public Class MyGameTutorial
 Inherits Game

 Private MainMenu As MainMenuScreen
 Private TheGameItself As GameScreen

 'Initialization, etc.

End Sub

This gains us a few benefits. With the addition of a Screen node, we can now transform (move rotate scale etc) an entire Screen of "stuff", be it the main menu or the game itself, something we couldn't do easily prior to implementing Screens. This also gives us something of a "choke point" for state management - if we want to draw the Main Menu, we just tell the main menu screen to .Update or .Draw and thanks to the underlying Sprite class, the rest more or less runs itself. All our Game really has to worry about now is calling the Update & Draw methods of whichever Screen(s) are appropriate for the current Game State.

XNA.Framework.Game.Components and the ScreenManager

This has all been very cute, but it seems that all we've done is shuffled the question of which Game Objects to draw to which Screens to draw. What we've done so far helps organize us, but it hasn't helped us answered that important question.

You may be pleased to know we can eliminate the need to ask this question all together. We can simply update/draw whichever Screens happen to be Enabled and/or Visible at the moment. For this, we will borrow upon the Components member of the XNA.Framework.Game class.

In brief, XNA.Framework.Game has a built-in Components collection. This collection is meant to hold objects which derive from GameComponent and/or DrawableGameComponent, but in truth can hold any object which implements the IGameComponent/IUpdateable/IDrawable interface(s). The Game.Components collection is handy because, similar to how our Sprite class was created to automatically draw & update it's children (if any), XNA.Framework.Game understands that if it finds anything in it's Components collection, it should Update and Draw whatever it finds in there. There are a few useful features of this which we're going to borrow upon:
  • The IUpdateable and IDrawable interfaces have UpdateOrder and DrawOrder properties, respectively. When XNA.Framework.Game processes it's Components collection, it uses these values to determine which objects get Updated first and which ones get Drawn first
  • The IUpdateable interface has an Enabled property (Boolean). The Game class will only Update components where Enabled=True
  • Similar to Enabled, the IDrawable interface has a Visible property and only objects which are Visible will be Drawn
This is a convenient little system to plug our Screens into.

Now you may have noticed some Interfaces being included in the base Screen class in the previous article, let's get another look at that:

Public MustInherit Class Screen
 Inherits Sprite
 Implements IGameComponent
 Implements IDrawable
 Implements IUpdateable

 'etc

End Class

Implementing these interfaces allows our Screen class to be "registered" or added to the Game.Component collection. Alternatively, you could be dealing with classes that inherit from GameComponent or DrawableGameComponent, which Implement these interfaces for you. Since our Screen inherits from Sprite, this hasn't been done for us so I'll just have the Screen class implement the necessary interfaces.

The Screen Manager class is going to be our middle-man between our game code and the Screens stored in the Game.Components collection. The Screen Manager is going to add our Screens to the Collection for us, set DrawOrder for us, set Enabled & Visible for us, all that stuff. We'll just use the "Add", "Load" and "Unload" methods of the Screen Manager, for the most part. Here's the class, which I'll post for your amusement. If I get around to making a video about this I can go into more detail:

Public Class ScreenManager

 Public Shared Game As GameEngine
 Public Shared Root As Root
 Private Shared NameToGameID As New Hashtable(Of Integer)
 Private Shared GameIDArrangement As New List(Of Integer)

 Public Shared ReadOnly Property GetScreen(ByVal ScreenName As String) As Screen
 Get
 If NameToGameID.Contains(ScreenName) Then
 Return CType(Game.Components(NameToGameID(ScreenName)), Screen)
 Else
 Return Nothing
 End If
 End Get
 End Property

 Public Shared ReadOnly Property CurrentScreen() As Screen
 Get
 Dim objRet As Screen = Nothing
 For intX As Integer = GameIDArrangement.Count - 1 To 0 Step -1
 If CType(Game.Components(GameIDArrangement(intX)), Screen).Enabled Then
 objRet = CType(Game.Components(GameIDArrangement(intX)), Screen)
 Exit For
 End If
 Next
 Return objRet
 End Get
 End Property

 Public Shared ReadOnly Property IsScreen(ByVal ScreenName As String) As Boolean
 Get
 Return NameToGameID.Contains(ScreenName)
 End Get
 End Property

 Public Shared Function CreateRoot(ByVal YourGame As GameEngine, ByVal objRootSize As Vector2, Optional ByVal strTextureName As String = ""Optional ByVal strTextureNameItem As String = ""As Root

 Dim objRetVal As Root

 'make a new root object of the specified size at 0,0
 objRetVal = New Root(YourGame, Nothing"root"TrueTrue, strTextureName, strTextureNameItem)
 objRetVal.Size = objRootSize

 Return objRetVal

 End Function

 ''' <summary>
 ''' Adds a screen to the screen manager. Adds to the top of the stack by default, but does not alter Enabled or Visible
 ''' </summary>
 ''' <param name="obj"></param>
 ''' <remarks></remarks>
 Public Shared Sub AddScreen(ByVal obj As Screen)

 Root.AddScreen(obj)
 Game.Components.Add(obj)
 NameToGameID.Add(obj.Name, Game.Components.Count - 1)
 GameIDArrangement.Add(Game.Components.Count - 1)
 obj.Initialize()

 End Sub

 ''' <summary>
 ''' Sends the specified screen to the bottom of the stack and excludes it from the update/draw loop
 ''' </summary>
 ''' <remarks></remarks>
 Public Overloads Shared Sub Unload(ByVal intScreenID As Integer)

 'Remove this screenID the update/draw loop
 With CType(Game.Components(intScreenID), Screen)

 .SetVisiblity(False)
 .SetEnabled(False)

 .IsModal = False

 End With

 End Sub

 ''' <summary>
 ''' Brings the specified screen to the front of the stack and includes it in the update/draw loop
 ''' </summary>
 ''' <remarks></remarks>
 Public Overloads Shared Sub Load(ByVal intScreenID As IntegerByVal Modal As BooleanOptional ByVal TransitionIn As Boolean = True)

 Dim ModalScreen As Screen
 Dim tex As Texture

 'Bring to the front of the stack
 ScreenManager.BringToFront(intScreenID)

 'that changes the gameIDArrangement (this screenID is now at .Count-1 and all others have been pushed -1)

 'Include this screenID the update/draw loop

 With CType(Game.Components(intScreenID), Screen)

 .SetVisiblity(True)
 .SetEnabled(True)

 'Set modal state
 .IsModal = Modal

 If TransitionIn Then
 .TransitionIn()
 End If

 End With

 'Now if we have just shown something modal, we need to stay in a loop here so the calling code
 'can resume when the screen gets hidden.  For as long as we are modal, this loop continues

 ModalScreen = CType(Game.Components(GameIDArrangement(GameIDArrangement.Count - 1)), Screen)

 If ModalScreen.IsModal Then

 While ModalScreen.IsModal
 Game.Tick()
 End While

 Game.GraphicsDevice.Textures(0) = tex

 End If

 End Sub

 ''' <summary>
 ''' Brings the screen to the top of the render stack. Does not change Enabled or Visible
 ''' </summary>
 ''' <remarks></remarks>
 Public Overloads Shared Sub BringToFront(ByVal intScreenID As Integer)

 'If this screen is already in the show stack, remove it
 If GameIDArrangement.Contains(intScreenID) Then
 GameIDArrangement.Remove(intScreenID)
 End If

 'Put this screen at the top of the show stack
 GameIDArrangement.Add(intScreenID)

 ScreenManager.SetDrawOrders()

 End Sub

 ''' <summary>
 ''' Syncs the draw orders of the screens with their positions in the show stack
 ''' </summary>
 ''' <remarks></remarks>
 Private Shared Sub SetDrawOrders()
 For intX As Integer = 0 To GameIDArrangement.Count - 1
 CType(Game.Components(GameIDArrangement(intX)), Screen).SetDrawOrder(intX)
 Next
 End Sub

 ''' <summary>
 ''' Sends the screen to the bottom of the render stack
 ''' </summary>
 ''' <remarks></remarks>
 Public Overloads Shared Sub SendToBack(ByVal intScreenID As Integer)

 'If this screen is already in the show stack, remove it
 If GameIDArrangement.Contains(intScreenID) Then
 GameIDArrangement.Remove(intScreenID)
 End If

 'Put this screen at the bottom of the show stack
 GameIDArrangement.Insert(0, intScreenID)

 ScreenManager.SetDrawOrders()

 End Sub

 Public Overloads Shared Sub Load(ByVal ScreenName As String, ByVal Modal As BooleanOptional ByVal TransitionIn As Boolean = True)
 Load(NameToGameID(ScreenName), Modal, TransitionIn)
 End Sub

 Public Overloads Shared Sub Unload(ByVal ScreenName As String)
 Unload(NameToGameID(ScreenName))
 End Sub

 Public Overloads Shared Sub BringToFront(ByVal ScreenName As String)
 BringToFront(NameToGameID(ScreenName))
 End Sub

 Public Overloads Shared Sub SendToBack(ByVal ScreenName As String)
 SendToBack(NameToGameID(ScreenName))
 End Sub

End Class


You'll see some funky oddities in there but it's not too scary.

Let's sum this situation up here before I get carpel tunnel:
  • Sprites are the building blocks - they contain the code to invoke the Matrix cascade and contain the code necessary to facilitate the creation of tree nodes.

  • Game Objects are reusable custom Sprite classes, typically with their own custom Update code and usually contain other child sprites to create small trees.

  • Screens are the building blocks of State management. They derive from Sprite but typically don't require a texture, per se. They mainly exist to act as parent nodes so that we can graft related Game Objects and Sprites together into a Game State context.

  • We use the Screen Manager to Register screens with the Component collection. We use the "Load" and "Unload" methods of the Screen Manager to basically set the DrawOrder, Enabled and Visible properties of Screens, which in turn causes the Component collection to change their order, start/stop Updating, or start/stop Drawing them.

  • Game State is defined by whatever the state of our Enabled/Visible properties are on the Screens in the component collection - when a game starts, it registers it's Screens through the Screen Manager and Loads the Main Menu Screen. The Game State simply remains on the Main Menu Screen until changed, leaving no real need to question Game State (we simply allow all Enabled/Visible components in the Game.Component collection to Update & Draw as per their Enabled/Visible properties).

Once you've got this in place and you make a game, add some screens with the screen manager and Load one, you essentially Update and Draw your game by calling MyBase.Update and MyBase.Draw from your game class. This will trigger the underlying XNA.Framework.Game to Draw & Update, which will cause it to process it's Components collection where your Screens live. In the end, you remove all Update and Draw calls to custom objects & screens from your game class and just let the underlying XNA Game process it's Components collection, using the Screen Manager to control which Screens in the collection end up getting Updated & Drawn.

My apologies for the verbosity, I guess it's a lot to cover. There's still more to come...

1/14/08

A forest for your trees

Trees

The "parent/child" relationship afforded by the Sprite class can be thought of as an unordered tree structure:



In Star Maze, I've got a game object called PlayerShip. Like all game objects it inherits from Sprite:

Public Class PlayerShip
 Inherits Sprite
 'Update, Draw, etc.
End Class

Considering Sprites to be tree nodes, any class inheriting from Sprite (including PlayerShip) becomes a valid tree node and can be diagrammed as such:



It's not much to look at on it's own so let's use the AddChild method of the PlayerShip to attach a Turret sprite:

PlayerShip.AddChild(Turret)



The thrusters for the player's ship are separate game objects. Like the PlayerShip (and like all game objects we'll make with this engine), a Thruster is another game object derived from Sprite:

Public Class Thruster
 Inherits Sprite
 'Update, Draw, etc.
End Class

We'll add a couple afterburner sprites to the Thruster,

Thruster.AddChild(YellowAfterburner)
Thruster.AddChild(BlueAfterburner)

and here's the resultant tree diagram for the Thruster object:


Now we've got two different game objects, both of which inherit from Sprite: "Ship" and "Thruster". You can also interpret this as having two sprite trees: the PlayerShip tree and the Thruster tree:



So far in the tutorials we've been using the AddChild method to append simple "leaf" nodes to sprites, but we can also do something slightly more profound, and that is to graft one entire tree onto another:

PlayerShip.AddChild(Thruster)



By adding the root node of the Thruster tree as a child of PlayerShip, the entire Thruster tree is grafted to the PlayerShip.

Thinking about Sprites as tree nodes just gives us an alternate way to describe or illustrate the "parent/child" system Sprites employ.

Trees in a Forest

The "games" created in the tutorials thus far are simple programs meant to demonstrate classes like Sprite and VBContentManager (et al). None of the tutorials have put much thought into organizational techniques yet. A typical tutorial is structured like this:

Public Class MyGameTutorial
 Inherits Game

 Private GameObject1 As SpriteTree1
 Private GameObject2 As SpriteTree2

 'etc.

 Protected Overrides Sub Update()

 GameObject1.Update()
 GameObject2.Update()

 'etc.

 End Sub

 Protected Overrides Sub Draw()

 GameObject1.Draw()
 GameObject2.Draw()

 'etc.

 End Sub

End Sub

There may be some game object classes defined somewhere, but the general structure so far has been to create instances of game objects as private members of the Game class, and then simply Update & Draw them from within the Game's Update and Draw methods. Which is fine for a quick tutorials dealing with a limited number of sprites and game objects. But when we eventually get to the point where we're creating an actual game, we're going to end up with a lot more than just "a limited number of sprites & game objects". Our lack of organization will turn into a problem.

Consider a tiny, theoretical game with 1 player, 2 bad guys, and a high score. This game starts up on a "main menu" with various options (new game, continue, quit, graphic settings). You can assume game object types such as BadGuyShip and PlayerShip (etc) to be irrelevant, all that matters is they derived from Sprite and are therefore valid tree structures of some kind:

Public Class MyGameTutorial
 Inherits Game

 'Main menu stuff
 Private objNewGame As NewGame
 Private objGraphics As Graphics
 Private objQuit As Quit
 Private objContinue As Sprite

 'Game stuff
 Private objShip1 As BadGuyShip
 Private objShip2 As BadGuyShip
 Private objPlayer As PlayerShip
 Private objHighScore As Sprite

 'Initialization, etc.

 Protected Overrides Sub Update()
 'Which ones do I update?
 End Sub

 Protected Overrides Sub Draw()
 'Which ones do I draw?
 End Sub

End Sub

Here's all of our game objects, in tree view.



When this game runs and the update cycle comes around... which game objects do we update? High Score? BadGuy #2? The "Quit" menu item? We're now having to ask ourselves a new question: "Where am I?".

There's a few ways to answer this question; you could just define a string called WhereAmI and if WhereAmI = "MainMenu" then you update/draw main menu stuff, and if WhereAmI = "PlayingTheGame" you update/draw the game stuff. And that would work, but as you add new features and possible "places" you could potentially be in the program (main menu, the game, the inventory screen, the game over screen, any of those but paused, etc) it becomes apparent that there may be better approaches.

First of all, we need to get organized. We need a better method of organization for our game objects. Knowing where we are, ergo what to update & draw, will tie closely into our organizational strategy.

Ironically enough one path to organization can be found in adding more Sprites. Not just any Sprite though; a special game object which is a bit of a Sprite hack, to tell the truth. It's called a Screen:

Public MustInherit Class Screen
 Inherits Sprite
 Implements IGameComponent
 Implements IDrawable
 Implements IUpdateable

 'etc

End Class

Screens are going to help us turn this:



Into this:




Once you're comfortable with the idea of Sprites as tree nodes, this is pretty simple; you define a MainMenu Screen and then just graft all your main menu game objects via MainMenu.AddChild(). A Screen is basically a container, a bucket into which we can toss game objects; it's the forest of our trees.

Now anything we do to the Screen node - position, rotation, scale - the matrix cascade is going to apply that to every single attached child as well. This is our secret weapon that will let us make fancy screen transitions without too much of a problem.

Ok this is just the beginning of what I want to get into with this, but it's taken me 2 days just to get this much done so I'm going to publish this and start working on part 2 - the Screen Manager, which is what we'll use to show / hide / order screens, we can even use it to show screens & dialogs "modal", continuing to update and draw our game but without loosing our place in code. And then, the great paradigm shift: Cameras, which may change all of it.

1/3/08

Not Vacation

Back from vacation and busy at work. Working on ADO stuff mostly, I've got this 5+ year project ahead of me here and, like most programs now, it's just a big sprawling database front end. I'm building some good base classes for presenting, editing, and saving SQL data, with generic concurrency checking & error handling, irrespective of database or interface. Kind of a generic data access layer "engine" (well, sort of). It'll really help automate the tedium of dealing with database records & concurrency etc., which is super important for this job (medical records, medical billing - you don't want to screw this stuff up) but the reality is, be it a database of medical records or a database of high scores, it's always the same ADO issues no matter what the columns happen to be or what the tables are named. So as per usual, the stuff I'm working on now will eventually help me somewhere else on the XNA side. Which is always cool to think about.

And speaking of XNA I've been thinking about how exactly I want to implement that 2D camera class into the engine. It's all well and good to just have camera code that "works", but now I want it to be simple to use. I want to have a Camera object abstracted to the point where I just define it, add it to the game, and "there you go".

That's going to be a little trickier than I first thought. Possible, just tricky (for me anyway). If you've read along with half the tutorials here you know the gist of the engine - game objects are in a parent/child relationship, their matrices multiply together and that's basically how stuff is put on the screen. I want Cameras to be a "game object" as well, so I can put a camera here or there just as easily as I could put a sprite here or there.

What's tricky is that you can't necessarily make a game object (like say, the player's ship) a child of a Camera. What if the ship needs to show up in 2 Cameras at once? But then, if game objects aren't children of Cameras, how then could the game object be automatically influenced by the Camera's matrix ala the parent/child matrix cascade?

I don't really know yet - but one of the fundamental changes to the engine I'm considering is to say that a Screen holds a set of game objects - for instance, the Game screen holds a players ship, the bad guys, the bullets, all that crap in it's child collection. And then, Cameras are also in the Screen's child collection. The change would be that when a Screen is told to draw, it only draws it's Cameras; and the actual drawing of objects is performed by the Camera, rather than by the Screen. So the Screen becomes something of a Camera manager... or something like that. I haven't actually sat down with it yet, this is mostly mental fragments pieced together while driving to work or falling asleep at night.

The Camera is going to be a significant component of the engine and may play a very important role in how the engine renders sprites onto the screen, so I want to take care to approach it in a somewhat methodical manner. Under the thumb of bad weather here on the US west coast, perhaps this weekend I'll have nothing better to do.