Friday 19 June 2009

Game Components of the Drawable Kind

** Warning! The following may document something useful, or just my own stupidity! **


So I've been looking into Game Components and haven't found a great deal on the web about the topic so ended up quite frustrated (especially saying as the MSDN tutorial tells you only how to add one to your game!), so I thought I'd post this information incase anyone find it useful!
A bit of history - C++ Collection Classes
Before I made (/am still making) the transfer from C++ game programming to XNA I would use something called Collection Classes to do a lot of work and I was wanting to implement the same principles in XNA. Some may ask "What's a collection class", well it is an array of instances of a class, for example here is a basic class:

private class CGameCharater{

public: //space for public variables (in C++ everything is private unless declared in the public section

int iPublicInt1;
....

private: // space for private variables

int iPrivateInt1;

public void CGameCharacter(string sCharacter, float xPos, float yPos, float zPos){} // constructor

private void UpdatePosition(){} // example method

private void ~CGameCharacter(){} //destructor so you could do your tidying up here

}; // end of class

Now, say you're doing a game where you want the one class to be the base of all your characters in the game and some characters you want to repeat (think of the Grand Theft Auto series for example, the main characters are only rendered once but the citizens of Vice City (for example) are the same set of models rendered multiple times in different locations), well you could have a default class that handles all of this behaviour and create multiple instances like this:

CGameCharacter* cGC[MAXCHARACTERS]; // MAXCHARACTERS is a static variable

You now have an array that contains the CGameCharacter. Next create a class called something like CGameCharacterCollection and use this class to perform all your initialization, and then a pointer to the class at the end so it is accessible. This way, when you want to draw the collection you would just call the Draw method of the CGameCharacterCollection class and that would draw all of your models (which keeps GameMain quite tidy) like this:

pCGCC -> Draw(); //pCGCC = pointerGameCharacterCollection which was declared directly after the Collection class was created

Simple once you've done it a few times!!

The Present - XNA GameComponents
So now I want to do the same (or at least similar) thing in XNA using Game Components so I started with the IDrawable extension of the GameComponent class so that I could use it to render objects to the screen. Unfortunately, as stated before, there are no real resources for this (if you find one though, please post a comment with a link) so Iwas stuck using the error checking in the C# Visual Studio compiler (which seems to be getting much better). Once I stuggled through that I was able to get a version going!

So what's the structure, well it goes something like this (bear in mind: there may be errors here as I only just got it working so it's not refined yet!):

public class GameComponent1 : Microsoft.Xna.Framework.GameComponent, IDrawable
{
public GameComponent1(Game game)
: base(game)
{}

int drawOrder;
bool isVisible;

// variables declared here but not included to save space

public event EventHandler DrawOrderChanged;
public event EventHandler VisibleChanged;



public void Initialize(Vector3 pos, float rotY, float rotX, String model)
{

mdlMainCharacter = Game.Content.LoadModel>(model);
Position = pos;
RotationX = rotX;
RotationY = rotY;

base.Initialize();
}

public override void Update(GameTime gameTime)
{base.Update(gameTime);}


public int DrawOrder
{
get { return drawOrder; }
set { drawOrder = 1; }
}
public bool Visible
{
get { return isVisible; }
set { isVisible = true; }
}

public void Draw(GameTime gameTime)
{
// generic Draw code used here
}
}

If you don't use the Visible and DrawOrder methods then you will get horrible errors from the compiler! Anyway, so in your main Game.cs file you will need to create a new instance of the Game Component you created (GameComponent1 in this example), register it with the game, call the initialize manually so you can pass the variables, then call Draw within the Draw in the main game class:

GameComponent1 gC1;
Vector3 gameCharacter1Pos = new Vector3(500.0f, 10.0f, 10.0f);
public float gc1RotX = 0.0f;
public float gc1RotY = 0.0f;
String strMainCharacterModelName = "dude";

Then in your game constructor:

gC1 = new GameComponent1(this);
Components.Add(gC1);

In Initialize:

gC1.Initialize(gameCharacter1Pos, gc1RotY, gc1RotX, strMainCharacterModelName);

In Draw:

gC1.Draw(gameTime);

And that would get a character rendered to the screen (I used the dude model from the Skinned Model sample for GS 2.0).

What you can do now is you can create multiple instances of GameComponent1, or you could carry on and create the Collection Class, cool or what! :)

1 comment:

  1. Nice stuff! An alternative way of creating a Drawable Game Component is to inherit from the DrawableGameComponent class instead of the IDrawable interface. This means you don't have to explicitly declare the DrawOrder, UpdateOrder, etc. methods, and can help keep the code be a bit cleaner.

    ReplyDelete