I'm trying to create a top down game where you can move in 8 directions. I've implemented Vector2 in order to move in a normalised manner. I use a spritebatch system to draw with standard rectangles (source and destination) but soon changed to RectangleF as regular rectangles use integers.
The Spritebatch does not recognise the RectangleF and hence returns the error, "Cannot convert from 'System.Drawing.RectangleF' to 'Microsoft.Xna.Framework.Vector2'?". The Color also returns ambiguous and no matter 'System.Drawing' or 'Microsoft.Xna.Framework.Color', it always returns how it cannot be converted into 'Microsoft.Xna.Framework.Rectangle'.
In this code, a Vector2 (Position) is called in and used to draw the RectangleF's position. I then use the Spritebatch.Draw to draw a texture with the Source and Destination and it is always updated to give the implication of a moving character.
public static RectangleF destinationRectangle, sourceRectangle;
public void Draw(SpriteBatch spriteBatch)
{
int width = 64;
int height = 64;
realPositionX = PlayerMovement.Position.X;
realPositionY = PlayerMovement.Position.Y;
sourceRectangle = new RectangleF(currentFrame * width, row * height, width, height);
destinationRectangle = new RectangleF(realPositionX, realPositionY, width, height);
spriteBatch.Begin();
spriteBatch.Draw(Player, destinationRectangle, sourceRectangle, Microsoft.Xna.Framework.Color.White); //Error Occurs Here
spriteBatch.End();
}
I'm confused as how I should approach this issue, is there a way of drawing a RectangleF through SpriteBatch or do I have to look at another method?
Thanks.
There is absolutely no reason to use RectangleF.
Usually you use a pixel based coordinate system in your game and therefore the Rectangle Class is what you want to go for.
Other than that it is a better approach to store the players postion in a Vector2 and using the the appropriate overload:
SpriteBatch.Draw (Texture2D, Vector2, Color)
Related
Normally XNA start drawing sprite from top-left, but I would like to start draw object from bottom-center, how this could be done?
You want to specify a different origin in your SpriteBatch.Draw calls. The default is 0,0 (top-left). Note that the origin is relative to the sprite, not the screen.
So if your sprite is 64x64, you want to use an origin of 32x64 for bottom center.
e.g. using this override (MSDN)
spriteBatch.Draw (
texture,
position,
sourceRectangle,
color,
rotation,
new Vector2(32, 64), // origin
scale,
effects,
layerDepth
)
You can calculate these on the fly if you wish. e.g if you're using the full texture you could specify it as new Vector2(texture.Center.X, texture.Height). Or alternatively you could base it on the sourceRectangle if you're using a sprite sheet.
You need to specify a bunch of other arguments to use these Draw overrides but you can just pass in the defaults. The defaults are:
sourceRectangle: null = full texture
color: Color.White = default color (sprite colors will be used)
rotation: 0f = no rotation
scale: 1f = default scale
efects: SpriteEffects.None = no flipping
layerDepth: 0 = default layer
Lets say you are drawing an image WidthxHeight on position XxY.
spriteBatch.Draw(texture, position, Color.White);
First let's set the bottom of the image to those coordinates by subtracting images height from position's Y coordinate (subtracting because in XNA the Y-axis is inverted, not like in your math class)
spriteBatch.Draw(texture, position + new Vector2(0, -texture.Height), Color.White);
Second, let's set the image to the left by subtracting half of the image's width from position's X coordinate.
spriteBatch.Draw(texture, position + new Vector2(-texture.Width/2, -texture.Height), Color.White);
And there you have it.
Edit: Another thought: you can create new variable called DrawPosition and use that variable when needed, instead of always substracting. That would look something like this:
private Texture2D texture;
public Vector2 position;
public Vector2 DrawPosition
{ get { return position + new Vector2(-texture.Width/2, -texture.Height); } }
public void Draw(SpriteBatch spriteBatch)
{ spriteBatch.Draw(texture, DrawPosition, Color.White); }
Or, if this new variable doesn't make sense to you, create a function that will return the DrawPosition()
public Vector2 DrawPosition()
{ return position + new Vector2(-texture.Width/2, -texture.Height); }
So in order to get the Color[] data from a texture after it has been rotated in order to use this data for perpixel collisions, I use the following method to draw said texture (rotated) to a separate RenderTarget2D, then convert this back into a texture2D and get the color data from it:
public Color[] GetColorDataOf(SpriteBatch spriteBatch, Texture2D texture, float rotation)
{
// Get boundingBox of texture after rotation
Rectangle boundingBox = GetEnclosingBoundingBox(texture, rotation);
// Create new rendertarget of this size
RenderTarget2D buffer = new RenderTarget2D(GraphicsDevice, boundingBox.Width, boundingBox.Height);
// Change spritebatch to new rendertarget
GraphicsDevice.SetRenderTarget(buffer);
// Clear new rendertarget
GraphicsDevice.Clear(Color.Transparent);
// Draw sprite to new rendertarget
spriteBatch.Draw(texture, new Rectangle(boundingBox.Width / 2, boundingBox.Height / 2, texture.Width, texture.Height), null, Color.White, rotation, new Vector2(boundingBox.Center.X, boundingBox.Center.Y), SpriteEffects.None, 1f);
// Change rendertarget back to backbuffer
GraphicsDevice.SetRenderTarget(null);
// Get color data from the rendertarger
Color[] colorData = new Color[boundingBox.Width * boundingBox.Height];
Texture2D bufferTexture = (Texture2D)buffer;
bufferTexture.GetData(colorData);
return colorData;
}
Now I'm having two issues with that (I expect they are linked), firstly the texture gets drawn on screen, and all the Color[] data returned is empty (i.e all fields equal to 0).
** Edit **
Using Texture2D.SaveAsPng() I can see that bufferTexture is the correct size but just completely transparent indicating that the issue would lie in drawing to the buffer.
So I fixed it. Turns out I need to create another set of SpriteBatch.Begin() and SpriteBatch.End() calls around where I drew to the new rendertargets, otherwise it was just drawing to the backbuffer instead.
Very odd problem, when I try and draw my billboard sprite it always appears as a white block, changing the .draw color property still draws it as white, it also doesn't matter it I use a jpeg, or transparent png.
[EDIT]
So I'm now trying to use a Viewport instead of a basic effect to just get an x and y screen coordinate, I'll fix any scaling issue later, however now the image stays in the exact same spot (on the screen, it doesn't change position based on the camera) and doesn't get any bigger or smaller based on how far away it is
My new billboard rendering function:
public void Draw(Camera camera, GraphicsDevice device, SpriteBatch spriteBatch, Texture2D Texture)
{
Viewport viewport = new Viewport(new Rectangle(0, 0, 800, 480));
Vector3 viewSpaceTextPosition = viewport.Project(this.position, camera.Projection, camera.View, camera.World);
spriteBatch.Begin();
spriteBatch.Draw(Texture, new Vector2(viewSpaceTextPosition.X, viewSpaceTextPosition.Y), null, Color.White, 0, new Vector2(Texture.Bounds.Center.X, Texture.Bounds.Center.Y), this.Scale, SpriteEffects.None, viewSpaceTextPosition.Z);
spriteBatch.End();
device.RasterizerState = RasterizerState.CullCounterClockwise;
device.BlendState = BlendState.Opaque;
device.DepthStencilState = DepthStencilState.Default;
}
So is my use of Viewport wrong or do I just need to use it's information differently in spriteBatch.Draw()?
I think this should do the trick using viewport project... taking two projection points and calculating its distance you get a value affected by depth... so if it's deeper that value will be smaller.
public void Draw(Camera camera, GraphicsDevice device, SpriteBatch spriteBatch, Texture2D Texture)
{
Vector3 pos1= device.Viewport.Project(
this.position,
camera.Projection, camera.View, camera.World);
Vector3 pos2= device.Viewport.Project(
this.position+ Vactor3.UnitY*10,
camera.Projection, camera.View, camera.World);
Vector2 pos = new Vector2(pos1.X, pos1.Y);
Vector2 origin = new Vector2(Texture.Bounds.Center.X, Texture.Bounds.Center.Y);
float Scale = Vector3.Distance(pos1, pos2) * CustomRatio;
spriteBatch.Begin();
spriteBatch.Draw(Texture, pos, null, Color.White, 0,
origin, Scale, SpriteEffects.None, 0);
spriteBatch.End();
device.RasterizerState = RasterizerState.CullCounterClockwise;
device.BlendState = BlendState.Opaque;
device.DepthStencilState = DepthStencilState.Default;
}
In other hand... your previous code seems to be extracted from a source that drinks from this article made by the guy behind Xna that explain how to use basiceffect to draw billboards in 3D with spritebatch...
http://blogs.msdn.com/b/shawnhar/archive/2011/01/12/spritebatch-billboards-in-a-3d-world.aspx
I hope it helps you
Figured it out, you have to enable textures on the effect and then set effect.Texture to the Texture2D you want to use just before calling spriteBatch.Begin() in the draw function
There is a similar question System.Drawing in XNA, but this may be a clearer question and thus one easier to answer.
We're trying to draw lines on the screen in C#. We are using the XNA library. This code
void DrawLine2 (Vector2 point1, Vector2 point2)
{
System.Drawing.Pen pen = new System.Drawing.Pen(System.Drawing.Color.Green, 1);
Point p1 = new Point((int)point1.X, (int)point1.Y), p2 = new Point((int)point2.X, (int) point2.Y);
Graphics.DrawLine (pen, p1, p2);
}
gives the compile-time error that Graphics does not exist.
Perhaps I should be using something in XNA to draw the line rather than in System -- but if so I am not sure what. XNA has a Spritebatch drawing function, but AFAIK you give it a sprite and a center (and a rotation), rather than 2 points.
Try this handy extensions method,
public static void DrawLine(this SpriteBatch spriteBatch, Vector2 begin, Vector2 end, Color color, int width = 1)
{
Rectangle r = new Rectangle((int)begin.X, (int)begin.Y, (int)(end - begin).Length()+width, width);
Vector2 v = Vector2.Normalize(begin - end);
float angle = (float)Math.Acos(Vector2.Dot(v, -Vector2.UnitX));
if (begin.Y > end.Y) angle = MathHelper.TwoPi - angle;
spriteBatch.Draw(1X1 PIXEL TEXTURE, r, null, color, angle, Vector2.Zero, SpriteEffects.None, 0);
}
Spritebatch can be used to draw a line, as Beanish commented you can simply make a one pixel sprite and extend it between two points.
This is a great library for drawing 2D primitives in XNA that uses the technique for drawing lines, as well as other objects like arcs. I use it extensively:
http://sourceforge.net/projects/primitives2d/
Vector2 drawPos = (-screenPosition);
drawPos.X *= (float)device.DisplayMode.Width;
drawPos.Y *= (float)device.DisplayMode.Height;
spriteBatch.Draw(
texture,
drawPos,
getRectangle(),
Color.White,
rotation,
getOrigin(),
1.0f / zoom,
SpriteEffects.None,
0);
I have a drawPos essentialy being 0..1 and multiply that with the display width and height. screenposition is obtained by dragging the screen. With other elements, primitives, the position is correct and is exactly being dragged along with the input. However when drawing a sprite the sprite is moving to quickly, faster than the input, giving a sort of parallax effect, not what I want.
I somehow get the feeling I am using the parameters wrong, and spriteBatch.Draw(..) does not need pixelcoordinates..
Width and height is obtained by texture loader.
public Vector2 getOrigin()
{
return new Vector2(width / 2, height / 2);
}
public Rectangle getRectangle()
{
return new Rectangle(
0,
0,
width,
height);
}
Also, I am developing for Windows Phone.
The getRectangle() method is basically useless, you are specifying a source rectangle which is the same size as the texture - use null instead (unless ofcourse you've just simplified your code for us).
How is screenPosition defined? I don't understand why you'd times it by -1. You are also using the screen that the window is on to get width/height with device.DisplayMode which is something you should avoid because this will only work when your window is the same size as the screen.
Try drawing the sprite to Vector2.Zero without an origin, and with the sourceRectangle set to null. Slowly add back in your other parameters and see where the error is occuring. Can't really say much else without more information!
spriteBatch.Draw(
texture,
Vector2.Zero,
null,
Color.White,
0,
Vector2.Zero,
1,
SpriteEffects.None,
1);
This is what I would use to make sure the sprite is actually displaying properly.