Monday 14 February 2011

XNA A common Sprite Batch as a Game service

In a previous Post it was pointed out that using game components can lead to multiple sprite batches in each component. A solution to this was presented to me by Neil Gannon and I am applying it here to multiple components were each component just has a sprite batch draw event and each component

public class Game1 : Microsoft.Xna.Framework.Game
{
MovingCircle myCircle;
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;


.



.



Then in the game initialize function we create the sprite batch as a service and we set up the moving service components. The Constructor for the Moving Circle Component accesses the Game Components and adds itself. Hence the commented out line  below. Again Neil’s style.



protected override void Initialize()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
this.Services.AddService(typeof(SpriteBatch), spriteBatch);
for (int i = 0; i < 9; i++)
{
myCircle = new MovingCircle(this);
//this.Components.Add(myCircle);
}
base.Initialize();
}



Now to the Game Draw event.



protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
base.Draw(gameTime);
spriteBatch.End();
}


Note: The base.Draw(gameTime);  call before the End() call allows all the Draw() events of the components to be marshalled before the sprite batch End() call commits the draws to the graphics device.



The Code for the Moving Circle Class is thus….



 



public class MovingCircle : Microsoft.Xna.Framework.DrawableGameComponent
{
SpriteBatch spriteBatch;
Texture2D movingCircle;
Vector2 movingCirclePosition;
Vector2 Target;


.



.



public MovingCircle(Game game)


            : base(game)


        {


            spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));


            game.Components.Add(this); // Add yourself

       
}



// The component adds its own content


protected override void LoadContent()
{
movingCircle = Game.Content.Load<Texture2D>("MovingCircle");
movingCirclePosition = new Vector2(LocalRand.RMinMax(0,600), LocalRand.RMinMax(0,400));
Target = new Vector2(LocalRand.RMinMax(0, 600), LocalRand.RMinMax(0, 400));
}


// Update the moving Circle    
public override void Update(GameTime gameTime)
{
movingCirclePosition = Vector2.Lerp(movingCirclePosition, Target, 0.1f);
if (Vector2.Distance(movingCirclePosition, Target) < 0.2)
{
Random r = new Random();
movingCirclePosition = Target;
Target = new Vector2(r.Next(600), r.Next(400));
}
base.Update(gameTime);
}
// Line up the Draw() for this component
public override void Draw(GameTime gameTime)
{
spriteBatch.Draw(movingCircle, movingCirclePosition, Color.Red);
base.Draw(gameTime);
}
The Result


MovingComponents


The Code is here

1 comment:

  1. My thoughts exactly. I wonder why this isn't the way it's done by default - with all the bad rap about multiple SpriteBatch.End() calls being a performance hit, you'd think they'd just give you a Game.SpriteBatch property and tell you to make all your Draw calls from that. You can always create a new SpriteBatch if you need it for eg. Effects, and there's no harm with nesting calls to Begin()...

    ReplyDelete