Thursday, 10 February 2011

XNA Game services

I read a good analogy recently explaining the concepts of Game Components and Game services. Game components are like the plumbing (or sewage depending on how good your code is) system underneath a city that is your game. The components are interconnected through the Game class, but can exist independently to certain extent. Game Services are like have a Large Zeppelin over the city with rope ladders that can you can use to grab elements that you need to reference in your game objects. You climb up the rope ladder and grab the type of object that you want. Game services allow you to share objects registered as Game services across Game components. It is reserved for items that a Game Component might need to preform a function


Here is how to use game services. You set up your objects in the main game


Say you want to use a common sprite batch that is set up in your main Game class

spriteBatch = new SpriteBatch(GraphicsDevice);


this.Services.AddService(typeof(SpriteBatch), spriteBatch);


the typeof function says what kind of ladder you will be using to access the service later.

then in the class that you want to retrieve it from (in this case a game drawable component) 

public ChaseImage(Game game, Texture2D CircleImage)
: base(game)
{
sp = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
image = CircleImage;
spriteFrame = new Rectangle(0,0, image.Width/4, image.Height/4);
imagePosition = new Vector2(LocalRand.RMinMix(0, 40), LocalRand.RMinMix(0, 40));
}



then you can use the shared sprite batch to draw the all components thus



public override void Draw(GameTime gameTime)
{
sp.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullCounterClockwise,null, SpriteScale);


sp.Draw(image, spriteFrame, null, new Color(new Vector4(1,1,1,0.02f)),1.0f,Vector2.Zero,SpriteEffects.None,1);


sp.End();


base.Draw(gameTime);
}


Note:




  1. If you have a collection of similar services all with the same typeof you could use the generic search code presented in the previous post to retrieve each of the services.


  2. This method only uses one sprite batch but still has multiple calls to the sprite batch for each component drawn.





Tuesday, 8 February 2011

Converting a class to a DrawableGameComponent

Given the original code for a sprite class the source of which is provide here was provided by Neil Gannon.

   1: class Sprite



   2: {



   3: public Sprite(Texture2D texture,Vector2 userPosition, int framecount)



   4:         {



   5:             spriteImage = texture;



   6:             position = userPosition;



   7:             numberOfFrames = framecount;



   8:             spriteHeight = spriteImage.Height;



   9:             spriteWidth = spriteImage.Width / framecount;



  10:  



  11:         }



  12: public void UpdateAnimation(GameTime gametime) {}



  13: public void Draw(SpriteBatch spriteBatch) {}



  14:  



  15: }




image



We need to sub class the class off DrawableGameComponent and then add an override for update and draw. These must be public overrides to match the method declarations in the the DrawableGameComponent Class. The game component needs it’s own sprite batch to draw to the graphics card and hence it needs to see the Games Graphics Device. But thankfully this is exposed in the component model for the game class. The Class becomes





   1: class Sprite : DrawableGameComponent



   2:     {



   3:         //sprite texture and position



   4:         Texture2D spriteImage;



   5:         Vector2 position;



   6:         SpriteBatch spriteBatch;



   7:         //the number of frames in the sprite sheet



   8:         //the current fram in the animation



   9:         //the time between frames



  10:         int numberOfFrames = 0;



  11:         int currentFrame = 0;



  12:         int mililsecindsBetweenFrames = 100;



  13:  



  14:         //the width and height of our texture



  15:         int spriteWidth = 0;



  16:         int spriteHeight = 0;



  17:  



  18:         //the source of our image within the sprite sheet to draw



  19:         Rectangle sourceRectangle;



  20:  



  21:         float timer = 0f;



  22:         



  23:         public Sprite(Game G, Texture2D texture,Vector2 userPosition, int framecount) :base(G)



  24:         {



  25:             spriteBatch = new SpriteBatch(G.GraphicsDevice);



  26:             spriteImage = texture;



  27:             position = userPosition;



  28:             numberOfFrames = framecount;



  29:             spriteHeight = spriteImage.Height;



  30:             spriteWidth = spriteImage.Width / framecount;



  31:             



  32:         }



  33:         



  34:         public override void Update(GameTime gametime)



  35:         {



  36:             timer += (float)gametime.ElapsedGameTime.Milliseconds;



  37:  



  38:             //if the timer is greater then the time between frames, then animate



  39:                     if (timer > mililsecindsBetweenFrames)



  40:                     {



  41:                         //moce to the next frame



  42:                         currentFrame++;



  43:  



  44:                         //if we have exceed the number of frames



  45:                         if (currentFrame > numberOfFrames – 1)



  46:                         {



  47:                             currentFrame = 0;



  48:                         }



  49:                         //reset our timer



  50:                         timer = 0f;



  51:                     }



  52:             //set the source to be the current frame in our animation



  53:                     sourceRectangle = new Rectangle(currentFrame * spriteWidth, 0, spriteWidth, spriteHeight);



  54:                     base.Update(gametime);



  55:             }



  56:  



  57:  



  58:  



  59:         public override void Draw(GameTime gameTime)



  60:         {



  61:             //draw the sprite , specify the postion and source for the image withtin the sprite sheet



  62:             spriteBatch.Begin(SpriteBlendMode.AlphaBlend);



  63:             spriteBatch.Draw(spriteImage, position,sourceRectangle, Color.White);



  64:             spriteBatch.End();



  65:             base.Draw(gameTime);



  66:         }



  67: }




Notes




  1. The constructor has to have a reference to the game that creates it and must pass it on to the Super class.


  2. We can use this reference in the constructor to access the Graphics device reference to create the sprite batch. We can alternatively access the publicly exposed Game property Game.GraphicsDevice and indeed you can reference the public exposed properties of the Game class from anywhere in the game components.


  3. You must remember to add the created sprites as components in the Game class when we create the sprite





   1: rollingImage = Content.Load<Texture2D>(@"Images\rolling");



   2: rolling = new Sprite(this,rollingImage, new Vector2(500, 50), 4);



   3: this.Components.Add(rolling);




Now we can subclass the Sprite Class and create two different types of Sprite game components. The player Sprite class will have an update method that will override the sprite update and deal with the input from the input device and the CGC Sprite Class will move around the screen.



We’ll look at those classes next time.



Postscript



It’s worth looking at the forum discussion here about the ins and outs of using multiple sprite batches or a single sprite batch for drawing components. So it would seem from the comments made by Shaun Hargreaves in the forum discussion referenced above that although components give good modularity and structure to games, but at the cost of efficiency in committing to the graphics device as multiple sprite batches in components seem not to be a good idea from a performance perspective!! So for multiple instances a Drawable game component may not be a good idea?? So back to the drawing  board eh? It would be a good idea to change the XNA framework so that it batches sprite batch (yeah I know – a bit of a recursive definition) and have a common end() method for them. The solution suggested is to have a Sprite Manager. But I thought that’s what a sprite batch should do!!



At that stage we might consider making the Sprite Class abstract as we would probably not want to create instances of it after that subclass action.

Thursday, 3 February 2011

XNA GameEngine Development Series « Running on Empty

Not for the Faint Hearted XNA GameEngine Development Series « Running on Empty but a good source for ideas.