Saturday, August 10, 2013

Bunny Hunters Online: Introducing TwoDEngine

In the last blog, Monogame was used directly to display an individual image.  That works great for simple situations but a game can quickly grow beyond such simple graphics needs.  In particular, complex multi-sprite objects need a way to move objects in relation to each other.  A Scenegraph is a very common way to handle this.

A scene graph is simply a tree of graphical objects where position and orientation of children are all in reference to their parent.   In such a system a single graphical object is often called a Sprite.   

In a side scroller, when the background is moved the rest of the sprites should move along with it so they stay in the same place relative to the background.  This is accomplished with a Scenegraph by making the background the parent of the other sprites.

Another example of how a Scenegraph might be used is in a top down tank battle game.  The tank body is one sprite but its turret is another.  The turret has to be rotatable in relation to the body, but has to move and rotate with the body when the body moves and rotates.  This is accomplished by making the turret sprite a child of the body sprite.  

In general Scenegraphs simplify the organization of objects in an environment.  They are common in 3D systems such as Unity3D.  TwoDEngine is, at its heart, a 2.5D Scenegraph.  The 2D part is positions in x and y.  The .5 refers to draw priority, which is not full 3D but does provide a relative "depth" to the objects.

Although the actual hierarchical needs of BHO are minimal, TwoDEngine also contains useful animation, motion and collision detection code as well as some other features that will make implementing BHO much simpler.

In order to use TwoDEngine the first thing to do is to download it.  It is available in both source code and pre-compiled library form.  For this project the pre-compield libraries are simpler to use.  You can download them from The Unseen University by clicking the Download button on this page:

Unzip the downloaded zip file and copy it to your Bunny Hunters Online Project folder.  The folder should look like this afterward:



Now go back into VS2012 and add the proper TwoDEngine lib as a reference.  To do this, right click on "references" in the project explorer and select "add reference".


When the add reference dialog pops up, select "Browse" on the left and then click the Browse button on the bottom right.


Navigate to your Bunny Hunters Online folder and then down into the TwoDEngine directory.  Beneath that you will see a list of platforms, navigate down into Windows 8 Store.


Form here, navigate down to bin->Windows 8->Debug.  Select TwoDEngine.dll and press Add in the lower right of the dialog.  (Don't worry about the other DLLs you already have them as part of your monogame Windows 8 Store project.)


Your project should look like this now.  Note that TwoDEngine (Windows Store) is now in your references list.


The next step is to modify Game1.cs to use TwoDEngine.  This will actually simplify the Game1.cs file a bit.  

To start with, you will need to include 3 namespaces from TwoDEngine.  Go to the top of Game1.CS and add using TwoDEngine, using TwoDEngine.Scenegraph and using TwoDEngine.Scenegraph.SceneObjects.  When you are done it should look like this.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using TwoDEngine;
using TwoDEngine.Scenegraph;
using TwoDEngine.Scenegraph.SceneObjects;

Next, the global variables at the top of the Game1.cs class should be removed as they are no longer necessary.  It currently looks like this.
Remove all 3 field declarations so all that is left at the top is the class declaration like this.

 public class Game1 : Game
    {
        

Next, change the constructor.  It currently looks like this


The global _graphics no longer exists, so remove the first line entirely.  Instead, you need to create a Scenegraph.  The Scenegraph will actually create a GraphicsDeviceManager for you.  To create the Scenegraph you add the line new Scenegraph(this) at the top.  (The Scenegraph constructor has a whole bunch of optional parameters that follow the Game parameter but you can ignore them for now.

You still need to set the PreferredBackBuffer size.  To do that you need access to the GraphicsDeviceManager that the Scenegraph created.  Conveniently, the Scenegraph registers the GraphicsDeviceManager in the TwoDEngine registry.  You can ask the registry for the registered object that implements a given class or interface with the Registry.Lookup command.  Add this line just after the line Content.RootDirectory = "Content" :
GraphicsDeviceManager _graphics = Registry.Lookup<GraphicsDeviceManager>()

When you are done modifying the constructor it should look like this:
public Game1()
        {
            new Scenegraph(this);
            Content.RootDirectory = "Content";
            GraphicsDeviceManager _graphics = Registry.Lookup<GraphicsDeviceManager>();
            _graphics.PreferredBackBufferHeight = 600;
            _graphics.PreferredBackBufferWidth = 800;
        }
The next thing that needs to be modified is the LoadContent method.  It will still need to load the Texture2D but instead of storing it in a global for use in Draw(), it will use it locally to create a Sprite to display.

As mentioned earlier, Sprites can have other Sprites as parents.  They can also have the Scenegraph object itself as a parent.  The Scenegraph object is the root of the displayed scene.  In order to be displayed, a Sprite must be able to trace its parentage back to the Scenegraph.

Since all the code does so far is display a single image, that image can be made into a BasicSprite whose parent is the Scenegraph itself.  You get the Scenegraph the same way you got the GraphicsDeviceManager, by looking it up in the Registry.

Replace all of the code currently in LoadContent with this code in order to do that:
 protected override void LoadContent()
        {
            
            // TODO: use this.Content to load your game content here
            Texture2D startScreen = Content.Load<Texture2D>("intro");
            Scenegraph sg = Registry.Lookup<Scenegraph>();
            new BasicSprite(sg, startScreen);
        }
Finally, since the Scenegraph is managing the actual drawing of its sprites, we no longer need or want any game specific code in the Draw method Game1.cs.  Return it to its originally generated state like this:

 protected override void Draw(GameTime gameTime)
        {
            
            base.Draw(gameTime);
        }
Build and run this code and you should see the same title screen you saw before.  Next blog will take this much and create a "game mode" from it in order to start enabling other game displays.

No comments:

Post a Comment