How to Make a Mini-Game
The purpose of this tutorial is to help a new developer get started with their own mini-game addition to the project.
It's expected that reader will have some knowledge of programming, preferably C# and XNA.
Contents
Your Project
It's not necessary for your mini-game to be integrated into the main code base. To get started, create a new XNA Project using either the Windows Game or Windows Game Library template. Next, your project needs to reference the main code. Right-click on References in your project and select 'Add Reference', Navigate to the main code base, either via SVN or from an installed copy, and select Util.dll. Util.dll is a shared library that contains many common elements between the existing mini-games.
Your Game
Next, we need to modify the XNA code to work with the mini-game system.
Game.cs
First, open the Game.cs file that came with the XNA template. Instead of being a subclass of Game, change it to be a subclass of GameScreen. GameScreen is a DrawableGameComponent that contains common functionality for mini-games to use, and is run by the main system for your game to be played.
public class Game : Gamescreen
Constructor
Because your mini-game object may be constructed at any time, your constructor should contain as little as possible, and need not exist.
Initialize
The Initialize method should contain all of your mini-game's start-up logic. In particular, you should do the following, if needed:
- Call GetLogger(); and initialize your logger object.
- Call GetInputHandler(); and initialize your input handler object.
- Bind the inputs your mini-game needs in your InputHandler. (see information on event codes)
mLogger = GetLogger(); mLogger.Initialize();
inputHandler = GetInputHandler(); inputHandler.Initialize(); inputHandler.Bind(Keys.A, Trigger.Pressed, EventCodes.UserPressedA); inputHandler.Bind(Keys.B, Trigger.Pressed, EventCodes.UserPressedB); inputHandler.Bind(Keys.C, Trigger.Pressed, EventCodes.UserPressedC);
Update
The Update method should contain all of your mini-game's runtime game logic. In particular, you must handle input, if set up in your initialization.
while (inputHandler.HasEvent()) { InputEvent inputEvent = inputHandler.GetNextEvent(); switch ((EventCodes)inputEvent.EventCode) { case EventCodes.UserPressedA: inputEvent.WriteToLog(mLogger, "A was pressed. Correct!"); break; case EventCodes.UserPressedA: inputEvent.WriteToLog(mLogger, "B was pressed. Try Again."); break; case EventCodes.UserPressedA: inputEvent.WriteToLog(mLogger, "C was pressed. See you later."); break; } }
Note that you must call WriteToLog for each input event, and provide any context information you can, even if the event was ignored. The Logging information is very important for the experimental value.
Game Info
In order for the main system to find and display your mini-game correctly, you need to provide some additional information. For this purpose, you will need to create a new class in your project. The class will be a subclass of MiniGameInfo, which is also from Util.dll. MiniGameInfo specifies a number of abstract methods, which must be overridden.
- GetDisplayName() - Return a string that will be used by the main system for display of your mini-game in menus and the like.
- GetGameScreen() - Return a constructed instance of your main Game, in this case, a new Game() from the code we edited above.
Playing
For the main system to find your mini-game, the built exe or dll and any required content must be placed in a subdirectory of the main project. It's recommended to follow the pattern of /minigames/"minigamename"/ currently used, to prevent conflicts. That's all.
Example Source Code
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Util; namespace ExampleGame { public enum EventCodes { UserPressedA = 1, UserPressedB = 2, UserPressedC = 3, } public class Game : GameScreen { Logger mLogger; InputHandler inputHandler; Color backgroundcolor = Color.CornflowerBlue; public override void Initialize() { mLogger = GetLogger(); mLogger.Initialize(); inputHandler = GetInputHandler(); inputHandler.Initialize(); inputHandler.Bind(Keys.A, Trigger.Pressed, (int)EventCodes.UserPressedA); inputHandler.Bind(Keys.B, Trigger.Pressed, (int)EventCodes.UserPressedB); inputHandler.Bind(Keys.C, Trigger.Pressed, (int)EventCodes.UserPressedC); base.Initialize(); } public override void Update(GameTime gameTime) { while (inputHandler.HasEvent()) { InputEvent inputEvent = inputHandler.GetNextEvent(); switch ((EventCodes)inputEvent.EventCode) { case EventCodes.UserPressedA: inputEvent.WriteToLog(mLogger, "A was pressed. Correct!"); backgroundcolor = Color.Green; break; case EventCodes.UserPressedB: inputEvent.WriteToLog(mLogger, "B was pressed. Try Again."); backgroundcolor = Color.Red; break; case EventCodes.UserPressedC: inputEvent.WriteToLog(mLogger, "C was pressed. See you later."); Done(); break; } } base.Update(gameTime); } public override void Draw(GameTime gameTime) { GraphicsDevice.Clear(backgroundcolor); base.Draw(gameTime); } } public class Info : MiniGameInfo { protected override string GetDisplayName() { return "Example Mini-Game"; } protected override GameScreen GetGameScreen() { return new Game(); } } }