I am currently working on a game, in which we need to combine the use of DrawUserIndexedPrimitives and normal spriteBatch.Draw. Not combined as we use them at the same time, but we first have to draw some 2d sprites using spriteBatch, where after we disable spriteBatch to enable basicEffect and draw the primitives, and at last enable spriteBatch again. The code below shows the section of the code, where the problem is occurring.
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, null, cam.get_transformation(GraphicsDevice));
levelController.Draw(spriteBatch);
if (gameReady)
{
foreach (GameObject.GameObject go in gameObjects)
{
go.Draw(spriteBatch);
}
spriteBatch.End();
foreach (GameObject.GameObject go in gameObjects)
{
if (go is GameObject.Enemy.Enemy)
{
GameObject.Enemy.Enemy enemy = (GameObject.Enemy.Enemy)go;
basicEffect = new BasicEffect(graphics.GraphicsDevice);
basicEffect.VertexColorEnabled = true;
basicEffect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, enemy.GetCollisionTriangle.Triangle, 0, 3, enemy.GetCollisionTriangle.Indices, 0, 1);
}
}
}
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, null, cam.get_transformation(GraphicsDevice));
If the code below gets quoted out, the lag stops.
basicEffect = new BasicEffect(graphics.GraphicsDevice);
basicEffect.VertexColorEnabled = true;
basicEffect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, enemy.GetCollisionTriangle.Triangle, 0, 3, enemy.GetCollisionTriangle.Indices, 0, 1)
Can it really be that we can't both use spriteBatch and basicEffect without the game lags a lot? it has been tested on 3 different computers, from a very old laptop, to a brand new gamer pc.
The game is unplayable with the lag that occurs.
I think you should create the basiceffect elsewhere then your drawing routine. If I guess right you will use the same basiceffect all the time, so put it into inicializing, since "new" per every frame (edit: in a foreach for each and every object) can cost performance.
Related
(Cross post from Gamedevs)
I'm working with Monogame to do a 2d sprite engine. I've gotten it work pretty well where I can move lots of sprites around the screen with the effects and positions that I want.
Last night I tried to add a feature and I can't quite get it to work right.
I'm trying to create permanent trails that the moving sprites can leave behind. So I have a mostly transparent PNG with some white lines on it called "streak". The idea is that I set up a new Texture2D surface (called streakoverlay) and I draw the streaks on to it.
I switch back to the back buffer and draw: The background, the streakoverlay, and finally the sprite on top of it. The streaks should build up over time. It almost works, but the trouble I have is that streakOverlay seems to clear itself every time. How I'd like it to behave would be the same behaviour as having the graphics display without this line GraphicsDevice.Clear(Color.White); - everything would just pile up in it.
Instead it resets back to the Purple transparent color that is the default for that texture. Any suggestions? Here's the core piece of the rendering engine:
GraphicsDeviceManager graphics;
RenderTarget2D streakOverlay;
SpriteBatch spriteBatch;
private Texture2D bkg;
private Texture2D prototype;
private Texture2D streak;
//Initialize
graphics = new GraphicsDeviceManager(this);
GraphicsDevice.Clear(Color.Transparent);
streakOverlay = new RenderTarget2D(GraphicsDevice, 200,200);
//Load Content
bkg = Content.Load<Texture2D>("bkg"); //Background image
prototype = Content.Load<Texture2D>("prototype"); //Prototype sprite that moves around
streak = Content.Load<Texture2D>("blueStreak"); //Trails being left by Prototype sprite
graphics.GraphicsDevice.SetRenderTarget(streakOverlay);
GraphicsDevice.Clear(Color.Transparent); //Attempt to make the streakoverlay is fully transparent.
graphics.GraphicsDevice.SetRenderTarget(null);
//Draw
Random r = new Random();
graphics.GraphicsDevice.SetRenderTarget(streakOverlay); //Switch to drawing to the streakoverlay
spriteBatch.Begin();
//Draw some streaks that should accumulate
spriteBatch.Draw(streak, new Vector2(r.Next(0, 200), r.Next(0, 200)), null, Color.White, 0f, new Vector2(25, 25), 1f, SpriteEffects.None, 1f);
spriteBatch.End();
//Switch back to drawing on the back buffer.
graphics.GraphicsDevice.SetRenderTarget(null);
spriteBatch.Begin();
spriteBatch.Draw(bkg, new Rectangle(0, 0, 2000, 2000), Color.White); //Draw our background
spriteBatch.Draw(streakOverlay, new Vector2(0, 0), Color.White); //Put the overlay on top of it
spriteBatch.Draw(prototype, new Vector2(_spritePrototype.getX, _spritePrototype.getY), null, Color.White, DegreeToRadian(rotationSprite), new Vector2(25, 25), .5f, SpriteEffects.None, 0f); //Draw the sprite that's moving around.
spriteBatch.End();
base.Draw(gameTime);
You need to use the extended constructor for RenderTarget2D that takes 4 parameters and specify PreserveContents. The assumption is that render targets are often reused and so when it is set it gets cleared automatically unless this flag is set.
Im just experimenting with pixel shader. I found a nice blur effect and now Im trying to create an effect of blurring an image over and over.
HOW I want to do that: I want to render my image hellokittyTexture in a RenderTarget applying the blur effect, then replace hellokittyTexture with the result of that render and do it over and over again every Draw iteration:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
GraphicsDevice.SetRenderTarget(buffer1);
// Begin the sprite batch, using our custom effect.
spriteBatch.Begin(0, null, null, null, null, blur);
spriteBatch.Draw(hellokittyTexture , Vector2.Zero, Color.White);
spriteBatch.End();
GraphicsDevice.SetRenderTarget(null);
hellokittyTexture = (Texture2D) buffer1;
// Draw the texture in the screen
spriteBatch.Begin(0, null, null, null, null, null);
spriteBatch.Draw(hellokittyTexture , Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
But I get this error "The render target must not be set on the device when it is used as a texture." Because hellokittyTexture = (Texture2D) buffer1; is not copying the texture but the reference to the RenderTarget (basicly they are the same object after the asignation)
Do you know a nice way to get the Texture inside the RenderTarget? or a more elegant way to do what Im trying?
spriteBatch.Draw(hellokittyTexture , Vector2.Zero, Color.White);
In this line, you're drawing the texture... to itself... That can't happen.
Assuming buffer1 and hellokittyTexture have been properly initialized, replace this line:
hellokittyTexture = (Texture2D) buffer1;
with this:
Color[] texdata = new Color[hellokittyTexture.Width * hellokittyTexture.Height];
buffer1.GetData(texdata);
hellokittyTexture.SetData(texdata);
This way, hellokittyTexture will be set as a copy of buffer1, rather than a pointer to it.
Just a little addition to McMonkey answer:
I get this error: "You may not call SetData on a resource while it is actively set on the GraphicsDevice. Unset it from the device before calling SetData."
I solved it by creating a new texture. Dont know if this could be a performance problem, but its working now:
Color[] texdata = new Color[buffer1.Width * buffer1.Height];
buffer1.GetData(texdata);
hellokittyTexture= new Texture2D(GraphicsDevice, buffer1.Width, buffer1.Height);
hellokittyTexture.SetData(texdata);
I'm drawing multiple circles (not filled) on the screen, I'm using the same sprite for all of them, but different scale values. Problem is, when there's about 6-10 of them, it lags a lot. Texture itself is 400x400 png picture. Note when using spritebatch.Draw() method with least amount of arguments, performance improves significantly. I'm drawing when cycling through list which holds scale values. It may be important to mention, I'm developing for WP7 platform and lag occurs only on slower devices.
Here's code I'm using:
for (int i = 0; i < active_triangles.Count; i++)
{
spriteBatch.Draw(circle, circle_origin, null, Color.White, 0, circle_pivot, scales[i], SpriteEffects.None, 0);
active_triangles[i].Draw(spriteBatch);
}
triangles' Draw method:
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, frame, Color.White * alpha, rotation, pivot, 1, SpriteEffects.None, 0);
}
I'm currently making a game in XNA. I have been going well so far but I don't know how to change the texture of a sprite whilst running the game. An example of this would be. When the character is still that's one image, then when he walks that's a different image. How would I make this happen?
Run checks and just set the instance's Texture2D texture to some other texture from your preloaded library.
I usually load all the textures in my content folder into a dictionary and use like so:
var StringTextureDic = new Dictionary<string, Texture2D>();
// code that loads all textures into the dictionary, file names being keys
// whenever I need to assign some texture, I do this:
if (!playerIsMoving)
Player.texture = StringTextureDic["player standing"];
if (playerIsMoving)
Player.texture = StringTextureDic["player moving"];
Well, actually, change the player texture midgame is a bad idea. In such cases, i prefer to use a texture sheet.
Rectangle frame;
int curFrame;
int frameWidth;
int frameHeight;
int runAnimationLength;
Update()
{
//Handle your "animation code"
if(playerIsMoving)
curFrame++; //Running
if(curFrame == runAnimationLength)
curFrame =0;
else
curFrame = 0; //Standing still
}
Draw(SpriteBatch spriteBatch)
{
frame = new Rectangle(curFrame*frameWidth,curFrame*frameHeight,frameWidth,frameHeight);
spriteBatch.Draw(
texture,
position,
**frame**,
color,
rotation,
origin,
SpriteEffects.None,
1);
}
I am trying to draw text on to the screen using a spritefont in XNA.
I can get the text to appear, but no matter what I try there is always something going wrong with something else when I draw the text.
I am trying to display the FPS.
I have tried it in a few different ways and in various different XNA projects I have made.
Some of the things that happen when drawing the text to the screen include -
Vertices being drawn further away and in the wrong spots, vertices not being drawn at all, wire frame mode not being able to be turned on.
Just depending on how I try to render the text I always end up with one of these.
And then if I comment out the part that draws the text, everything will be back to normal.
Here is some code
My variables at the top of the code are -
GraphicsDeviceManager graphics;
SpriteFont font;
SpriteBatch spriteBatch;
In my LoadContent I have
font = Content.Load<SpriteFont>("SpriteFont1");
RasterizerState rs = new RasterizerState();
rs.FillMode = FillMode.WireFrame;
GraphicsDevice.RasterizerState = rs;
spriteBatch = new SpriteBatch(GraphicsDevice);
And here is my entire Draw method -
protected override void Draw(GameTime gameTime)
{
CreateMesh();
GraphicsDevice.Clear(Color.SkyBlue);
effect.Parameters["View"].SetValue(cam.viewMatrix);
effect.Parameters["Projection"].SetValue(projectionMatrix);
effect.CurrentTechnique = effect.Techniques["Technique1"];
effect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Count, 0, indices.Length / 3);
spriteBatch.Begin();
spriteBatch.DrawString(font, frameRate.ToString(), new Vector2(1, 1), Color.Black);
spriteBatch.End();
frameCounter++;
base.Draw(gameTime);
}
This is the closest way I have foundto working yet, but for some reason it makes wireframe not work no matter what I try to do, and I really need wire frame.
EDIT:
I debugged the fill mode as the program runs and it sets it back to solid on its own for some reason when using the sprite font.
I can reset the the fill mode every frame before the vertices are drawn, but I'd really rather not have to do that.
You should apply your rasterizer state just before drawing somthing win wireframe mode, not only in the load method... because batch.Begin() method will set a default rasterizer state
if you want use other rasterizer state for spritebatch, you should provide one
spritebatch.Begin(SortMode,...,,.., rs );
your code should be changed this way:
static RasterizerState rs = new RasterizerState() { FillMode = FillMode.Wireframe; }
protected override void Draw(GameTime gameTime)
{
CreateMesh();
GraphicsDevice.Clear(Color.SkyBlue);
GraphicsDevice.RasterizerState = rs;
effect.Parameters["View"].SetValue(cam.viewMatrix);
effect.Parameters["Projection"].SetValue(projectionMatrix);
effect.CurrentTechnique = effect.Techniques["Technique1"];
effect.CurrentTechnique.Passes[0].Apply();
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Count, 0, indices.Length / 3);
....
Using a SpriteBatch changes the render state to the point that 3D graphics no longer work unless you change the render state back to a 3D-friendly state. See this article for more details.
http://blogs.msdn.com/b/shawnhar/archive/2006/11/13/spritebatch-and-renderstates.aspx