Written by: Monday, November 28, 2011
The latest release of the DigitalRune Engine contains a new sample called GameStatesSample which covers a some of the basics:
The result is a stripped down XNA application – no fancy graphics, no gameplay, just a few screens and menus. Take a look:
This articles is a tutorial showing how to build the sample from scratch. The final source code can be found in the folder <InstallationFolder>\Samples\DigitalRune.Game.UI\GameStatesSample.
First, we need to create a new XNA Windows Game project in Visual Studio.
Then we add the required references: Right-click the newly created project and select Add Reference… Browse to the folder <Installation Folder>\References\XNA\v4.0\Windows\x86 and select the following DigitalRune assemblies:
Quite a lot of assemblies to add, but in the course of this tutorial we will use several different features.
Before we can use any of these features we need to set the license key. This can be done in the Main method in Program.cs or in the static constructor of the Game1 class. (Without a valid license key the libraries will stop working after a trial period of 30 days. Non-commercial use is free in most cases: Get a free license key for non-commercial use.)
public class Game1 : Game { static Game1() { DigitalRune.Licensing.AddSerialNumber("A4DsypZMsBsI8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); } ... }
Next we will create the required services (input service, UI service, and animation service) and set up the game loop. Most of this is boilerplate code, which is similar in most DigitalRune-based XNA games.
Add the following using-statements to Game1.cs:
using DigitalRune.Animation; using DigitalRune.Game.Input; using DigitalRune.Game.UI;
The services are created in Game1.Initialize():
private InputManager _inputManager; private UIManager _uiManager; private AnimationManager _animationManager; protected override void Initialize() { // ----- Initialize Services // The services are stored in Game.Services to make them accessible by all // game components. // Add the input service, which manages device input, button presses, etc. _inputManager = new InputManager(false); Services.AddService(typeof(IInputService), _inputManager); // Add the UI service, which manages UI screens. _uiManager = new UIManager(this, _inputManager); Services.AddService(typeof(IUIService), _uiManager); // Add the animation service. _animationManager = new AnimationManager(); Services.AddService(typeof(IAnimationService), _animationManager); // ----- Add GameComponents // The component that shows the individual screen. Components.Add(new MainComponent(this)); base.Initialize(); }
The custom game component MainComponent, which is added in the code above, is where we put the application-specific code. Simply add a new class called MainComponent, which inherits from DrawableGameComponent.
Now lets set up the game loop in Game1:
protected override void Update(GameTime gameTime) { var deltaTime = gameTime.ElapsedGameTime; // Update input manager. The input manager gets the device states and performs other work. _inputManager.Update(deltaTime); // Update UI manager. The UI manager updates all registered UIScreens and handles // button clicks, etc. _uiManager.Update(deltaTime); // Update game components. base.Update(gameTime); // Update the animations. The animations results are stored internally but not yet applied. _animationManager.Update(deltaTime); // Apply the animations. This method changes the animated objects. _animationManager.ApplyAnimations(); }
As you can see, we handle the tasks in the following order:
Note that we don’t need to override the game’s Draw-method. All rendering is done in MainComponent.
Before we write the game logic in MainComponent, lets add a UI theme first. The UI theme is required by the DigitalRune Game UI. It defines how the different controls are rendered. A typical theme consists of cursors, fonts, textures, and a theme definition file (XML), which are processed by the XNA content pipeline.
Usually, I recommend to copy and adjust an existing theme – see the DigitalRune Engine samples. But in this case, we will start completely from scratch.
For rendering text we need to create an XNA sprite font (right-click content project, select Add | New Item… | Sprite Font):
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics"> <Asset Type="Graphics:FontDescription"> <FontName>Miramonte</FontName> <Size>24</Size> <Spacing>0</Spacing> <UseKerning>true</UseKerning> <Style>Bold</Style> <CharacterRegions> <CharacterRegion> <Start> </Start> <End>~</End> </CharacterRegion> </CharacterRegions> </Asset> </XnaContent>
I picked the Miramonte font and called the file MiramonteBold.spritefont. I chose the font because it is a free redistributable font included with XNA GameStudio 4. If you can’t find it on your hard drive, it is also available as a separate download: Redistributable Font Pack
The UI theme needs to have at least one texture. In this example we do not actually need a texture, but is still required. So let’s just add a dummy texture – for example, a white texture with 1 x 1 pixels – and call it UITexture.png.
Now we need to write a theme definition file and save it as Theme.xml:
<?xml version="1.0" encoding="utf-8" ?> <Theme> <Fonts> <Font Name="Default" File="MiramonteBold.spritefont" IsDefault="true"/> </Fonts> <Textures> <!-- The styles below don't use any images. The UITexture is just a 1 x 1 pixels, white dummy texture. --> <Texture Name="UITexture" File="UITexture.png"/> </Textures> <Styles> <!-- The sample uses the UIScreen, Windows, StackPanels, Buttons and TextBlocks. UIScreen, Windows, StackPanels do not require a special style. The default values are fine. But Buttons and TextBlocks should be rendered using special styles. --> <!-- The TextBlocks are white. --> <Style Name="TextBlock"> <Foreground>255;255;255;255</Foreground> </Style> <!-- Buttons containing TextBlocks are used in the main menu of the game. Only the contained TextBlocks shall be rendered - no chrome. So we don't need to define any states with images. To indicate the different states (Default, MouseOver, Pressed, etc.) we use different opacities. --> <Style Name="Button"> <Opacity>0.4</Opacity> <State Name="Default"> </State> <State Name="MouseOver"> <Opacity>1.0</Opacity> </State> <State Name="Pressed"> <Opacity>1.0</Opacity> </State> <State Name="Focused"> <Opacity>0.8</Opacity> </State> </Style> </Styles> </Theme>
The theme definition file ties all relevant parts of the UI theme together: the default font, texture, and control styles. (Defining styles in the XML file is optional, but makes themes easily reusable and the different controls do not have to be styled in code.)
Add the theme file to the XNA content project in your solution. (Note: The theme file is the only file you need to add to the content project. The sprite font and the texture don’t have to be in the content project. Those files are automatically included because they are referenced by the theme definition file.)
The theme definition file requires a special XNA content importer and processor. Before we can change the importer/processor settings we need to reference the following DigitalRune assemblies in the content project.
Right-click the content project, select Add Reference…, again browse to <Installation Folder>\References\XNA\v4.0\Windows\x86 and select these content pipeline assemblies:
Then select the file Theme.xml and its Content Importer and Content Processor in the Properties Window to UI Theme – DigitalRune.
In the next blog post, we will implement the code that renders the screens.
1 comment(s) so far...
Re: How to Build an Animated, State-Based Game Menu in XNA (Part 1) Nice work !
Re: How to Build an Animated, State-Based Game Menu in XNA (Part 1)
Nice work !
A collection of the most useful blog articles can be found here:
Article Collection (on Documentation page)
DigitalRune is a trademark of Garstenauer Information Technology OG.
Garstenauer Information Technology OG Weingartenstrasse 35, 4452 Ternberg Austria (EUROPE) office@digitalrune.com