XNA - weird frame rate drop when using Draw and DrawString together - c#

I have a Board class in my game. This Board class is responsible for drawing a number of Zone objects, which currently all have the same texture.
So my draw calls go like: Game1.Draw calls Board.Draw which calls Zone.Draw for every zone object, which calls Spritebatch.Draw
Draw method of Game1
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, cam.Transform);
board.Draw(gameTime);
spriteBatch.End();
}
Draw method in Board class (Board.Draw)
public void Draw(GameTime gameTime)
{
if (zones != null)
{
for (int x = 1; x < xbound-1; x++)
{
for (int y = 1; y < ybound-1; y++)
{
zones[x, y].Draw(gameTime);
}
}
}
}
Draw method in Zone (Zone.Draw)
public void Draw(GameTime gameTime)
{
if (Tile != null)
{
Game1.spriteBatch.Draw(Tile, new Rectangle((int)pos.X, (int)pos.Y, size, size), Color.White);
//Game1.spriteBatch.DrawString(font2, pos.X.ToString() + "," + pos.Y.ToString(), pos, Color.Blue);
}
}
Everything works fine until I start drawing strings. Uncommenting the DrawString call results in a catastrophic FPS drop from consistent 60 to 20-25.
But here's the weird part. I created a different method for drawing the strings, instead of doing it in Zone's Draw. I called this method after Board.Draw finished drawing the Zones.
New seperate method for drawing the strings
public void DrawNumbers(GameTime gameTime)
{
if (zones != null)
{
for (int x = 1; x < xbound - 1; x++)
{
for (int y = 1; y < ybound - 1; y++)
{
Game1.spriteBatch.DrawString(font2, x.ToString() + "," + y.ToString(), zones[x, y].getPos(), Color.Blue);
}
}
}
}
Now Game1's Draw looks like this
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, cam.Transform);
board.Draw(gameTime);
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, cam.Transform);
board.DrawNumbers(gameTime);
spriteBatch.End();
}
Doing it this way provides no performance hit! I get constant 60FPS again, which sounds counter-intuitive for me...
Any idea why its doing this? Why does calling DrawString right after a Draw call seem to be so costly?
Please edit or inquire if the question isn't clear.
Thanks in advance.

The fact that you're interspersing Draw() with DrawString() is a bit of a red herring; the relevant fact here is that you're repeatedly swapping textures.
Your zone objects are all on one texture. So when you first draw all the zones, and then draw all of the strings, this is what's happening in the video driver:
OK, we're about to draw polygons using zoneTexture. Set up our device state.
Draw all of the zone polygons.
Now we're going to draw polygons using fontTexture. Set up our device state.
Draw all of the string polygons.
Done.
On the other hand, when you intersperse your text with your zones, it looks more like this:
OK, we're about to draw polygons using zoneTexture. Set up our device state.
Draw the polygons for Zone 1.
Hold on, we've got to draw some text using fontTexture. Set up our device state.
Draw the polygons for the text for Zone 1.
OK, we've still got more zones to draw using zoneTexture. Set up our device state.
Draw the polygons for Zone 2.
Oops, Zone 2 also has text using fontTexture. Set up our device state.
Draw the polygons for the text for Zone 2.
OK, time for the next zone using zoneTexture. Set up our device state.
And on, and on, and on...
As you can see, interspersing Draw() calls which require different device states forces the graphics driver to do a heck of a lot more work! So that's why you're seeing a drop in FPS.

Related

Moving Draw() code to a Class

