XNA Arcade tutorial (2 of 5) – The AbstractGame and ArcadeGamesManager
This is an introduction to how we will create the basis for all the games (AbstractGame)
which can be loaded and played in the XNA Arcade. We will also create a helperclass
(ArcadeGamesManager) which can load these classes and their resources from external
assemblies (DLLs).
Why an AbstractGame class?
At its core, what we want is to have something (a small game) behave differently depending on which game it is - but we should be able to load them all in the same way from the hostgame.
So our coder-mindset says: "Different things which should appear alike in code - AHA, WE NEED AN INTERFACE!!".
Next our coder-mindset says "...but wait - we can probably implement some of the code in a superclass, to avoid having to code everthing again whenever we make a new game - AHA, WE CAN MAKE AN ABSTRACT SUPERCLASS!!!
Basically, an abstract class is a class which you can't "new up" (instantiate) and it is often used to write code to reuse in subclasses.
The basic structure - Draw() and Update()
Since we are already familiar with the idea of the Game class in XNA, we will let the AbstractGame class have an Update and a Draw method as well. This simplifies running the subgame in the hostgame as the calls to Update and Draw in the hostgame will just forward the calls to Update and Draw in the whatever game is currently loaded.
This effectively puts the hostgame out of the loop. So we need some way to return control when the subgame has ended. We do this by letting the hostgame read a public variable called GameOver on the subgame. The subgame can then set this variable to true when the hostgame should take control again. Here's code from the hostgame, calling the subgame's Update and Draw:
//To hold the currently running subgame AbstractGame _currentGame; protected override void Update(GameTime gameTime) { //if we're playing a game if (_currentGame != null) { //if the game is over, empty the current game //thereby returning to the game selection screen if (_currentGame.GameOver) { _currentGame = null; } else { //call the update method of the game being played _currentGame.Update(gameTime); } } } protected override void Draw(GameTime gameTime) { if (_currentGame != null) { //use that game's Draw _currentGame.Draw(gameTime); } else { //draw the hostgame's gameselection screen } }
Give AbstractGame the tools it needs
Since the subgame isn't a real Game class it doesn't have access to the same variables that an ordinary Game does (e.g. ContentManager, GraphicsDevice, etc.). This means we have to supply it with these things. The way I've chosen to handle this is by making a helperclass (ArcadeGamesManager) which is instantiated using a reference to the hostgame. When you want an instance of a game you only need to tell the ArcadeGamesManager what type of game to create and give it a reference to the SpriteBatch you want it to draw on (here the "this" refers to the hostgame where the ArcadeGamesManager is created):
ArcadeGamesManager manager = new ArcadeGamesManager(this); AbstractGame myGame = manager.CreateGame(typeof(MyChessGame), this.SpriteBatch);
What about resources?
Since we would like the subgames to be selfcontained - so just adding a new DLL to a subdirectory in the hostgame's folder will add new games - we need to be able to contain everything we need inside the DLL: code, graphics, sounds, fonts, etc. How we embed the resources will be covered in part 4, but just know for now, that the AbstractGame class has two ContentManager properties HostContent and LocalContent to be able to load content from both the hostgame and its own DLL.
The idea behind this is that you want the subgames to be able to stand on their own as well and supply all resources, but if you are developing a gamespack, you might design all subgames to load resources from the main game (fonts, textures, etc.) to give them a coherent look-and-feel.
This gives you both possibilities
Sample code
HostContent.Load("background"); //loads from the hostgame LocalContent.Load("background"); //loads from the game's own DLL
Rolling your own subgame
Since you don't construct your own instances of your games, but leave that up to the ArcadeGamesManager, you may still want to do some intialization (load resources, set variables, etc.). The AbstractGame defines an Initialize method which you can override. Just make sure you call
base.Initialize(...)
as the first line in your own Initialize method, so the AbstractGame is initialized as well.
In my next (upcoming) blogpost I will look at how we inspect the DLL files and make it possible for the player to select a game to run:
Next chapter: Part 3 - Creating the hosting game and selecting a game to play