C# MonoGame: Moving Sprite Image Up and Down Using Xbox 360 GamePad - c#

Here is the code from my MonoGame project where I have one player image in the bottom left corner and another player image in the top right. The goal is to use the left thumbstick on an Xbox 360 controller to move the player 1 on the left up and down. I used a break point and found that my controller is connected, but nothing happens when I use the thumbstick. Here is my code:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace _2PersonShooterGame_v0._1
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
// GamePad support
const float THUMB_STICK_DEFELCTION = 100;
//Window Resolution support
const int WINDOW_WIDTH = 1200;
const int WINDOW_HEIGHT = 800;
// player images and their rectabgles support
Texture2D player1;
Rectangle drawRect1;
Texture2D player2;
Rectangle drawRect2;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Change resolution to 1200, 800
graphics.PreferredBackBufferWidth = WINDOW_WIDTH;
graphics.PreferredBackBufferHeight = WINDOW_HEIGHT;
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// Load sprites
player1 = Content.Load<Texture2D>(#"graphics\sprite_player1");
player2 = Content.Load<Texture2D>(#"graphics\sprite_player2");
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// game-specific content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
// Move player1 up and down using the thumbstick on an Xbox 360 remote
GamePadState gamepad = GamePad.GetState(PlayerIndex.One);
if (gamepad.IsConnected)
{
drawRect1.Y -= (int)(gamepad.ThumbSticks.Left.Y * THUMB_STICK_DEFELCTION);
}
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// Drawing sprites in their respective rectangles
spriteBatch.Begin();
// Place player one in the bottom left corner and player two in the top right corner
drawRect1 = new Rectangle(0, WINDOW_HEIGHT - player1.Height, player1.Width, player1.Height);
drawRect2 = new Rectangle(WINDOW_WIDTH - player2.Width, 0, player2.Width, player2.Height);
spriteBatch.Draw(player1, drawRect1, Color.White);
spriteBatch.Draw(player2, drawRect2, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
}

The problem is that you're setting drawRect1.Y property in the Update(...) method but then again you're overriding this rectangle in your Draw(...) method.
// update
if (gamepad.IsConnected)
{
drawRect1.Y -= (int)(gamepad.ThumbSticks.Left.Y * THUMB_STICK_DEFELCTION);
}
// draw
drawRect1 = new Rectangle(0, WINDOW_HEIGHT - player1.Height, player1.Width, player1.Height);
What you should do i to just update rectangle in your Update(...) method and use the same ( updated ) value in Draw(...) call.
To fix this just remove this line : drawRect1 = new Rectangle(0, WINDOW_HEIGHT - player1.Height, player1.Width, player1.Height); from your Draw(...) method.

Related

C#, showing a variable on screen

The game that i am making using C# is Square Chase. You mouse over the block and click it before it moves. The timer will count down in the back ground and the score will be raised by 1 each "tag" on the block. You can do the Project on XNA 4.0 Game Development by Example. This is 2010 C# and The project type is Windows Game (4.0).
Now I want to add extra stuff to it. I want take away 5 seconds of time every time they click on the block, but I don't know if its working so I need to make the time remaining visible. I want to make it smaller every time you hit the square.
Code: (under update and the if statement I have already done the subtraction on the time but would want to make sure its working by making it visible) Thank You
I need to find out how to sink and in large an image, I need to know how to show a float on the screen.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace RFSquareChase
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Random rand = new Random ();
Texture2D squareTexture;
Rectangle currentSquare;
int playerScore = 0;
float timeRemaining = 0.0f;
const float TimePerSquare = 0.75f;
Color [] colors = new Color[3] { Color.Red, Color.Green, Color.Blue };
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
this.IsMouseVisible = true;
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
squareTexture = Content.Load<Texture2D>(#"SQUARE");
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
if (timeRemaining == 0.0f)
{
currentSquare = new Rectangle(
rand.Next(0, this.Window.ClientBounds.Width - 25),
rand.Next(0, this.Window.ClientBounds.Height - 25),
25, 25);
timeRemaining = TimePerSquare;
}
MouseState mouse = Mouse.GetState();
if ((mouse.LeftButton == ButtonState.Pressed) &&
(currentSquare.Contains(mouse.X, mouse.Y)))
{
playerScore++;
timeRemaining = 0.0f;
timeRemaining = timeRemaining - 5;
}
timeRemaining = MathHelper.Max(0, timeRemaining - (float)gameTime.ElapsedGameTime.TotalSeconds);
this.Window.Title = "Score : " + playerScore.ToString();
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Gray);
spriteBatch.Begin();
spriteBatch.Draw(
squareTexture,
currentSquare,
colors[playerScore % 3]);
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}
To show a float on screen you simply use spriteBatch.DrawString, and could be useful Math.Round to show only the decimal you want. As you can see here.
To change the dimension of a sprite you can set the scale parameter in your spriteBatch.Draw. As you can see here

Code required for a walk cycle? XNA Visual Studio C#

So I just started learning C# code a few months ago and I want to get a sprite in a full walk cycle.
I've got my walk cycle on a PNG image. Which is obviously a series of separate images showing the different stages of a walk cycle. Now I've got that into my Project/Solution I'm doing it as a Windows Game 4.0 in visual studio 2010.
And I'm fairly far on, so I've started trying to do srcRect and destRect set up. But all i've managed to get on screen is the image flicking left to right very fast and then infinitely going right. I achieved this by using the srcRect.X += srcRect.width line.
But what I want to do is have this move much slower. I want the destRect to move right on the screen when the thumbstick is pushed right and as this is happening I want it to flick right through my walk cycle.
I'm getting very muddled and I'm a beginner so any help with this would be great appreciated! Below is my code so far!
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace fra_walk
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
GamePadState pad1, oldpad1;
Rectangle srcRect;
struct Rect
{
public Rectangle destRect;
public Rectangle frame;
public Point velocity;
}
Rect destRect;
struct Wright
{
public Texture2D txr;
public Rectangle frame;
}
Wright walkright;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 600;
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
destRect.velocity = new Point(0, 0);
destRect.frame = new Rectangle(400, 400, 83, 170);
srcRect = new Rectangle(0, 0, 64, 170);
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
walkright.txr = Content.Load<Texture2D>("walk right");
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
GamePadState pad1 = GamePad.GetState(PlayerIndex.One);
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
srcRect.X += srcRect.Width;
// if ((pad1.ThumbSticks.Left.X == 1.0f)
// && (oldpad1.ThumbSticks.Left.X == 1.0f))
// {
// srcRect.X += destRect.frame.Width;
// }
oldpad1 = pad1;
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(walkright.txr, destRect.frame, srcRect, Color.White);
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}
An easy way to do what you're wanting would probably be to implement a counter.
Add something like public double animCounter = 0 to the top of your code.
Then around the srcRect.X += srcRect.Width; line add something to the effect of:
if (animCounter > 0)
{
animCounter -= gameTime.ElapsedGameTime.TotalSeconds;
}
else
{
srcRect.X += srcRect.Width;
animCounter = 0.5f;
}
Again, this is the easy way to do what you're wanting to do, but not necessarily the best. For one, += srcRect.Width will quickly exceed the width of your sprite sheet, so you'd need to check for that and correct it. As itsme86 suggested, you should search Google for some tutorials.
This one isn't a bad start:
http://msdn.microsoft.com/en-us/library/bb203866.aspx