I'm creating a game in monogame, and I've loaded tiles in my game inside the Draw() function like so:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(danChar, charPosition, Color.White);
// loop below loads the 'grass' tiles only
// assuming gameworld size of 770x450
for (int i = 0; i < 770; i += 31) // adds 31 to i (per tile)
{
position = new Vector2(i, 392); // places incrementation into vector position
spriteBatch.Draw(gameTile, position, Color.White); // draws the tile each time
if (i == 744)
{
i = i + 26; // fills last space between 744 and 770
position = new Vector2(i, 392);
}
spriteBatch.Draw(gameTile, position, Color.White);
}
// loop below loads the brick tiles only (ones without grass)
spriteBatch.End(); // ends the spriteBatch call
base.Draw(gameTime);
}
However I would prefer that this was a separate class rather than being placed directly into the draw function, however I'm not too sure how to do this and would appreciate any help given.
Thanks in advance!
If you just want to move the code as is to another class, create your class (e.g. something like GameWorld seems to appropriate for your code)
public class GameWorld
{
// You may wish to move your gameTile definition into this class if it is the only
// class that uses it, and handle the content loading for it in here.
// e.g. if you're currently loading the texture in the LoadContent method in your game
// class, create a LoadContent method here and pass in ContentManger as a parameter.
// I've passed in the texture as a parameter to the Draw method in this example to
// simplify as I'm not sure how you're managing your textures.
public void Draw(SpriteBatch spriteBatch, GameTime gameTime, Texture2D gameTile)
{
// loop below loads the 'grass' tiles only
// assuming gameworld size of 770x450
for (int i = 0; i < 770; i += 31) // adds 31 to i (per tile)
{
Vector2 position = new Vector2(i, 392); // places incrementation into vector position
spriteBatch.Draw(gameTile, position, Color.White); // draws the tile each time
if (i == 744)
{
i = i + 26; // fills last space between 744 and 770
position = new Vector2(i, 392);
}
spriteBatch.Draw(gameTile, position, Color.White);
}
// loop below loads the brick tiles only (ones without grass)
}
}
Then the Draw method in your Game class would look like
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(danChar, charPosition, Color.White);
// Assuming you've created/loaded an instance of the GameWorld class
// called gameWorld in Initialize/LoadContent
gameWorld.Draw(spriteBatch, gameTime, gameTile);
spriteBatch.End(); // ends the spriteBatch call
base.Draw(gameTime);
}
Just make sure you're calling the Draw methods in the correct order. e.g. you want your player to appear above any background tiles.
I believe the default SpriteSortMode is Deferred which draws in the order the calls are made (i.e. from the back to the front).
You can specify a different SpriteSortMode in your call to spriteBatch.Begin() if you need to but for a simple game just move the Draw calls around.
More info on SpriteSortMode at MSDN if needed.
Similarly you can chain your Update, LoadContent methods into these classes if you wish, making sure to pass in anything you need as arguments.
Update:
To define gameWorld as an instance of the GameWorld class, you define it near the top of your game class, then typically initialize it in the Initialize method.
So your game class will look something like
public class MyGameName : Microsoft.Xna.Framework.Game
{
private SpriteBatch spriteBatch;
// other variable declarations
// Add a declaration for gameWorld
private GameWorld gameWorld;
protected override Initialize()
{
// Add the following line to initialize your gameWorld instance
gameWorld = new GameWorld();
}
// other existing code - your LoadContent, Update, Draw methods etc.
}

Xna draw order not working right

I have an 2d array of Texture2D, it holds the different parts of the map in this array. I have a problem though, when I run the game, the map is drawn correctly but for some reason the array[0, 0] texture overlaps all my textures including my player texture and mouse texture. Every other texture works as my mouse and player texture correctly overlaps the map.
I'm really confused right now as the map textures are being drawn together using a nested for loop.
Here is my draw method for my map which I call in the Game's Draw method:
public void Draw()
{
// Draws the Map from a 2D Array
for (int row = 0; row < mapTexture.GetLength(0); row++)
{
for (int col = 0; col < mapTexture.GetLength(1); col++)
{
spriteBatch.Draw(mapTexture[row, col], mapPosition[row, col], Color.White);
}//end for
}//end for
}//end Draw()
My actual draw method:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
spriteBatch.Draw(mouseIcon, mouseIconPosition, Color.White);
player.Draw();
map.Draw();
spriteBatch.End();
base.Draw(gameTime);
}//end Draw()
Try inverting the order that they're drawn, AND use SpriteSortMode.Deferred
You can try use overloaded SpriteBatch.Draw method with depth. As example:
SpriteBatch.Draw (Texture2D, Vector2, Nullable, Color, Single, Vector2, Single, SpriteEffects, Single) Adds a sprite to the batch of sprites to be rendered, specifying the texture, screen position, optional source rectangle, color tint, rotation, origin, scale, effects, and sort depth.
or can try change order for drawing:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
map.Draw(); // first
player.Draw(); // second
spriteBatch.Draw(mouseIcon, mouseIconPosition, Color.White); // third
spriteBatch.End();
base.Draw(gameTime);
}//end Draw()
(its for SpriteSortMode.Deferred)
P.S. Sorry for google-translating
oops... I have not updated comments before responding

