As far as I know, rotation in all the classic block games such as Tetris or Blockout was done in about the same way-- by storing an array of values for each of a block's possible states. For example, a long block in Tetris would have been stored something like the following. In its default rotation, it's horizontal with the following coordinates: [0,1], [1,1], [2,1],[3,1]. In after rotating clockwise, it's vertical like this: [1,0], [1,1], [2,1], [3,1]. The other two rotations would just be mirrors of the first two, so the entire block data could be stored in a multi-dimensional array like this:
rotation = 0;
L = [
[[0,1],[1,1],[2,1],[3,1]],
[[1,0],[1,1],[2,1],[3,1]],
[[0,2],[1,2],[2,2],[3,2]],
[[2,0],[2,1],[2,2],[2,3]]
];
Then the variable L[rotation] would return the coordinates of L in whatever rotation it happened to be in (in this case [0,1], [1,1], [2,1],[3,1]). Rotating blocks is very quick this way, but the downside is that rotational info has to be manually inputted for each block.
Since the block game I'm making isn't just a tetris clone, and I want to have a shop in in where players can buy or maybe even design their own blocks, I've decided not to use this method. Instead of entering everything in by hand, I'll use math and let the computer do the work of coming up with the transformations for each block. 2D rotations are a simple 2x2 matrix multiplication, and 90 degree rotations are even easier than that. This way, each time a piece is rotated, the locations of its constituent tiles will be updated by a matrix multiplications.
Problems
1) Matrix multiplication is very slow compared to an array lookup. I can live with this. It's not 1985 and there will only be one block being rotated at a time anyway.
2) Repeatedly rotating a piece can make it drift until the coordinates of its squares are hugely positive or negative and further rotations just make it orbit around a point instead of just rotating in place. This was really annoying. I fixed it by making the 2,2 location the "center" of each piece, subtracting [2,2] from each tile's coordinates before doing rotation multiplications and then adding [2,2] back to them after the rotations. This fixed the problem with a couple of caveats. Pieces have to be created such that rotating around their 2,2 point looks reasonable, and...
3) Some pieces should just stay in place when they rotate. A 2x2 square is a perfect example. Yes, I could just have it rotate around whichever of its four tiles happened to be at location 2,2. It would look stupid, though. The center of the visible part of the piece isn't on any of its constituent tiles, it's between them. I solved this problem by allowing a property to the block class to state that rotations should do nothing. I'm not 100% satisfied with this, but off the top of my head I can't think of any pieces that must transform when rotating and yet would look bad rotating around one of their constituent tiles.
Here's my rotation function for my block class. Note that shape
is an array in the block class that holds the locations of a block's tiles:
function rotate():Array {
var xoff:int = 2; // every block will rotate around 2,2
var yoff:int = 2;
if (rotatable == false) return shape; //rotation doesn't change this shape
var newshape:Array = new Array;
for (var coord in shape) { //use 90 degree matrix rotation [0 -1]
// [1 0]
newshape[coord] = new Array;
newshape[coord][0] = (shape[coord][1] - xoff) * -1 + xoff;
newshape[coord][1] = shape[coord][0];
}
return newshape;
}