Game lagging (beginner)

I am creating a game in which asteroids spawn and move down the screen. In the update method of the game, i am using a random number to spawn the asteroids sporatically. When i start it up, it begins to lag within the first 5 seconds. I can see this because the score counter(which goes up every tick) starts going in intervals of 30. Also, the images of the asteroid do not even show up.
Here is the gameObject class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace thedodger
{
public abstract class gameObject
{
public static Texture2D texture;
public Rectangle rectangle;
public abstract void Draw(SpriteBatch spriteBatch);
public abstract void Update(GameTime gameTime);
}
}
here is the asteroid class;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace thedodger
{
public class Asteroid : gameObject
{
Random rand = new Random(1);
int yPos = -10;
public override void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Begin();
spriteBatch.Draw(texture, new Rectangle(rand.Next(32,400), yPos,32,32),Color.White);
spriteBatch.End();
}
public override void Update(GameTime gameTime)
{
yPos--;
}
}
}
and here is the game1 class:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace thedodger
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
int scorevalue = 0;
SpriteFont font;
player player1 = new player();
List<gameObject> objectList = new List<gameObject>();
Random rand = new Random(1);
Asteroid asteroid = new Asteroid();
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>("font");
//player1.image = Content.Load<Texture2D>("EnemyShip005.png");
gameObject.texture = Content.Load<Texture2D>("asteroid");
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
scorevalue++;
if (rand.Next(0, 8) == 2)
{
for (int i = 0; i < 30; i++)
{
objectList.Add(asteroid);
}
}
foreach (Asteroid asteroid in objectList)
{
asteroid.Update(gameTime);
asteroid.Draw(spriteBatch);
}
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
spriteBatch.DrawString(font, "Score: " + scorevalue, new Vector2(5, 5), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
All help would be greatly appreciated.
sorry about the code. I am having difficult setting it up. Help on that too please.
As Tobias said, you probably should have put this on the Game Dev site and you appear to only have one Asteroid instantiated. In the Game1 object, you declare and instantiate an Asteroid object. Then in the Update method you add it repeatedly to the objectList. To get what I think you want, you should change
Asteroid asteroid = new Asteroid();
to
Asteroid asteroid;
Then change
for (int i = 0; i < 30; i++)
{
objectList.Add(asteroid);
}
to
for (int i = 0; i < 30; i++)
{
asteroid = new Asteroid();
objectList.Add(asteroid);
}
In your original code you declare and instantiate asteroid as a specific Asteroid but then never change it. So, throughout the whole program asteroid is pointed to one specific instance of an Asteroid. Then you repeatedly add asteroid to the objectList. So after each frame, 30 new references to the same asteroid object are being added to the objectList and the Update() and Draw() methods are being called on asteroid for every reference to it in objectList. So after 30 frames, one second if running at 30FPS, up to 900 references to the same exact asteroid object are in objectList and on that 30th frame asteroid is having its Update() and Draw() methods called up to 900 times. I'm pretty sure this is the source of your lag. Doing the corrections given above will result in objectList being populated with up to 900 different Asteroids, but will certainly experience lag as well. What you need to do as well is add a limit to the amount of asteroids on screen at any given time (the length of objectList can only be x amount) and/or lower the amount of asteroids created each time. I would suggest something like
for (int i = 0; i < 5; i++)
{
if (objectList.Count < 50) // Maximum asteroids on screen
{
asteroid = new Asteroid();
objectList.Add(asteroid);
}
}
will result in only five new asteroids for time and a maximum of 50 at any given time. If you add in functionality to destroy asteroids though, you'll have to remember to remove them from the objectList.
EDIT- As Neomex has said, your drawing is also a problem. I would suggest checking out the GDC 2008 talk on the XNA Framework performance here: http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=6082
It very briefly covers some drawing and calculation performances and what you should/shouldn't do.
EDIT2- Actually, because of your random probability, it would be up to 900 object references and calls, not a full 900.
Try to make as least begin-end calls as possible, they often slow down game a lot.
( Asteroid class )
You are displaying graphics in your update function which is really bad thing.
If these fail, try to download newest drivers.
Reasuming:
Change in your asteroid class
public override void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Begin();
spriteBatch.Draw(texture, new Rectangle(rand.Next(32,400), yPos,32,32),Color.White);
spriteBatch.End();
}
to
public override void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, new Rectangle(rand.Next(32,400), yPos,32,32),Color.White);
}
Now in your update function
foreach (Asteroid asteroid in objectList)
{
asteroid.Update(gameTime);
asteroid.Draw(spriteBatch);
}
to
foreach (Asteroid asteroid in objectList)
{
asteroid.Update(gameTime);
}
And add this in draw function:
foreach (Asteroid asteroid in objectList)
{
asteroid.Draw(spriteBatch);
}
As Tobias has already pointed out, you are using the same asteriod object every time you want to spawn a new asteriod. You need to do add a new Asteriod() to the object list instead of using the same one.
Also, this code is mostly likely the reason you are suffering performance wise.
if (rand.Next(0, 8) == 2)
{
for (int i = 0; i < 30; i++)
{
objectList.Add(asteroid);
}
}
You are adding 30 instances of the same object to the collection on every update call (with is around 60 times a second). I realize you are using a random number, but it sill happens to about 12.5% of the update calls.
So within a 5-10 seconds, you now have thousands of these objects and are drawing the same image thousands and thousands of times.

