I'm trying to include support for zooming and rotating my camera class, around the centre of the viewport.
In my application, I've already positioned sprites manually before entering the SpriteBatch.Begin code, according to where the camera is positioned (to make culling easier to implement). Although each sprite is positioned manually, I would rather not rotate and scale each sprite individually.
I therefore am trying to use the matrixTransform argument on the SpriteBatch.Begin method.
Below is a hard-coded application I've made to illustrate the problem (using a Car.png content image). The rotation isn't as fast as I'd expect (10 degrees rotation every frame?), and it rotates/zooms about the top left. I would like it to rotate around the centre of the screen, which would always keep the middle car in the centre, and also scale from that point.
I have tried several combinations of creating matrix translations, reordering/adding/multiplying/translating by the halfway distance of viewport, but I don't really understand how matrices work. I've also tried the solutions on several websites which I haven't managed to make work for me.
Can someone tell me the matrix translations I have to create, or point me in the direction of a website you think will work for my setup?
Windows XNA application to demonstrate the problem:
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 RenderTest
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D _carTexture;
float _zoom = 1.0f;
float _rotationInDegrees = 0.0f;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
_carTexture = Content.Load<Texture2D>("Car");
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Up)) // Zoom in key
_zoom *= 1.1f;
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Down)) // Zoom out key
_zoom /= 1.1f;
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Left)) // Rotate anticlockwise key
_rotationInDegrees -= 10;
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right)) // Rotate clockwise key
_rotationInDegrees += 10;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullNone, null, GetMatrix(GraphicsDevice));
spriteBatch.Draw(_carTexture, new Rectangle(0, 0, 50, 50), Color.White);//Square car placed top left
spriteBatch.Draw(_carTexture, new Rectangle(GraphicsDevice.Viewport.Width / 2 - 25, GraphicsDevice.Viewport.Height / 2 - 50, 50, 100), Color.Green);//Car placed centre
spriteBatch.Draw(_carTexture, new Rectangle(GraphicsDevice.Viewport.Width / 2 + 100, GraphicsDevice.Viewport.Height / 2 + 100, 50, 50), Color.Black);//Off centre but always within screen
spriteBatch.End();
base.Draw(gameTime);
}
Matrix GetMatrix(GraphicsDevice graphicsDevice)
{
Matrix translationMatrix = Matrix.CreateTranslation(0, 0, 0);
Matrix rotationMatrix = Matrix.CreateRotationZ(MathHelper.ToRadians(MathHelper.ToRadians(_rotationInDegrees)));
Matrix zoomMatrix = Matrix.CreateScale(_zoom);
Matrix compositeMatrix = translationMatrix * rotationMatrix * zoomMatrix;
return compositeMatrix;
}
}
}
Thanks,
Lee
Solution
Matrix GetMatrix(GraphicsDevice graphicsDevice)
{
Matrix translateToOrigin = Matrix.CreateTranslation(-graphicsDevice.Viewport.Width / 2, -graphicsDevice.Viewport.Height / 2, 0);
Matrix rotationMatrix = Matrix.CreateRotationZ(MathHelper.ToRadians(_rotationInDegrees));
Matrix zoomMatrix = Matrix.CreateScale(_zoom);
Matrix translateBackToPosition = Matrix.CreateTranslation(graphicsDevice.Viewport.Width / 2, graphicsDevice.Viewport.Height / 2, 0);
Matrix compositeMatrix = translateToOrigin * rotationMatrix * zoomMatrix * translateBackToPosition;
return compositeMatrix;
}
SpriteBatch
, and even when it is, you don't usually have to be too accurate about it (you could disregard rotation and simply draw a larger area). (See this answer.) – Andrew Russell