Pixel Color does not always appear the same

I have a falling sand style game that is currently only using two colors, black and cyan. I am setting the colors of a texture using texture.SetData<Color>(colorArray);
The main program updates the colors, when a pixel leaves a spot, the color at that position is set to black, and the new spot is set to cyan. However, as you can see in the picture, the cyan color is not uniform through out (look closely at the bottom right, it is very dark blue). This is occurring on a windows phone emulator. At first I thought the screen might just be updating so fast that the colors are projecting and leaving to quickly, but after stopping the update method (to take the following picture) the colors remained the same.
Any ideas as to why this is happening?
There is no shading (unless it is being done automatically and I don't realize it). There is only: colorArray[position] = Black; and colorArray[newPos] = Cyan;
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
map.Draw(spriteBatch);
spriteBatch.DrawString(spriteFont, fps, Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
//map.Draw()
public void Draw(SpriteBatch sb)
{
sb.Draw(pixels, Vector2.Zero, Color.White);
}
public void Update()
{
//this is only a portion of the update method
colors[currentpos] = getColor(0);// returns a color from a list
//some more updates not related to the texture
//blah
//blah blah
colors[currentpos] = getColor(ID);// returns a color from a list
//at the end of the update loop
texture.SetData<Color>(colors);
}

Draw() method in XNA stops working inside a for loop

So far, this is my first XNA game and I'm having real trouble trying to learn this.
I'm following a tutorial from Microsoft, found here: XNA Xbox Live Indie Games
Every now and again, the code breaks. Admittedly, I have taken a couple of bits out that I didn't think I'd need and I've created two enemy classes rather than just the one, but I don't think I hit any major faultlines with my adjustments.
In the Draw() method in the main Game1.cs file, I've had to include a for loop that will iterate through a list of available enemies and draw them upon update. However, the line of code flags up as incorrect, and I have absolutely no idea why. I followed the tutorial, and it looks like it should work, but it doesn't. Here's the entire Draw() method:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.ForestGreen);
backRect.Width = 800;
backRect.Height = 480;
// TODO: Add your drawing code here
// Start drawing
spriteBatch.Begin();
spriteBatch.Draw(backgroundTexture, backRect, Color.White);
// Draw the Player
player.Draw(spriteBatch);
for (int i = 0; i < goblins.Count; i++)
{
goblins[i].Draw(spriteBatch);
}
// Stop drawing
spriteBatch.End();
base.Draw(gameTime);
}
It's the code inside the for loop that won't work. Any ideas how to fix it and/or any suggestions for a better tutorial?
I like this tutorial a lot:
http://xbox.create.msdn.com/en-US/education/tutorial/2dgame/getting_started
It got me started off pretty well.
You always need to call SpriteBatch.Begin() and SpriteBatch.End() on your sprite-batches. I am not sure about mixing them, but try to avoid it and use as few spritebatches as possible.
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.ForestGreen);
backRect.Width = 800;
backRect.Height = 480;
// TODO: Add your drawing code here
// Start drawing
spriteBatch.Begin();
spriteBatch.Draw(backgroundTexture, backRect, Color.White);
// Draw the Player
spriteBatch.Draw(playerTexture, playerRect, Color.White);
for (int i = 0; i < goblins.Count; i++)
{
spriteBatch.Draw(goblins[i].Texture, goblins[i].Rect, Color.White);
}
// Stop drawing
spriteBatch.End();
base.Draw(gameTime);
}
See here for the documentation.