Lining up sprites with scale and rotation around arbitrary point

I have 2 sprites which when drawn together make up the correct image on the screen. Drawing them both at the same time is not an option.
Imagine this class:
class MyImage
{
Vector2 drawOffset; // this gets added before the image is drawn
Vector2 sourceRect; // this is where it is on the source texturepage
void Draw(Vector2 position)
{
position = position + drawOffset;
spriteBatch.Draw(sourceTexture, position, sourceRect, Color.White);
}
}
And this code calling into it:
MyImage a = new MyImage(); // assume they get initialised correctly
MyImage b = new MyImage(); // with different drawOffsets and sourceRects
a.Draw(position); // this composes the final
b.Draw(position); // screen image from the 2 source images
Now I'd like to add scale and rotation to the Draw() function, but am having real trouble getting the parameters to the SpriteBatch.Draw function correct. This would be the version which takes scale, rotation and an origin. I need the final composed image to scale and rotate correctly (around some arbitrary centre) but can't for the life of me work out how to manipulate the scale, rotation and origin parameters to make the 2 images appear to scale and rotate in concert. Has anyone done something like this before? Happy to mod the question based on feedback if anything's unclear. If images would help I can get them posted somewhere...
I've looked at rotation around point xna 2D but am still stumped.
Cheers,
Charlie.
Thanks so much for the answer below - using it I've managed to get the images rendering correctly. One other issue remains, which is that I seem to need to use a lot of spritebatch.Begin/End pairs (one per image render). I don't have a way to measure performance on this device yet and the framerate's not chugging so I guess it's not a problem.
Here's my code:
// gr is the graphic object:
// gr.position is the location of the image in the atlas
// gr.DrawOffset is the draw offset so the image is placed correctly in it's virtual box
// gr.pageIndex is the index into the texture/atlas array
// hw,hh are half the width/height of the image (it always rotates around it's centre in fact)
Matrix m = Matrix.CreateTranslation(-hw, -hh, 0) *
Matrix.CreateRotationZ(rotation) * // rotation : parameter
Matrix.CreateScale(scale) * // scale : parameter
Matrix.CreateTranslation(pos.X + hw, pos.Y + hh, 0); // pos : parameter!
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, m);
spriteBatch.Draw(page[gr.pageIndex].texture, gr.DrawOffset, gr.position, color);
spriteBatch.End();
If you are going to work with the SpriteBatch.Draw to draw the textures I would suggest that you forgo trying to manipulate the origin, scale arguments to try an achieve this, simply I doubt it can be done this way. But you do have an alternative, you can manipulate the SpriteBatch Matrix.
Here is a quick and dirty example, note that the texture I used here is 128x96 so I hard coded the values for that image size. Do not look for any best practices in this code, I wrote it to try and show the concept as cleanly as possible.
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace WindowsGame1
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
private Texture2D _texture;
private MyImage _image1;
private MyImage _image2;
// Attributes of the composed sprite
private float _angle = 0.0f;
private Vector2 _position = new Vector2(100, 100);
private Vector2 _rotationPoint = new Vector2(96, 48);
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
_texture = Content.Load<Texture2D>("Gravitar");
// Create the two MyImage instances
_image1 = new MyImage(_texture, Vector2.Zero, Vector2.Zero);
_image2 = new MyImage(_texture, new Vector2(64, 0), new Vector2(64, 0));
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
_angle += 0.5f * elapsedTime;
if (Mouse.GetState().LeftButton == ButtonState.Pressed)
{
_angle = 0.0f;
}
if (Keyboard.GetState().IsKeyDown(Keys.Left))
_position += new Vector2(-10, 0)*elapsedTime;
if (Keyboard.GetState().IsKeyDown(Keys.Right))
_position += new Vector2(10, 0) * elapsedTime;
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// Setup the sprite batch matrix
// Notice that we first translate to the point or rotation
// then rotate and when we translate to the desired position we
// need to compensate for the first translation so that the texture
// appears at the correct location
Matrix m =
Matrix.CreateScale(1.5f)
* Matrix.CreateTranslation(-_rotationPoint.X, -_rotationPoint.Y, 0)
* Matrix.CreateRotationZ(_angle)
* Matrix.CreateTranslation(_position.X + _rotationPoint.X, _position.Y + _rotationPoint.Y, 0);
// Begin the SpriteBatch passing the matrix
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, m);
_image1.Draw(spriteBatch);
_image2.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
class MyImage
{
Vector2 _drawOffset;
Vector2 _sourcePoint;
Texture2D _sourceTexture;
public MyImage(Texture2D sourceTexture, Vector2 sourcePoint, Vector2 drawOffset)
{
_drawOffset = drawOffset;
_sourcePoint = sourcePoint;
_sourceTexture = sourceTexture;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(_sourceTexture, _drawOffset,
new Rectangle((int)_sourcePoint.X, (int)_sourcePoint.Y, 64, 96),
Color.White);
}
}
}
}

