This question already has answers here:
Xna draw order not working right
(2 answers)
Closed 9 years ago.
Problem: When an if clause is put around a spriteBatch.Draw statement, it seems to affect other spriteBatch.Draw that is outside the if clause .
This is (part of) the code in the Draw method:
if(OvertakeMouseHover == true)
{
spriteBatch.Draw(OvertakeButton, OvertakeButtonPosition, Color.White);
}
spriteBatch.Draw(Car1, Car1Position, Color.White);
spriteBatch.Draw(Car2, Car2Position, Color.White);
And this is the code setting OvertakeMouseHover:
if (mouse.X > OvertakeButtonPosition.X && mouse.X < OvertakeButtonPosition.X + OvertakeButton.Width &&
mouse.Y > OvertakeButtonPosition.Y && mouse.Y < OvertakeButtonPosition.Y + OvertakeButton.Height)
{
//Overtake button mouseover
OvertakeMouseHover = true;
}
else
{
OvertakeMouseHover = false;
}
When running the above code, OvertakeButton appears as expected (ie when the mouse is hovering over its location). Car2 appears as expected (all the time). However Car1, rather than appearing all the time, appears and disappears with the OvertakeButton, as though spriteBatch.Draw(Car1...) is within the if statement.
The following code makes all 3 sprites appear all the time:
//if(OvertakeMouseHover == true)
//{
spriteBatch.Draw(OvertakeButton, OvertakeButtonPosition, Color.White);
//}
spriteBatch.Draw(Car1, Car1Position, Color.White);
spriteBatch.Draw(Car2, Car2Position, Color.White);
I can't for the life of me work out why spriteBatch.Draw(Car1..) is affected by the if statement, but it definitely looks like it is to me. All ideas welcome!
EDIT - and this code makes all 3 sprite appear and disappear with the MouseHover. It's as though the bracket is one line lower than it should be:
if (OvertakeMouseHover == true)
{
spriteBatch.Draw(OvertakeButton, OvertakeButtonPosition, Color.White);
spriteBatch.Draw(Car1, Car1Position, Color.White);
}
spriteBatch.Draw(Car2, Car2Position, Color.White);
EDIT 2. Okay this is to do with the image appearing in front/behind of my background. In which case the issue is to do with sorting. How do I force the sorting order? I assumed it was just back to front in order of the code but that doesn't seem to be it.
EDIT 3. Entire draw method. Currently investigating what the various sort options do.
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.Backtofront, BlendState.AlphaBlend);
spriteBatch.Draw(RaceBackground, BackgroundPosition, Color.White);
spriteBatch.Draw(Car1, Car1Position, Color.White);
spriteBatch.Draw(Car2, Car2Position, Color.White);
if (OvertakeMouseHover == true)
{
spriteBatch.Draw(OvertakeButton, OvertakeButtonPosition, Color.White);
}
string output = Car1Speed.ToString();
// Find the center of the string
Vector2 FontOrigin = Font1.MeasureString(output) / 2;
// Draw the string
spriteBatch.DrawString(Font1, output, FontPos, Color.LightGreen,
0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
You should call SpriteBatch.Begin with SpriteSortMode.Deferred
Deferred guarantees that your sprites will be drawn in the order that you called the Draw method for them.
Deferred
Sprites are not drawn until End is called. End will apply graphics
device settings and draw all the sprites in one batch, in the same
order calls to Draw were received. This mode allows Draw calls to two
or more instances of SpriteBatch without introducing conflicting
graphics device settings. SpriteBatch defaults to Deferred mode.
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritesortmode.aspx
according to the documentation, BackToFront says that it works the same as deferred, except that it takes depth into account, but you might be running into a bug with it. This is not the first time that I would have seen this bug.
If you use SpriteSortMode.BackToFront you need to specify the depth of each sprite, otherwise the game will draw them at the same time, making them to "flicker".
You may need to use a different SpriteBatch.Draw overload that has float layerDepth parameter, something like this one:
public void Draw (
Texture2D texture,
Vector2 position,
Nullable<Rectangle> sourceRectangle,
Color color,
float rotation,
Vector2 origin,
float scale,
SpriteEffects effects,
float layerDepth
)
Or better, as Sam I am previously wrote, use SpriteSortMode.Deferred which is easier to use and understand.
Related
I have followed a SharpGL tutorial that can display a rotating block. Initially this only had default colors drawn on it with gl.Color(r, g, b). After this succeeded I tried to texture the cube with an uv map.
When I run the application fullscreen while only coloring the cube (with the sharpGL component covering the entire inside of the application) I get 70~80 fps only for displaying a colored cube. When I enable OpenGL.GL_TEXTURE_2D and draw the textures on a singular cube I get 8~9 fps.
Whenever a bitmap is loaded for use as a texture, it is stored in the memory. This drop in framerates only occurs after I enable OpenGL.GL_TEXTURE_2D and call gl.TexCoord(c1, c2) for all coordinates. Actually moving the object with gl.Rotate(angle, x, y, z) does not noticably affect performance.
The provided data for the method including GetBlockUv and CubeCoordinates are static float-arrays.
Is SharpGL supposed to perform this poorly (i.e. on displaying a singular cube) or is there another reason? Am I doing something wrong that is affecting performance? Is applying textures supposed to affect the performance like that?
The main draw Event happens in a Block:
public void DrawBlock(object sender, OpenGLEventArgs args)
{
// Get the OpenGL instance that's been passed to us.
OpenGL gl = args.OpenGL;
// Reset the modelview.
gl.LoadIdentity();
// Move the block to its location
gl.Translate(Coord.X, Coord.Y, Coord.Z);
gl.Rotate(angle, 1.0f, 1.0f, 0.5f);
angle += 3;
// retrieve the right texture for this block and bind it.
Texture blockTex = BlockTexture.GetBlockTexture(gl, _type);
blockTex.Bind(gl);
// retrieve the uv map for this block
float[] uv = BlockTexture.GetBlockUv(_type);
// retrieve the coordinates for a cube
float[] cube = CubeCoordinates();
gl.Enable(OpenGL.GL_TEXTURE_2D);
// Draw the cube with the bound texture.
gl.Begin(OpenGL.GL_QUADS);
//
//
// Begin by allowing all colors.
gl.Color(1.0f, 1.0f, 1.0f);
// since the uv index increments with 2 each time, we will be keeping track of this separately.
int uvInd = 0;
// i denotes the current coordinate. Each coordinate consists of 3
// values (x, y, z), thus letting us skip 3.
//
// Seeing as we are creating quads, it is expected that cube.Length
// is 3 * 4 * N (where n is a whole number)
for (int i = 0; i < cube.Length; i += 3)
{
// color experiment
//if (i < cube.Length / 3)
//{
// gl.Color(1.0f, 0.00f, 0.00f);
//}
//else if (i < 2 * (cube.Length / 3))
//{
// gl.Color(0.0f, 1.0f, 0.0f);
//}
//else
//{
// gl.Color(0.0f, 0.0f, 1.0f);
//}
try
{
// set the coordinate for the texture
gl.TexCoord(uv[uvInd], uv[uvInd + 1]);
// set the vertex
gl.Vertex(cube[i], cube[i + 1], cube[i + 2]);
}
catch (IndexOutOfRangeException e)
{
throw new IndexOutOfRangeException(
"This exception is thrown because the cube map and uv map do not match size");
}
// increment the uv index
uvInd += 2;
}
gl.End();
gl.Disable(OpenGL.GL_TEXTURE_2D);
}
OpenGL is initialized elsewhere
private void OpenGLControl_OpenGLDraw(object sender, OpenGLEventArgs args)
{
// Get the OpenGL instance that's been passed to us.
OpenGL gl = args.OpenGL;
// Clear the color and depth buffers.
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
// call the draw method of the GameRunner if the
// GameRunner has already been created.
game?.DrawOpenGL(sender, args);
// Flush OpenGL.
gl.Flush();
}
private void OpenGLControl_OpenGLInitialized(object sender, OpenGLEventArgs args)
{
// Enable the OpenGL depth testing functionality.
args.OpenGL.Enable(OpenGL.GL_DEPTH_TEST);
}
All the intermediate GameRunner does right now is call the DrawBlock routine.
What I mainly would want to know is some insight into the performance I can expect of openGL / sharpGL and whether there are better alternatives. I would like to keep using the WPF architecture surrounding the game, but if openGL inside WPF is more meant as a gimmick, that might not be the best course of action.
I've been having the exact same issue, and it seems to be the case that either SharpGL or the WPF control itself are using software rendering. I tested this by disabling my main display adapter in Device Manager and got the exact same performance as I did with it enabled.
I don't know how to enable hardware acceleration though, so I don't actually know how to fix the issue.
I'm making a game in C# and XNA 4.0. It uses multiple objects (such as a player character, enemies, platforms, etc.), each with their own texture and hitbox. The objects are created and drawn using code similar to the following:
class Object
{
Texture2D m_texture;
Rectangle m_hitbox;
public Object(Texture2D texture, Vector2 position)
{
m_texture = texture;
m_hitbox = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, m_hitbox, Color.White);
}
}
Everything works properly, but I also want to allow the player to resize the game window. The main game class uses the following code to do so:
private void Update(GameTime gameTime)
{
if (playerChangedWindowSize == true)
{
graphics.PreferredBackBufferHeight = newHeight;
graphics.PreferredBackBufferWidth = newWidth;
graphics.ApplyChanges();
}
}
This will inevitably cause the positions and hitboxes of the objects to become inaccurate whenever the window size is changed. Is there an easy way for me to change the positions and hitboxes based on a new window size? If the new window width was twice as big as it was before I could probably just double the width of every object's hitbox, but I'm sure that's a terrible way of doing it.
Consider normalizing your coordinate system to view space {0...1} and only apply the window dimensions scalar at the point of rendering.
View Space to Screen Space Conversion
Pseudo code for co-ordinates:
x' = x * screenResX
y' = y * screenResY
Similarly for dimensions. Let's say you have a 32x32 sprite originally designed for 1920x1080 and wish to scale so that it fits the same logical space on screen (so it doesn't appear unnaturally small):
r = 32 * screenResX' / screenResY
width' = width * r
height' = height * r
Then it won't matter what resolution the user has set.
If you are concerned over performance this may impose, then you can perform the above at screen resolution change time for a one-off computation. However you should still always keep the original viewspace {0...1}.
Collision Detection
It's arguably more efficient to perform CD on screen space coordinates
Hope this helps
What i want to do is make a title like Terraria just the rocking back and forth side of it not the graphics side i know that its just a .png rocking back and forth but could anyone help me and other people who read this and what to know how to do it?
So what i would like is to learn how to make a rocking back and forth image like the title displayed in Terraria?
Something like this for the people who don't know what Terraria is.
http://www.youtube.com/watch?v=3K8PMG42l3M
It appears that the logo is rotating and changing its size over non-equal intervals of time.
First, you need to get familiar with this method:
SpriteBatch.Draw Method (Texture2D, Vector2, Nullable, Color, Single, Vector2, Single, SpriteEffects, Single)
The parameters are:
Texture2D texture, // texture of your logo
Vector2 position, // where to draw
Nullable<Rectangle> sourceRectangle, // null
Color color, // Color.White
float rotation, // you will be changing this
Vector2 origin, // and this
float scale, // also this
SpriteEffects effects, // SpriteEffects.None
float layerDepth // 0
Use these variables:
float rotation = 0,
rotationSpeed = 0.002f, // this is how much rotation will change each frame
maximumAngle = 0.1f,
minimumAngle = -0.1f,
rotationDirection = 1,
scale = 1f, // 1 means 100%, 0.95f = 95%
scaleChange = 0.005f, // this may seem not much, but it's enough
maxScale = 1.1f,
minScale = 0.9f,
scaleDirection = 1;
Just put DrawLogo(); in your main Draw() method.
void DrawLogo()
{
// these two very similar pieces of code will control scale and rotation
if (rotationDirection > 0)
{
if (rotation < maximumAngle)
rotation += rotationSpeed;
else
rotationDirection = -rotationDirection;
}
else
if (rotation > minimumAngle)
rotation -= rotationSpeed;
else
rotationDirection = -rotationDirection;
if (scaleDirection > 0)
{
if (scale < maxScale)
scale += scaleChange;
else
scaleDirection = -scaleDirection;
}
else
if (scale > minScale)
scale -= scaleChange;
else
scaleDirection = -scaleDirection;
Texture2d t2d = logoTexture;
spriteBatch.Draw(t2d,
centerScreen, // change this to `new Vector2(123, 456)`
null, // null means draw entire texture
Color.White, // no tinting
rotation, // the rotation we calculate above
new Vector2(t2d.Width / 2, t2d.Height / 2),
// this sets rotation point to the center of the texture
scale, // the scale we calculate above
SpriteEffects.None, // you can mirror your texture with this
0); // I usually leave it zero :p
}
This is tested and works just fine :)
You mean the effect we can see at about 1:16 (and probably also at other times), when you choose stuff in the menus?
Concept
As far as I can see, you can do this with simple rotations and scaling. So, if you do not want to make an animated gif (which you suppose it is), you can just do it inside your XNA code. Take a png or gif with alpha-channel (so that the non-text is transparent).
Then, when you draw it on the screen with spriteBatch.draw() you can choose one of the overloads that support scaling and rotation.
Then you have to set:
the rotation you want to have (which will be a rotation over time)
the origin (to the center of the image)
the scale (which will be scaling over time)
As the clock is sent to the update() method as far as I remember XNA, we will have to update the rotation and scale of the image there. We need the clock, because we cannot just set rotaton = 10° and XNA will handle everything for us. We have to calculate the current rotation in each time step ourselves. E.g. if a full rotation shall endure 10 seconds and 5 seconds have passed, then you know you have a half rotation. So we would tell XNA: Set our rotation to 180° now, and in the next time step, we might tell: Set our rotation to 190° now.
The basic concept is:
Calculate how much part of rotation/scale we have done in the current time step
Tell XNA to adjust this rotation/scale in this time step
Iterate these two steps again and again
Implementation
I think the best thing to do here, is using a sin() or cos() function for the scaling and rotation. The good things about them:
they have positive and negative values as well (so we can easily rotate in both directions)
they are smooth, meaning your rotation and scaling will not look too abrupt at the end of the rotation/scaling
I hope my maths is correct here. I will explain everything, so others can correct me if something is wrong. Or also you can find out, if something is wrong. We will use a sin() here, because it starts at 0, which in our case means that nothing should happen. That’s what we want: We want to begin at a situation where nothing happens.
Now, sin() has a cycle time of 2*PI. Of course, we do not want a scaling to last 2*PI, but rather something like 1000 milliseconds. We cannot change the definition of Math.Sin() in C#, but we can change the value we throw inside. So when we mean 1000 milliseconds, we will give Math.Sin() 2PI and when we mean 500 milliseconds, we give it PI.
We would define these member variables:
// define some variables for rotation and scale speed, in milliseconds
int fullRotationTime = 1000; // max rotation will be reached after 1 second
float maxRotationAngle = MathHelper.ToRadians(10); // we will rotate by 10 degree up and down
int rotationTimePassed = 0;
float currentRotationAngle = 0;
int fullScaleTime = 1000; // max scale will be reached after 1 second
float maxScaleSize = 1.2f; // we will scale to 20% larger max
int scaleTimePassed = 0;
float currentScaleFactor = 1.0;
And in the Update() method, we calculate how much of our rotation we already have done.
protected virtual void Update(GameTime gameTime)
{
int milliseconds = gameTime.ElapsedGameTime.TotalMilliseconds;
// these are the milliseconds in the current rotation
rotationTimePassed += milliseconds;
scaleTimePassed += milliseconds;
if (rotationTimePassed >= fullRotationTime)
rotationTimePassed %= fullRotationTime;
if (scaleTimePassed >= fullScaleTime)
scaleTimePassed %= fullScaleTime;
float rotationTimeAdjustedToTwoPi = ((float)rotationTimePassed)/fullRotationTime * 2* Math.PI);
currentRotationAngle = maxRotationAngle * Math.Sin(rotationTimeAdjustedToTwoPi);
// we do not want the scaling to be negative, thus we add 1 to the whole and
// divide by 2. Then the maximum will be 1 and the minimum 0
float scaleTimeAdjustedToTwoPi = ((float)scaleTimePassed)/fullScaleTime * 2* Math.PI);
currentScaleFactor = maxScaleSize * (Math.Sin(scaleTimeAdjustedToTwoPi) + 1)/2;
}
Then, in the Draw() method we can take the values calculated before and display our rotated and scaled image.
protected virtual void Draw()
{
spriteBatch.Begin();
spriteBatch.Draw(texture,
new Vector2(50, 50),
null,
Color.White,
currentRotationAngle,
new Vector2(texture.width/2, texture.height/2),
currentScaleFactor,
SpriteEffects.None,
0
);
spriteBatch.End();
}
It’s not tested, so there might even be syntax errors, but I at least the basic idea should be correct and I think the important thing is that you understand how it can be done conceptually.
Variable time steps
It’s easy to integrate the variable time steps user1306322 has mentioned into the code above. We had these if-conditions where we checked if the current time-slice is over, like this: if (rotationTimePassed >= fullRotationTime).
Now it we want to make the time-slices variable length, just adjust a new time-slice based on a random number here. Like this:
var rand = new Random();
if (rotationTimePassed >= fullRotationTime)
{
rotationTimePassed %= fullRotationTime;
// next rotation might take between 0.5 and 2.5 seconds
fullRotationTime = rand.next(500, 2500);
}
I just play around with XNA and when I wanted to click on a sprite and something happen, I put this code:
if(Mouse.GetState().LeftButton == ButtonState.Pressed)
{
if (sprite.Bounds.Contains(Mouse.GetState().X, Mouse.GetState().Y))
{
this.Exit();
}
}
how ever when I hover over the sprite with my mouse and click nothing happens, why?
And how do I fix this?
If this helps I wrote my 2D sprite in a rectangle
Texture.Bounds does not put the rectangle in the texture's place, the values of X and Y are both equal to 0.
You would have create your own rectangle to do .Contains() on based on your SpriteBatch.Draw() inputs.
The texture "logoTexture" is somewhere near the bottom left corner of the screen.
Please confirm that Bounds is calculated as such:
public Rectangle Bounds
{
get
{
return new Rectangle(position.X - width / 2, position.Y - height / 2, width, height);
}
}
I also suggest getting a reference to Mouse.GetState() a single time per update instead of calling it as needed.
I'm trying for hours now to solve this problem..
So.. I want to use different shaders on every Sprite I draw on the screen.
I tried to make different RenderTarget2Ds but no success.
No matter how hard I try to make the Graphicsdevice.Clear() Transparent, it just won't work.
(I use xna 3.1)
So all my sprites blocking the other sprites because I can't make the background transparent :\
Any idea ?
Code I use (it's a method):
graphicsDevice.SetRenderTarget(0, r2d);
graphicsDevice.Clear(ClearColor);
spriteBatch.Begin();
spriteBatch.Draw(
tex,
Dest,
Source,
moodcolor,
this.RotationAngle,
new Vector2(0, 0),
Zoom,
spriteEffect,
this.Z_Index);
spriteBatch.End();
graphicsDevice.SetRenderTarget(0, null);
graphicsDevice.Clear(ClearColor);
for (int i = 0; i < efs.Count; i++)
{
efs[i].effect.CurrentTechnique = efs[i].effect.Techniques["Deferred"];
foreach (EffectPass pass in efs[i].effect.CurrentTechnique.Passes)
{
spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);
efs[i].effect.Begin();
pass.Begin();
spriteBatch.Draw(r2d.GetTexture(), Vector2.Zero, Color.White);
spriteBatch.End();
pass.End();
efs[i].effect.End();
}
}
Update!
You don't have to use RenderTarget2D!
It's important to keep AlphaBlend!
This is solution for anybody who came across the same problem!
Solution snippet:
foreach (EffectPass pass in efs[ObjShaders[i].ShaderID].effect.CurrentTechnique.Passes)
{
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);
efs[ObjShaders[i].ShaderID].effect.Begin();
pass.Begin();
spriteBatch.Draw(
tex,
Dest,
Source,
moodcolor,
this.RotationAngle,
new Vector2(0, 0),
Zoom,
spriteEffect,
this.Z_Index);
spriteBatch.End();
pass.End();
efs[ObjShaders[i].ShaderID].effect.End();
}
Reading your question again, perhaps all you need to do is
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
Now if your sprite textures have an alpha channel, they will be blended correctly.