How to: Rotate a Group of Sprites
This article demonstrates how to rotate a group of sprites around a single point using a rotation Matrix. It uses two methods; rotating the sprite positions manually during Update, and creating a rotation matrix during Update that SpriteBatch can use during Draw.
To draw a rotated group of sprites on screen
Follow steps 1–4 of How to: Draw a Sprite.
Create one set of Vector2 objects that represents the unrotated positions of the sprites, and one set to hold the rotated values.
private Vector2[] myVectors;
private Vector2[] drawVectors; protected override void Initialize() { // TODO: Add your initialization logic here. myVectors = new Vector2[9]; drawVectors = new Vector2[9];
base.Initialize();
}
After loading the sprite, calculate the positions of the unrotated group of sprites based on the sprite's size.
private Texture2D SpriteTexture;
private SpriteBatch ForegroundBatch; private Vector2 origin; private Vector2 screenpos; protected override void LoadGraphicsContent( bool loadAllContent ) { if (loadAllContent) { ... // Create unrotated texture locations. float texsize = SpriteTexture.Height; float[] points = { -texsize, 0, texsize }; Vector2 offset = Vector2.Zero;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
offset.X = points[i];
offset.Y = points[j];
myVectors[(i * 3) + j] =
Vector2.Add( screenpos, offset );
}
}
// TODO: Load any ResourceManagementMode.Manual content.
}
In your Update method, copy the unrotated vectors and determine the screen position around which all the sprites will rotate.
private float RotationAngle = 0f;
private bool batchAutoRotate = false; private Matrix rotationMatrix = Matrix.Identity; protected override void Update( GameTime gameTime ) { ... float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
RotationAngle += elapsed;
float circle = MathHelper.Pi * 2;
RotationAngle = RotationAngle % circle;
// Copy and rotate the sprite positions.
drawVectors = (Vector2[])myVectors.Clone();
if (!batchAutoRotate)
RotatePoints( ref screenpos, RotationAngle, ref drawVectors );
else
UpdateMatrix( ref screenpos, RotationAngle);
base.Update( gameTime );
}
Transform each vector using a rotation matrix created for the rotation angle. To rotate around the origin, transform each vector relative to the origin by subtracting the origin vector. Then add the origin vector to the transformed vector to create the final rotated vector.
private static void RotatePoints( ref Vector2 origin, float radians, ref Vector2[] Vectors )
{ Matrix myRotationMatrix = Matrix.CreateRotationZ( radians );
for (int i = 0; i < 9; i++)
{
// Rotate relative to origin.
Vector2 rotatedVector = Vector2.Transform( Vectors[i] - origin, myRotationMatrix );
// Add origin to get final location.
Vectors[i] = rotatedVector + origin;
}
}
Draw each sprite using the rotated vectors as screen locations.
private void DrawPoints()
{ graphics.GraphicsDevice.Clear( Color.CornflowerBlue ); // Draw using manually rotated vectors ForegroundBatch.Begin(); for (int i = 0; i < drawVectors.Length; i++) ForegroundBatch.Draw( SpriteTexture, drawVectors[i], null, Color.White, RotationAngle, origin, 1.0f, SpriteEffects.None, 0f ); ForegroundBatch.End(); }
- When all the sprites have been drawn, call End.
To draw a rotated group of sprites on screen using SpriteBatch transformation
Follow steps 1–4 of How to: Draw a Sprite.
Create one set of Vector2 objects that represents the unrotated positions of the sprites, and one set to hold the rotated values.
private Vector2[] myVectors;
private Vector2[] drawVectors; protected override void Initialize() { // TODO: Add your initialization logic here. myVectors = new Vector2[9]; drawVectors = new Vector2[9];
base.Initialize();
}
After loading the sprite, calculate the positions of the unrotated group of sprites based on the sprite's size.
private Texture2D SpriteTexture;
private SpriteBatch ForegroundBatch; private Vector2 origin; private Vector2 screenpos; protected override void LoadGraphicsContent( bool loadAllContent ) { if (loadAllContent) { ... // Create unrotated texture locations. float texsize = SpriteTexture.Height; float[] points = { -texsize, 0, texsize }; Vector2 offset = Vector2.Zero;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
offset.X = points[i];
offset.Y = points[j];
myVectors[(i * 3) + j] =
Vector2.Add( screenpos, offset );
}
}
// TODO: Load any ResourceManagementMode.Manual content.
}
In your Update method, copy the unrotated vectors and determine the screen position around which all the sprites will rotate.
private float RotationAngle = 0f;
private bool batchAutoRotate = false; private Matrix rotationMatrix = Matrix.Identity; protected override void Update( GameTime gameTime ) { ... float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
RotationAngle += elapsed;
float circle = MathHelper.Pi * 2;
RotationAngle = RotationAngle % circle;
// Copy and rotate the sprite positions.
drawVectors = (Vector2[])myVectors.Clone();
if (!batchAutoRotate)
RotatePoints( ref screenpos, RotationAngle, ref drawVectors );
else
UpdateMatrix( ref screenpos, RotationAngle);
base.Update( gameTime );
}
Create a rotation matrix for SpriteBatch to use during Draw. To rotate around a point that is not the upper-left corner of the screen, you must first subtract the origin of your rotation using a translation matrix, then multiply that translation matrix by the rotation matrix, and multiply the result by a translation matrix adding the origin of your rotation. This is because a rotation matrix must rotate around (0,0,0).
private void UpdateMatrix( ref Vector2 origin, float radians )
{ // Translate sprites to center around screen (0,0), rotate them, and // translate them back to their original positions Vector3 matrixorigin = new Vector3( origin, 0 ); rotationMatrix = Matrix.CreateTranslation( -matrixorigin ) * Matrix.CreateRotationZ( radians ) * Matrix.CreateTranslation( matrixorigin ); }
In your Draw method, call SpriteBatch.Begin, specifying your rotation matrix as the fourth parameter.
private void DrawMatrix()
{ graphics.GraphicsDevice.Clear( Color.Black ); // Draw using a rotation matrix with SpriteBatch ForegroundBatch.Begin( SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.None, rotationMatrix ); for (int j = 0; j < myVectors.Length; j++) ForegroundBatch.Draw( SpriteTexture, myVectors[j], null, Color.White, 0, origin, 1.0f, SpriteEffects.None, 0f ); ForegroundBatch.End(); }
- When all the sprites have been drawn, call End.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
ContentManager content;
public Game1()
{
graphics = new GraphicsDeviceManager( this );
content = new ContentManager( Services );
}
private Vector2[] myVectors;
private Vector2[] drawVectors;
protected override void Initialize()
{
// TODO: Add your initialization logic here.
myVectors = new Vector2[9];
drawVectors = new Vector2[9];
base.Initialize();
}
private Texture2D SpriteTexture;
private SpriteBatch ForegroundBatch;
private Vector2 origin;
private Vector2 screenpos;
protected override void LoadGraphicsContent( bool loadAllContent )
{
if (loadAllContent)
{
// TODO: Load any ResourceManagementMode.Automatic content.
ForegroundBatch = new SpriteBatch( graphics.GraphicsDevice );
SpriteTexture = content.Load<Texture2D>( "vipership" );
origin.X = SpriteTexture.Width / 2;
origin.Y = SpriteTexture.Height / 2;
Viewport viewport = graphics.GraphicsDevice.Viewport;
screenpos.X = viewport.Width / 2;
screenpos.Y = viewport.Height / 2;
// Create unrotated texture locations.
float texsize = SpriteTexture.Height;
float[] points = { -texsize, 0, texsize };
Vector2 offset = Vector2.Zero;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
offset.X = points[i];
offset.Y = points[j];
myVectors[(i * 3) + j] =
Vector2.Add( screenpos, offset );
}
}
// TODO: Load any ResourceManagementMode.Manual content.
}
protected override void UnloadGraphicsContent( bool unloadAllContent )
{
if (unloadAllContent == true)
{
content.Unload();
}
}
private float RotationAngle = 0f;
private bool batchAutoRotate = false;
private Matrix rotationMatrix = Matrix.Identity;
protected override void Update( GameTime gameTime )
{
// Allows the default game to exit on Xbox 360 and Windows.
if (GamePad.GetState( PlayerIndex.One ).Buttons.Back == ButtonState.Pressed)
this.Exit();
if (GamePad.GetState( PlayerIndex.One ).Buttons.A == ButtonState.Pressed)
batchAutoRotate = false;
if (GamePad.GetState( PlayerIndex.One ).Buttons.B == ButtonState.Pressed)
batchAutoRotate = true;
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
RotationAngle += elapsed;
float circle = MathHelper.Pi * 2;
RotationAngle = RotationAngle % circle;
// Copy and rotate the sprite positions.
drawVectors = (Vector2[])myVectors.Clone();
if (!batchAutoRotate)
RotatePoints( ref screenpos, RotationAngle, ref drawVectors );
else
UpdateMatrix( ref screenpos, RotationAngle);
base.Update( gameTime );
}
private void UpdateMatrix( ref Vector2 origin, float radians )
{
// Translate sprites to center around screen (0,0), rotate them, and
// translate them back to their original positions
Vector3 matrixorigin = new Vector3( origin, 0 );
rotationMatrix = Matrix.CreateTranslation( -matrixorigin ) *
Matrix.CreateRotationZ( radians ) *
Matrix.CreateTranslation( matrixorigin );
}
private static void RotatePoints( ref Vector2 origin, float radians, ref Vector2[] Vectors )
{
Matrix myRotationMatrix = Matrix.CreateRotationZ( radians );
for (int i = 0; i < 9; i++)
{
// Rotate relative to origin.
Vector2 rotatedVector = Vector2.Transform( Vectors[i] - origin, myRotationMatrix );
// Add origin to get final location.
Vectors[i] = rotatedVector + origin;
}
}
protected override void Draw( GameTime gameTime )
{
if (!batchAutoRotate)
{
DrawPoints();
}
else
{
DrawMatrix();
}
base.Draw( gameTime );
}
private void DrawMatrix()
{
graphics.GraphicsDevice.Clear( Color.Black );
// Draw using a rotation matrix with SpriteBatch
ForegroundBatch.Begin( SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred,
SaveStateMode.None, rotationMatrix );
for (int j = 0; j < myVectors.Length; j++)
ForegroundBatch.Draw( SpriteTexture, myVectors[j], null, Color.White, 0,
origin, 1.0f, SpriteEffects.None, 0f );
ForegroundBatch.End();
}
private void DrawPoints()
{
graphics.GraphicsDevice.Clear( Color.CornflowerBlue );
// Draw using manually rotated vectors
ForegroundBatch.Begin();
for (int i = 0; i < drawVectors.Length; i++)
ForegroundBatch.Draw( SpriteTexture, drawVectors[i], null, Color.White, RotationAngle,
origin, 1.0f, SpriteEffects.None, 0f );
ForegroundBatch.End();
}
}