Old topic but I'd like to talk a bit about the subject. About grouping sprites together there are two nice ways to handle that, depending on whether you want all the sprites to be siblings (i.e. grouped but on the same level) or if you want to have an hierarchy (i.e. sprites inside other sprites, where transforming the parent also transforms the children). Here's the general idea for each of them.
Scenario 1) All the sprites in the group are siblings:
Let's say you have a Sprite class and a Group class with this interface (pseudocode)...
Sprite: Position, Rotation, Scale, Origin, Draw()
Group: Sprites, Move(displacement), Rotate(angle, origin), Scale(amount, origin)
Each Sprite object will hold its Position/Rotation/Scale/Origin in world space and when calling Draw those values are fed to a SpriteBatch instance and drawn.
As for the Group class, it keeps a list of all the Sprites in the group and provides methods to manipulate the group as a whole. Those methods take an origin parameter which is the "center" of the group (e.g. around which point do you want it to rotate). The implementation goes something like:
Move(displacement): For every sprite, Add displacement to Position
Rotate(angle, origin): For every Sprite, Add angle to Rotation & apply Matrix to Position
Scale(amount, origin): For every sprite, Add amount to Scale & apply Matrix to Position
Where that Matrix is calculated with the origin of the transformation taken into account, i.e.:
Matrix = CreateTranslation(-origin) *
CreateRotation(angle) OR CreateScale(amount) *
CreateTranslation(origin)
What this does is basically move the Sprite temporarily into a new space where the origin is at (0,0), then it applies the transformation, and reverts the original displacement back to normal. So as long as you manipulate the Sprites through the Group interface, they'll be bound together.
Note: To apply the matrix to Position you use the Vector2.Transform() method of XNA.
Scenario 2) Sprite hierarchy
On the other hand if you can have sprites inside each other, the strategy is different. Basically you need to create a hierarchy of Parent and Children sprites, and each sprite will keep a transformation matrix that is not defined in World Space but rather in relation to its parent.
So you can create something like:
class Sprite {
List<Sprite> children;
Matrix localTransform;
void Draw(Matrix parentTransform);
}
And when drawing the sprite you need to get the global transform by multiplying the current sprite's transform by its parents' (which I'm passing as a parameter), then decompose that transform into values that SpriteBatch can understand, and finally draw the children. Something like (not 100% accurate, just to give an idea):
void Draw(Matrix parentTransform)
{
// Get actual matrix
Matrix globalTransform = localTransform * parentTransform;
// Translate into values
Vector2 position; float rotation; float scale;
DecomposeMatrix(globalTransform, out position, out rotation, out scale);
// Draw sprite
SpriteBatch.Draw(texture, position, rotation, scale);
// Draw children
foreach(Sprite child in children) children.draw(globalTransform);
}
And here's my implementation of DecomposeMatrix (the one I use on my project):
private static void DecomposeMatrix(ref Matrix matrix, out Vector2 position, out float rotation, out Vector2 scale)
{
Vector3 position3, scale3;
Quaternion rotationQ;
matrix.Decompose(out scale3, out rotationQ, out position3);
Vector2 direction = Vector2.Transform(Vector2.UnitX, rotationQ);
rotation = (float) Math.Atan2(direction.Y, direction.X);
position = new Vector2(position3.X, position3.Y);
scale = new Vector2(scale3.X, scale3.Y);
}
Using this strategy you could have the turret be a child of the tank, and whenever you rotated and moved the tank, the turret would follow automatically.