I have the following code in my MonoGame/Farseer Physics project:
_ball = BodyFactory.CreateCircle(World, 1f, 400f);
_ball.BodyType = BodyType.Dynamic;
_ballSprite = new Sprite(ScreenManager.Content.Load<Texture2D>("Common/ball"));
In my Draw method:
ScreenManager.SpriteBatch.Draw(_ballSprite.Texture, ConvertUnits.ToDisplayUnits(_ball.Position), null, Color.White, _ball.Rotation, _ballSprite.Origin, 1f, SpriteEffects.None, 0f);
The problem is that my texture is 120px by 120px, but when it renders on screen, the _ball body is larger than it in size. What can I do to resize the texture to fit exactly the width and height of the _ball body?
I think I have solved this mystery at last. I needed to compute the scale (_ballTextureScale that is, which was the part that I was stuck on):
var radius = 1f;
_ball = BodyFactory.CreateCircle(World, radius, 400f);
_ball.BodyType = BodyType.Dynamic;
var rectangleTexture = ScreenManager.Content.Load<Texture2D>("Common/ball");
_ballTextureScale = ConvertUnits.ToDisplayUnits(radius * 2) / rectangleTexture.Width;
This converts the radius * 2 (total width of the circle) to display units which it seems the rectangle texture is already in. Then when I go to draw the body, I just needed to make use of this _ballTextureScale property:
ScreenManager.SpriteBatch.Draw(_ballSprite.Texture, ConvertUnits.ToDisplayUnits(_ball.Position), null, Color.White, _ball.Rotation, _ballSprite.Origin, _ballTextureScale, SpriteEffects.None, 0f);
If this is an incorrect approach, I am all ears to a better method for accomplishing this task, however it seems to work very well for my needs.
Related
I've been looking around the internet for a while, and I can't seem to find the answer I've been looking for. So what I intend to do with bitmaps is I want to draw them onto the window, and be able properly resize them whenever. Any help would be appreciated!
For those of you who do not know, you can use matrices to translate, resize, and rotate. Here's how to do it. Also be sure to change the Matrix for the RenderTarget before you draw the actual object.
Vector2
RenderTarget.Transform
RenderTarget.DrawBitmap
Matrix.Transformation2D
Vector2 center = new Vector2(bitmap.Size.Width / 2, bitmap.Size.Height / 2);
renderTarget.Transform = Matrix.Transformation2D(center, 0f, new Vector2(width / bitmap.Size.Width, height / bitmap.Size.Height), center, MathHelper.ToRads(rotation), new Vector2(x - center.X, y - center.Y));
renderTarget.DrawBitmap(bitmap, 1f, BitmapInterpolationMode.NearestNeighbor);
renderTarget.Transform = Matrix.Transformation2D(Vector2.Zero, 0f, new Vector2(1f, 1f), Vector2.Zero, MathHelper.ToRads(0), Vector2.Zero);
I have a 2D image of a tanks body (top down) that can be moved left-right across the screen.
On top, there's a second image of the tanks turret. This turret can be rotated across the screen edge following the users mouse movement along the Y axis.
When the user presses'Enter' a bullet appears and moves across the screen at the angle of the turret. However, whilst the angle is fine, the bullet's position seems to vary a lot. At times, it sites where it should (in the centre of the cannon) however, as you move the mouse it seems to get offsetted.
Edit: For some strange reason, my pictures don't seem to be showing up - so here is a direct link: http://img824.imageshack.us/img824/7093/khte.png
Edited Code:
Tank Fire
if (keyBoardState.IsKeyDown(Keys.Enter))
{
shell.Initialize(rotation, new Vector2(location.X + 25, location.Y - 15));
shell.makeAlive();
}
(It is initialized with the location of the tank (+25, -25) so it appears at the end of the turrent. having this set at the tanks location (shell.Initialize(rotation, location);) seems to make no difference to the offset.)
Bullet/Shell:
public void Update(GameTime gameTime)
{
if (alive)
{
movement.X -= speed * (float)Math.Cos(rotation);
movement.Y -= speed * (float)Math.Sin(rotation);
location.X += (int)movement.X;
location.Y += (int)movement.Y;
}
}
public void Draw(SpriteBatch spriteBatch)
{
if (alive)
{
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
spriteBatch.Draw(texture, location, null, Color.White, rotation - MathHelper.PiOver2, new Vector2(texture.Width / 2, texture.Height / 2), 1.0f, SpriteEffects.None, 0f);
spriteBatch.End();
}
}
If you're trying to get your bullet to rotate about your tank, than I personally draw the bullet on top of the tank do the exact same thing that you do after you've fired your bullet(but with a greater magnitude) to displace it,
But if you want to use the origin parameter of SpriteBatch.Draw, than I'd imagine that it would look something like this:
given the following datapoints:
this.origin = new Vector2(texture.Width / 2, texture.Height / 2);
tank.origin = new Vector2(texture.Width / 2, texture.Height / 2);
// and this is what you'd pass into your sprite-batch.draw method
originToDraw = (tank.position + tank.origin)-(this.position + this.origin)
And you also have to initialize your bullet's position to the appropriate starting point Since I don't actually know where rotation comes from, I can't know for sure, but if it's the angle between your tank and the mouse, than I'd imagine it'd be the following
this.startingPosition = tank.position + tank.origin - this.origin
I asume that shell is a Bullet instance if yes your problem is the origin in Bullet class. It should have set this value in LoadTextures method
public void LoadTextures(Texture2D texture)
{
this.texture = texture;
this.origin = new Vector2(texture.Width, texture.Height);
}
And a little extra in Bullet.Draw method:
spriteBatch.Draw(texture, location, null, Color.White, rotation - 1.5f, origin, 1.0f, SpriteEffects.None, 0f);
change 1.5f to MathHelper.PiOver2 it is more accure when you want to rotate something 90 degrees
You have a nasty roundoff problem here.
You compute the velocity in floating point but switch to integers for the bullet's location. The problem is that while ON AVERAGE the errors even out any given bullet is going to either round up or round down every time.
Lets take an extreme case: The bullet is fired 25 degrees off the vertical axis and it's moving at two pixels per update cycle. Strangely enough the bullet flies directly down the axis.
Or an even more extreme case. The speed is 1/3 pixel per cycle. The bullet stands still.
I'm working on an RPG game that has a Top-Down view. I want to load a picture into the background which is what the character is walking on, but so far I haven't figured out how to correctly have the background redraw so that it's "scrolling". Most of the examples I find are auto scrolling.
I want the camera to remained centered at the character until you the background image reaches its boundaries, then the character will move without the image re-drawing in another position.
Your question is a bit unclear, but I think I get the gist of it. Let's look at your requirements.
You have an overhead camera that's looking directly down onto a two-dimensional plane. We can represent this as a simple {x, y} coordinate pair, corresponding to the point on the plane at which the camera is looking.
The camera can track the movement of some object, probably the player, but more generally anything within the game world.
The camera must remain within the finite bounds of the game world.
Which is simple enough to implement. In broad terms, somewhere inside your Update() method you need to carry out steps to fulfill each of those requirements:
if (cameraTarget != null)
{
camera.Position = cameraTarget.Position;
ClampCameraToWorldBounds();
}
In other words: if we have a target object, lock our position to its position; but make sure that we don't go out of bounds.
ClampCameraToBounds() is also simple to implement. Assuming that you have some object, world, which contains a Bounds property that represents the world's extent in pixels:
private void ClampCameraToWorldBounds()
{
var screenWidth = graphicsDevice.PresentationParameters.BackBufferWidth;
var screenHeight = graphicsDevice.PresentationParameters.BackBufferHeight;
var minimumX = (screenWidth / 2);
var minimumY = (screnHeight / 2);
var maximumX = world.Bounds.Width - (screenWidth / 2);
var maximumY = world.Bounds.Height - (screenHeight / 2);
var maximumPos = new Vector2(maximumX, maximumY);
camera.Position = Vector2.Clamp(camera.Position, minimumPos, maximumPos);
}
This makes sure that the camera is never closer than half of a screen to the edge of the world. Why half a screen? Because we've defined the camera's {x, y} as the point that the camera is looking at, which means that it should always be centered on the screen.
This should give you a camera with the behavior that you specified in your question. From here, it's just a matter of implementing your terrain renderer such that your background is drawn relative to the {x, y} coordinate specified by the camera object.
Given an object's position in game-world coordinates, we can translate that position into camera space:
var worldPosition = new Vector2(x, y);
var cameraSpace = camera.Position - world.Postion;
And then from camera space into screen space:
var screenSpaceX = (screenWidth / 2) - cameraSpace.X;
var screenSpaceY = (screenHeight / 2) - cameraSpace.Y;
You can then use an object's screen space coordinates to render it.
Your can represent the position in a simple Vector2 and move it towards any entity.
public Vector2 cameraPosition;
When you load your level, you will need to set the camera position to your player (Or the object it should be at)
You will need a matrix and some other stuff, As seen in the code below. It is explained in the comments. Doing it this way will prevent you from having to add cameraPosition to everything you draw.
//This will move our camera
ScrollCamera(spriteBatch.GraphicsDevice.Viewport);
//We now must get the center of the screen
Vector2 Origin = new Vector2(spriteBatch.GraphicsDevice.Viewport.Width / 2.0f, spriteBatch.GraphicsDevice.Viewport.Height / 2.0f);
//Now the matrix, It will hold the position, and Rotation/Zoom for advanced features
Matrix cameraTransform = Matrix.CreateTranslation(new Vector3(-cameraPosition, 0.0f)) *
Matrix.CreateTranslation(new Vector3(-Origin, 0.0f)) *
Matrix.CreateRotationZ(rot) * //Add Rotation
Matrix.CreateScale(zoom, zoom, 1) * //Add Zoom
Matrix.CreateTranslation(new Vector3(Origin, 0.0f)); //Add Origin
//Now we can start to draw with our camera, using the Matrix overload
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default,
RasterizerState.CullCounterClockwise, null, cameraTransform);
DrawTiles(spriteBatch); //Or whatever method you have for drawing tiles
spriteBatch.End(); //End the camera spritebatch
// After this you can make another spritebatch without a camera to draw UI and things that will not move
I added the zoom and rotation if you want to add anything fancy, Just replace the variables.
That should get you started on it.
However, You will want to make sure the camera is in bounds, and make it follow.
Ill show you how to add smooth scrolling, However if you want simple scrolling see this sample.
private void ScrollCamera(Viewport viewport)
{
//Add to the camera positon, So we can see the origin
cameraPosition.X = cameraPosition.X + (viewport.Width / 2);
cameraPosition.Y = cameraPosition.Y + (viewport.Height / 2);
//Smoothly move the camera towards the player
cameraPosition.X = MathHelper.Lerp(cameraPosition.X , Player.Position.X, 0.1f);
cameraPosition.Y = MathHelper.Lerp(cameraPosition.Y, Player.Position.Y, 0.1f);
//Undo the origin because it will be calculated with the Matrix (I know this isnt the best way but its what I had real quick)
cameraPosition.X = cameraPosition.X -( viewport.Width / 2);
cameraPosition.Y = cameraPosition.Y - (viewport.Height / 2);
//Shake the camera, Use the mouse to scroll or anything like that, add it here (Ex, Earthquakes)
//Round it, So it dosent try to draw in between 2 pixels
cameraPosition.Y= (float)Math.Round(cameraPosition.Y);
cameraPosition.X = (float)Math.Round(cameraPosition.X);
//Clamp it off, So it stops scrolling near the edges
cameraPosition.X = MathHelper.Clamp(cameraPosition.X, 1f, Width * Tile.Width);
cameraPosition.Y = MathHelper.Clamp(cameraPosition.Y, 1f, Height * Tile.Height);
}
Hope this helps!
I want to draw a texture2d 5 times larger than original with no smoothing.
Drawing line:
spriteBatch.Draw(texture, new Vector2(0, 0), null, Color.White, 0, Vector2.Zero,
5f, SpriteEffects.None, 0);
The result is a scaled smoothed image. Adding this.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp; doesn't help.
What you want is Point Texture filtering. Look here.
I am using both primitives and sprites in an XNA project. I draw my primitives using this code (a summary, not verbatim from my project):
transmatrix = Matrix.CreateTranslation(v23(-pos)) * Matrix.CreateScale(scale, -scale, 1f) * Matrix.CreateTranslation(v23(offset));
basicEffect.World = transmatrix;
basicEffect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0, (float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 0, 1.0f, 1000.0f);
basicEffect.Begin();
//draw primitives blah blah
basicEffect.End();
spritebatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None, transmatrix);
//draw sprites blah blah
spritebatch.end()
if I have
transmatrix = Matrix.CreateTranslation(v23(-pos)) * Matrix.CreateScale(scale, scale, 1f) * Matrix.CreateTranslation(v23(offset));
both the primitives and the sprites draw (but y is the inverse of what I want).
if I have
transmatrix = Matrix.CreateTranslation(v23(-pos)) * Matrix.CreateScale(scale, -scale, 1f) * Matrix.CreateTranslation(v23(offset));
The primitives draw correctly, but the sprites don't draw at all. What am I missing? I have tried messing around with lots of different things but nothing seems to work.
It turns it was another problem that was causing the issue. I have to scale all the sprites with negative y to get them to draw correctly as mentioned in a msdn community content "Something that is not obvious when using this form of the method to map SpriteBatch to a desired coordinate system is the effect of negative scaling on the results. If you scale by a single negative number in the X or Y axes (if, for instance, you want to flip the Y-axis to match your game coordinates) the polygon that the sprite gets drawn on gets flipped in the process and gets backface-culled. It is necessary to also scale the texture by a negative in the same direction to get the results you expect."
http://msdn.microsoft.com/en-us/library/ff433701.aspx
SpriteBatch sets up its own "camera" using its own parameters. To scale your camera, do this:
basicEffect.View = Matrix.CreateScale(1.0f, -1.0, 1.0f) * Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
Or you could just create a different matrix for the spritebatch.
Alternatively, you could pass in your BasicEffect to SpriteBatch:
http://msdn.microsoft.com/en-us/library/ff433700.aspx