Game Creation with XNA/Programming/Frameworks
Frameworks
editThere are as many frameworks out there as there are failed game developers. Each time somebody can't finish their game, or the game turns out to be a flop, the developers turn the remaining source code into a 'framework'. Fortunately, there are a handful of actually useful frameworks, and in this chapter we want to show you some that can be easily used to create a decent game quickly. One thing you should worry about is the licence under which the framework is being published.
LTrees
editLTrees lets you create randomly generated trees complete with a trunk, branches and leaves. It also features wind animations for the trees. There are some different trees available, such as birches, pines and willows. You can see an example to the right.
Adding LTrees to you project requires some work, but the code for adding some simple trees with a predefined wind animation is quite short.
First you need the LTrees sources. You can download them here. Extract the file, and add the projects "LTreesLibrary" and "LTreesPipeline" to your solution (see below). The next steps all take place in the Solution Explorer in Visual Studio. It's usually located at the left or right side. Right-click on your solution, and choose Add -> Existing Project. Now navigate to the extracted projects and select the respective *.csproj file to add them to your project. You might need to rebuild your solution (CTRL+Shift+B) now. Then you have to add the references to your main game project. Right-click on References -> Add reference. Select Projects and add the LTreesLibrary. Now expand the Content item, right-click on the Reference submenu and add the LTreesPipeline reference (same procedure as above). The reference to the LTreesLibrary should now be in the References section in the main folder of your project, while the reference to the LTreesPipeline should be in the References sections of the Content subfolder of your project. Now you need to add the tree models and textures to your project. Simply open the LTreeDemo project from the downloaded source pack in the Explorer (the normal Windows Explorer this time), navigate to the Content subfolder and drag and drop the folders Fonts, Textures and Trees to the Content folder of your game project in the Solution Explorer in Visual studio.
We can now proceed to the relevant code. The following examples are partly taken from the LTrees Demo Application[1], available in the source package. The first thing to add are the LTrees libraries:
using LTreesLibrary.Pipeline;
using LTreesLibrary.Trees;
using LTreesLibrary.Trees.Wind;
We need some global variables to load and create the trees and animations. The profile variables include information about the different trees. We also need a TreeLineMesh, some SimpleTree objects, a WindStrengthSin (this defines the pattern of the wind animation) and a TreeWindAnimator object.
public class MyGame : Microsoft.Xna.Framework.Game
{
//...
String profileAssetFormat = "Trees/{0}";
String[] profileNames = new String[]
{
"Birch",
"Pine",
"Gardenwood",
"Graywood",
"Rug",
"Willow",
};
TreeProfile[] profiles;
TreeLineMesh linemesh;
int currentTree = 0;
SimpleTree tree, tree2, tree3;
WindStrengthSin wind;
TreeWindAnimator animator;
//...
}
Two new methods are needed. LoadTreeGenerators() loads information about the trees to the Content Manager, NewTree() generates a simple tree, complete with a trunk, branches and leaves.
void LoadTreeGenerators()
{
profiles = new TreeProfile[profileNames.Length];
for (int i = 0; i < profiles.Length; i++)
{
profiles[i] = Content.Load<TreeProfile>(String.Format(profileAssetFormat, profileNames[i]));
}
}
void NewTree()
{
// Generates a new tree using the currently selected tree profile
// We call TreeProfile.GenerateSimpleTree() which does three things for us:
// 1. Generates a tree skeleton
// 2. Creates a mesh for the branches
// 3. Creates a particle cloud (TreeLeafCloud) for the leaves
// The line mesh is just for testing and debugging
//Each tree was loaded into the profiles[]-filed and can be accessed with the numbers 0 to 5. They are chosen randomly here.
Random num = new Random();
tree = profiles[num.Next(0, 5)].GenerateSimpleTree();
tree2 = profiles[num.Next(0, 5)].GenerateSimpleTree();
tree3 = profiles[num.Next(0, 5)].GenerateSimpleTree();
linemesh = new TreeLineMesh(GraphicsDevice, tree.Skeleton);
}
Above methods are called in the LoadContent() method. Additionally, the wind animations are loaded.
protected override void LoadContent()
{
// ...
wind = new WindStrengthSin();
animator = new TreeWindAnimator(wind);
LoadTreeGenerators();
NewTree();
// ...
}
Lastly, the trees have to be drawn. This happens in the Draw(GameTime) method. The trees need to be scaled and translated properly. Also, we need a StateBlock to capture and re-apply the rendering states, since LTrees won't do that for us. If you leave this out, you will most likely encounter graphical glitches.
protected override void Draw(GameTime gameTime)
{
//..
Matrix world = Matrix.Identity;
Matrix scale = Matrix.CreateScale(0.0015f);
Matrix translation = Matrix.CreateTranslation(3.0f, 0.0f, 0.0f);
Matrix translation2 = Matrix.CreateTranslation(-3.0f, 0.0f, 0.0f);
StateBlock sb = new StateBlock(GraphicsDevice);
sb.Capture();
tree.DrawTrunk(world * scale, cam.viewMatrix, cam.projectionMatrix);
tree.DrawLeaves(world * scale, cam.viewMatrix, cam.projectionMatrix);
animator.Animate(tree.Skeleton, tree.AnimationState, gameTime);
sb.Apply();
sb.Capture();
tree2.DrawTrunk(world * scale * translation, cam.viewMatrix, cam.projectionMatrix);
tree2.DrawLeaves(world * scale * translation, cam.viewMatrix, cam.projectionMatrix);
animator.Animate(tree2.Skeleton, tree2.AnimationState, gameTime);
sb.Apply();
sb.Capture();
tree3.DrawTrunk(world * scale * translation2, cam.viewMatrix, cam.projectionMatrix);
tree3.DrawLeaves(world * scale * translation2, cam.viewMatrix, cam.projectionMatrix);
animator.Animate(tree3.Skeleton, tree3.AnimationState, gameTime);
sb.Apply();
//..
}
Now compile and start your project and enjoy some trees swaying in the wind!
Nuclex Framework
editNuclex is framework, which actually contains several features. It is specifically build for XNA and other platforms, that are written in .NET. The advantage of Nuclex is the independency of the different available modules. Module simply means components like 3D Text Rendering or Game State Manager. They are interchangeable as well as adjustable. The programmer can mix them and take only some elements. In fact most of the modules are so essential for games, that the use of may be only one component already helps to decrease the completion time or focus on other parts of the game. The components are an efficient help for programmers to "not reinventing the wheel" and bring a solution, which can be customized later. If a game should contain a GUI, an GamePad input, Vector Fonts or other game related features - the Nuclex Framework is the right place to look for.
Interestingly the Nuclex Framework is part of an open source community called www.codeplex.com founded by Microsoft. Even though the code and components are not owned by Microsoft.
All classes and libraries are coded with complete test coverage, that includes testings of the garbage collector or memory management. As the project states, all classes have a Nuclex is open source, therefor it can be used for projects of any kind. The terms of use clearly state, that the libraries can be implemented in any game as long as it stays open for other users. Moreover every game creator is welcome to join the platform and collaborate with other Nuclex coders. It is very simple to sign up for and account and become a part of the community. According to the Nuclex community the only requirement for using the components of the framework is a solid understanding of the programming language. Besides that all of the following components can make a programmers life more enjoyable. [2]
Features of the Nuclex Framework:
- 3D Text Rendering
- Arbitrary Primitive Batching
- Automatic Vertex Declarations
- Special Collections
- Text input and standard PC game pad support
- Core-Affine ThreadPool
- Debugging Overlays
- Game State Management
- LZMA Content Compression
- Multi-threaded Particle System
- Rectangle Packing
- Skinned Graphical User Interfaces
More information for each module can be found on http://nuclexframework.codeplex.com/. Since there are many different useful classes in the framework, which handling can be easily followed on the webpage, this article will only cover some solutions. In the upcoming sections three major components of Nuclex will be explained.
The assembly of the framework looks quite complex but it is actually just an collection of different libraries that can be used separately. The core of the framework contains basic classes for Math, Networking and Windows Forms all.
Vector Fonts
editOne of the nicest components of the Nuclex framework is the vector font creation. It actually takes characters from a .ttf file and interpolates the edges of each character. After the interpolation all information are stored in an .xnb file, which than can be used by the Nuclex.Fonts library. Even though in small size the text does not look as good for big fancy head lines it is a great feature.
These Fonts can be seamlessly used on the PC or Xbox and are even faster than the SpriteBatch class from XNA.
There are three ways of displaying the fonts. Either an outlined text. It basically takes the letters from the font and calculates the edges of each character for an stroke viewing. Another way of showing the vector font is an filled way. The technic is the same as before, but it fills the characters. Last but not least an extruded version of the characters is available.
First it is important to import the Nuclex.Fonts and Nuclex.Graphics and providing a VectorFont object which loads the ttf. In the LoadContent() method you can now load the font:
using Nuclex.Fonts;
using Nuclex.Graphics;
private VectorFont arialVectorFont;
private Text helloWorldText;
protected override void LoadContent() {
this.arialVectorFont = this.content.Load<VectorFont>("Content/Fonts/Arial");
this.helloWorldText = this.arial24vector.Extrude("Hello IMIs!");
//.....
In addition to the VectorFont we need a similar class like the SpriteBatch, which is called TextBatch. With an instance of it we can actually draw the text. We are still in the LoadContent() method.
///....
this.spriteBatch = new SpriteBatch(this.graphics.GraphicsDevice);
this.textBatch = new TextBatch(this.graphics.GraphicsDevice);
}
private TextBatch textBatch;
Last but not least we need to connect all the parts together and draw the text. Of course choosing an different type of filling would deliver another result.
///....
this.textBatch.ViewProjection = this.camera.View * this.camera.Projection;
this.textBatch.Begin();
this.textBatch.DrawText(
this.helloWorldText, // text mesh to render
textTransform, // transformation matrix (scale + position)
Color.White // text color
);
this.textBatch.End();
This part of Nuclex is a library that offers all tools for an interactive optical interface for a game or application. In any way graphical objects adaptable with scaling or positioning, they are simple to control (like state changes) and the render system is not connected. So no interference with the game could occur.
Why use UserInterface?
edit- intuitive and simple design
- works cross-platform (XBox 360 and Windows)
- special console UI controls
- support for different keyboard layouts
- unified scaling
- renderer-agnostic design
- skinning in default renderer (skin elements using XML files)
- completely test coverage
Implementation
editComponent to create fast and easy a GUI in a game. It is not GUI Manager for complex settings but all aspects of a typical game GUI are covered. It automatically change sizes according the screen and supplies a default view/skin, unless a custom specification is chosen.
Just like in any other GUI framework you can create buttons, windows and almost every other modern feature of an interface.
Before we can really start we need a basic interface. Very intuitive that can be made by the Screen class. Create an instance and add it to an object of the GuiManager. The GuiManager is in charge of the window so you need to create it up front, my be in the constructor of your class.
Then as described before you can add the Screen Object and get ready for the real interface stuff. Note: Viewport is used to make the window in a suitable size.
The last lines make the bounds of the window. If you leaf them out, it will still appear but not as nice.
this.graphics = new GraphicsDeviceManager(this);
this.input = new InputManager(Services, Window.Handle);
this.gui = new GuiManager(Services);
Viewport viewport = GraphicsDevice.Viewport;
Screen mainScreen = new Screen(viewport.Width, viewport.Height);
this.gui.Screen = mainScreen;
mainScreen.Desktop.Bounds = new UniRectangle(
new UniScalar(0.1f, 0.0f), new UniScalar(0.1f, 0.0f), // x and y = 10%
new UniScalar(0.8f, 0.0f), new UniScalar(0.8f, 0.0f) // width and height = 80%
);
Lets start now with regular button. First you need an instance of a ButtonControl than add the text and finally set the bounds.
ButtonControl newGameButton = new ButtonControl();
newGameButton.Text = "Neu...";
newGameButton.Bounds = new UniRectangle(
new UniScalar(1.0f, -190.0f), new UniScalar(1.0f, -32.0f), 100, 32
}
After placing the button we can put a delegate to it and make it clickable. After that we need to add the button to our mainScreen. It reminds a lot like the other gui managers, since you simply add all objects to different components. In this case we want the button on the desktop (basically to lowest layer of the screen.)
Note: In the following code the delegete of the button opens a new window. DialogWin extends the WindowControl class, later more.
newGameButton.Pressed += delegate(object sender, EventArgs arguments) {
this.gui.Screen.Desktop.Children.Insert(0, new DialogWin());
};
Now that we have a button and made it clickable we maybe want a new window. We can simply do that by extending the WindowControl class and add own components to it. Adding means we attach it to the current window or rather to its children. Children is an object default instantiated by the WindowControl base class.
public partial class DialogWin : WindowControl {
//Initializes a new GUI demonstration dialog
public DialogWin() {
this.nameEntryLabel.Text = "Deine Martikelnummer bitte:";
this.nameEntryLabel.Bounds = new UniRectangle(10.0f, 30.0f, 110.0f, 24.0f);
Children.Add(this.nameEntryLabel);
}
Finally we want to add the gui to our frame and make the mouse visible. It is that simple to create an interface with the Nuclex Framework.
Components.Add(this.gui);
this.gui.DrawOrder = 1000;
IsMouseVisible = true;
Is, as the name reveals it, a manager which coordinates different states. Only one state at the time can be active, but it is possible to have one state on the other. The main menu, for example, puts the ongoing game besides and returns after exiting the main menu.
Manager's interface
// Manages the game states and updates the active game state</summary>
public class GameStateManager {
// Snapshot of the game's timing values
void Update(GameTime gameTime) { /* ... */ }
// Draws the active game state
void Draw(GameTime gameTime) { /* ... */ }
// Pushes the specified state onto the state stack
void Push(GameState state) { /* ... */ }
// Takes the currently active game state from the stack</summary>
void Pop() { /* ... */ }
// This replaces the running game state in the stack with the specified state.
void Switch(GameState state) { /* ... */ }
// The currently active game state. Can be null.</summary>
GameState ActiveState { get { /* ... */ } }
}
Authors
editLennart Brüggemann, mglaeser
References
edit- ↑ LTrees Demo Application, Change Set 22316. ltrees.codeplex.com, received on 28. May 2011
- ↑ http://nuclexframework.codeplex.com/
- ↑ http://nuclexframework.codeplex.com/wikipage?title=Nuclex.UserInterface&referringTitle=Home
- ↑ http://nuclexframework.codeplex.com/wikipage?title=Game%20State%20Management&referringTitle=Home