After Writing to a RenderTarget, How to Efficiently Clone the Output?

XNA noob here, learning every day. I just worked out how to composite multiple textures into one using a RenderTarget2D. However, while I can use the RenderTarget2D as a Texture2D for most purposes, there's a critical difference: these rendered textures are lost when the backbuffer is resized (and no doubt under other circumstances, like the graphics device running low on memory).
For the moment, I'm just copying the finished RenderTarget2D into a new non-volatile Texture2D object. My code to do so is pretty fugly, though. Is there a more graceful way to do this? Maybe I'm just tired but I can't find the answer on Google or SO.
Slightly simplified:
public static Texture2D MergeTextures(int width, int height, IEnumerable<Tuple<Texture2D, Color>> textures)
{
RenderTarget2D buffer = new RenderTarget2D(_device, width, height);
_device.SetRenderTarget(buffer);
_device.Clear(Color.Transparent);
SpriteBatch spriteBatch = new SpriteBatch(_device);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);
// Paint each texture over the one before, in the appropriate color
Rectangle rectangle = new Rectangle(0, 0, width, height);
foreach (Tuple<Texture2D, Color> texture in textures)
spriteBatch.Draw(texture.Item1, rectangle, texture.Item2);
spriteBatch.End();
_device.SetRenderTarget((RenderTarget2D)null);
// Write the merged texture to a Texture2D, so we don't lose it when resizing the back buffer
// This is POWERFUL ugly code, and probably terribly, terribly slow
Texture2D mergedTexture = new Texture2D(_device, width, height);
Color[] content = new Color[width * height];
buffer.GetData<Color>(content);
mergedTexture.SetData<Color>(content);
return mergedTexture;
}
I suppose I should check for IsContentLost and re-render as needed, but this happens in the middle of my main drawing loop, and of course you can't nest SpriteBatches. I could maintain a "render TODO" list, handle those after the main SpriteBatch ends, and then they'd be available for the next frame. Is that the preferred strategy?
This code is only called a few times, so performance isn't a concern, but I'd like to learn how to do things right.
Actually your code is not so bad if you're generating textures in a once-off process when you'd normally load content (game start, level change, room change, etc). You're transferring textures between CPU and GPU, same thing you'd be doing loading plain ol' textures. It's simple and it works!
If you're generating your textures more frequently, and it starts to become a per-frame cost, rather than a load-time cost, then you will want to worry about its performance and perhaps keeping them as render targets.
You shouldn't get ContentLost in the middle of drawing, so you can safely just respond to that event and recreate the render targets then. Or you can check for IsContentLost on each of them, ideally at the start of your frame before you render anything else. Either way everything should be checked before your SpriteBatch begins.
(Normally when using render targets you're regenerating them each frame anyway, so you don't need to check them in that case.)
Replace
Texture2D mergedTexture = new Texture2D(_device, width, height);
Color[] content = new Color[width * height];
buffer.GetData<Color>(content);
mergedTexture.SetData<Color>(content);
return mergedTexture;
with
return buffer;
Because RenderTarget2D extends Texture2D you will just get Texture2D class data returned. Also in case you are interested here's a class i made for building my GUI library's widgets out of multiple textures. In case you need to be doing this sort of thing a lot.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;
namespace Voodo.Utils {
/// <summary>
///
/// </summary>
public class TextureBaker {
private readonly SpriteBatch _batch;
private readonly RenderTarget2D _renderTarget;
private readonly GraphicsDevice _graphicsDevice;
/// <summary>
///
/// </summary>
public Rectangle Bounds {
get { return _renderTarget.Bounds; }
}
/// <summary>
///
/// </summary>
/// <param name="graphicsDevice"></param>
/// <param name="size"></param>
public TextureBaker(GraphicsDevice graphicsDevice, Vector2 size) {
_graphicsDevice = graphicsDevice;
_batch = new SpriteBatch(_graphicsDevice);
_renderTarget = new RenderTarget2D(
_graphicsDevice,
(int)size.X,
(int)size.Y);
_graphicsDevice.SetRenderTarget(_renderTarget);
_graphicsDevice.Clear(Color.Transparent);
_batch.Begin(
SpriteSortMode.Immediate,
BlendState.AlphaBlend,
SamplerState.LinearClamp,
DepthStencilState.Default,
RasterizerState.CullNone);
}
#region Texture2D baking
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
public void BakeTexture(Texture2D texture) {
_batch.Draw(
texture,
new Rectangle(0, 0, Bounds.Width, Bounds.Height),
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="destination"></param>
public void BakeTexture(Texture2D texture, Rectangle destination) {
_batch.Draw(
texture,
destination,
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="destination"></param>
/// <param name="source"></param>
public void BakeTexture(Texture2D texture, Rectangle destination, Rectangle source) {
_batch.Draw(
texture,
destination,
source,
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="sourceModification"></param>
/// <param name="destination"></param>
public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination) {
Stream sourceBuffer = new MemoryStream();
texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);
System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);
sourceBuffer = new MemoryStream();
sourceImage.RotateFlip(sourceModification);
sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);
_batch.Draw(
Texture2D.FromStream(_graphicsDevice, sourceBuffer),
destination,
Color.White);
}
/// <summary>
///
/// </summary>
/// <param name="texture"></param>
/// <param name="sourceModification"></param>
/// <param name="destination"></param>
/// <param name="source"></param>
public void BakeTexture(Texture2D texture, System.Drawing.RotateFlipType sourceModification, Rectangle destination, Rectangle source) {
Stream sourceBuffer = new MemoryStream();
texture.SaveAsPng(sourceBuffer, texture.Width, texture.Height);
System.Drawing.Image sourceImage = System.Drawing.Image.FromStream(sourceBuffer);
sourceBuffer = new MemoryStream();
sourceImage.RotateFlip(sourceModification);
sourceImage.Save(sourceBuffer, System.Drawing.Imaging.ImageFormat.Png);
_batch.Draw(
Texture2D.FromStream(_graphicsDevice, sourceBuffer),
destination,
source,
Color.White);
}
#endregion
#region SpriteFont baking
/// <summary>
///
/// </summary>
/// <param name="font"></param>
/// <param name="text"></param>
/// <param name="location"></param>
/// <param name="textColor"></param>
public void BakeText(SpriteFont font, string text, Vector2 location, Color textColor) {
_batch.DrawString(font, text, location, textColor);
}
/// <summary>
///
/// </summary>
/// <param name="font"></param>
/// <param name="text"></param>
/// <param name="location"></param>
public void BakeTextCentered(SpriteFont font, string text, Vector2 location, Color textColor) {
var shifted = new Vector2 {
X = location.X - font.MeasureString(text).X / 2,
Y = location.Y - font.MeasureString(text).Y / 2
};
_batch.DrawString(font, text, shifted, textColor);
}
#endregion
/// <summary>
///
/// </summary>
/// <returns></returns>
public Texture2D GetTexture() {
_batch.End();
_graphicsDevice.SetRenderTarget(null);
return _renderTarget;
}
}
}
if you are having problems with your rendertarget being dynamically resized when drawing it somewhere else, you could just have an off-screen rendertarget with a set size that you copy your finished RT to like this:
Rendertarget2D offscreenRT = new RenderTarget2D(_device, width, height);
_device.SetRenderTarget(offscreenRT);
_device.Clear(Color.Transparent);
SpriteBatch spriteBatch = new SpriteBatch(_device);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied);
spriteBatch.Draw(buffer, Vector2.Zero, Color.White);
spriteBatch.End();
_device.SetRenderTarget(null);

Categories