Drawing a "floor" texture

Alright, let's say that I have a tile texture of some floor or something. And I'd like that my player will walk on that.
How can I set this tile to make it a as a floor?
I need this tile texture to be all over the screen width right?
How am I doing it?
Thanks
If you want a really easy way, here it is:
First you create a new Class and name it Tile:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework; // Don't forget those, they will let you
using Microsoft.Xna.Framework.Content; // access some class like:
using Microsoft.Xna.Framework.Graphics; // Texture2D or Vector2
namespace Your_Project _Name
{
class Tile
{
}
{
So far so good, now create the Texture and Position in your class just like this:
namespace Your_Project _Name
{
class Tile
{
Texture2D texture;
Vector2 position;
public void Initialize()
{
}
public void Draw()
{
}
}
{
As you can see I also created two Methods, Initialize and Draw, now we will Initialize our
texture and position for the tile texture in the public void Initialize(),
I don't know how you use your ContentManager but here is a easy way:
public void Initialize(ContentManager Content)
{
texture = Content.Load<Texture2D>("YourfloorTexture"); //it will load your texture.
position = new Vector2(); //the position will be (0,0)
}
Now we need to draw our texture a number of time how will we do that? The way thasc said, the code can be more complex but here is one that you will understand, I will add a SpriteBatch so I can Draw. All this is done in the public void Draw():
public void Draw(SpriteBatch spriteBatch)
{
for (int i=0; i<30;i++) //will do a loop 30 times. Each Time i will =
//a valor from 0 to 30.
{
spriteBatch.Draw(texture, position, Color.White);
//Will draw the texture once, at the position Vector2
//right now position = (0,0)
spriteBatch.Draw(texture, new Vector2((int)i,(int)i), Color.White);
//Will Draw the texture 30 times, the first time on the position (0,0)
//Second Time on (1,1) .. third (2,2) etc...
spriteBatch.Draw(texture, new Vector2((int)position.X + (i * texture.Width), (int)position.Y + (i * texture.Height), Color.White));
//Will Draw the Texture 30 times Spaced by the Width and height
//of the texture (this is the code you need)
}
}
I didn't tried it but it should work, now its just a sample, you can figure out the rest. There is a lot of other methods to do it but this one is really easy. Ok, now the final step is to implement this class so go in your principal class where you have all your code and before this:
public Game1()
Create a new instance of your tile class
Tile tile;
and Initialize it in the protected override void Initialize():
tile = new Tile();
tile.Initialize(Content);
Now you have to draw it on the screen go at the end of the class and find protected override void Draw(GameTime gameTime) and call the draw method of our class:
spriteBatch.Begin();
tile.Draw(spriteBatch);
spriteBatch.End();
This is all the steps to complete a plain simple tile system. As I said there is a lot of others methods you just have to read tutorials about them or create them on your own.
If you don't plan on doing anything extra with the tiled background, I'd recommend thasc's solution and tile the sprite in a single call.
To do that, you create a rectangle as large as your background, and pass SamplerState.LinearWrap to SpriteBatch.Begin, then call Draw on the background rectangle.
Rectangle backgroundRect = new Rectangle(0, 0, backWidth, backHeight);
spriteBatch.Begin(..., ..., SamplerState.LinearWrap, ..., ...);
spriteBatch.Draw(backgroundTexture, backgroundRect, Color.White);
spriteBatch.End();
In case you're curious, what this does is create a single polygon that covers the background area, which will grab coordinates off your texture from 0.0f to backWidth. Textures are usually mapped between (0.0f, 0.0f) and (1.0f, 1.0f), which represent the corners of the given texture. If you go beyond these boundaries, TextureAddressMode defines how these coordinates will be treated:
Clamp will cut down the coordinates back into the 0-1 range.
Wrap will wrap the coordinates back to 0, so 0.0 = 2.0 = 4.0 = etc. and 1.0 = 3.0 = 5.0 = etc.
Mirror will also wrap, but mirroring the texture every other pass, basically going left-to-right-to-left-etc. as the polygon is rendered.

Categories

Resources