Rotate Tetris Shapes

Started by
11 comments, last by redneon 17 years, 6 months ago
I'm trying to get into Microsoft's XNA Game Studio Express thing so I thought I'd write a Tetris game to get into using C# and GSE. I've run into a little problem though which I'm hoping someone can help me with. I have a 2D array of Block objects which was created like this:

Block[,] gameGrid;
gameGrid = new Block[10, 18];

for (int x = 0; x < gameGrid.GetLength(0); x++)
{
    for (int y = 0; y < gameGrid.GetLength(1); y++)
    {
        gameGrid[x, y] = new Block(false, Color.Red, posX + (blockTexture.Width * x), posY - (blockTexture.Height * y), blockTexture.Width, blockTexture.Height, x, y);
    }
}



The first parameter of the Block constructor sets the blocks to be disabled so that they don't show up. Below is a screenshot of the grid with every block enabled to give you an idea of what it looks like: What I'm wanting to do is rotate the blocks but I'm not sure how to do it. For example, take the following scene: The green block is the centre block which the others will be rotated around. The desired effect is the following: What I need to do is calculate which blocks need to be disabled and which blocks need to be enabled and aside from having lots and lots of if statements, I can't see how I'm going to do it. I think the calculation for each block needs to be relative to the block to be rotated around. For the above shape, each block is adjacent to the centre block. But what about the following: Which needs to end up looking like this: Sorry for all of the images, but you know what they say? A picture's worth a thousand words =o) Can anyone help?
Advertisement
Each tetris shape is a series of blocks that have a position relative to each other.

So the most convenient way to represent a tetris shape is as a set of coordinates (representing the blocks) and a coordinate origin. For example, the 'triangle' shape might be the following blocks:

(0, 0), (0, 1), (-1, 0), (1, 0)

Now you just have to rotate it around the adequate block (in this case, (0,0)).

This is just multiplying each coordinate in the shape by a 90 degree rotation matrix, and you're set!

To make things simpler, choose your coordinate system so that the rotation is always performed around (0,0).

Eg. for a right rotation multiply each coordinate in the shape with the matrix:

0 -1
1 0
Because I'm using a 2D array, I do have a coordinate system for the blocks. For example, gameGrid[3, 2] is the fourth block in on the third line up.

I'm going to have to refresh my memory on matrix multiplication though as I've no idea how to go about implementing what you suggested =o)
I've used this rotation-around-central-brick method as well, but it then resulted in a kind of weird semantics. For instance, imagine rotating the square block ('O' block) -- in classic Tetris it doesn't rotate when you press the rotate key, however with this design, you have to choose a 'central' brick and the block then rotates around one of its corners.

In the end I went for a more hackish and simpler method -- I just defined a list of all possible shapes for every block (expressed in relative coordinates to the central brick) and when the rotation key is pressed, the next shape is chosen. This allows me to set only one shape for the 'O' block, resulting in no rotation happenning when the user presses the rotation key.
Quote:Because I'm using a 2D array, I do have a coordinate system for the blocks. For example, gameGrid[3, 2] is the fourth block in on the third line up.
That's the coordinates of the blocks in the tetris game board. It gives you no information as to how a block should be rotated.

You need another coordinate system for each shape, so each block in a shape knows where it stands in relation to the other blocks in a shape. This is necessary because blocks rotate in a different way depending on which shape they're in.

Quote:I've used this rotation-around-center-brick method as well, but it then resulted in a kind of weird semantics. For instance, imagine rotating the square block ('O' block) -- in classic Tetris it doesn't rotate when you press the rotate key, however with this design, you have to choose a 'central' brick and the block then rotates around one of its corners.
With all due respect, the weird semantics are entirely your doing.

If the square block shouldn't rotate, erm, don't rotate it?

You don't have to choose a 'central' brick or anything of the sort. You just have to NOT DO ANYTHING in response to the rotate command if the shape is a square. How hard is doing nothing at all? :)
Well, I'm bored so I wrote it up in python.

Disabuse yourself of the notion that things with the word 'matrix' in them are hard. Here's all the code we need to display AND rotate any shape around the (0,0) coordinate (shapes are just lists of coordinates. I think it's pretty self-explanatory; this rotates things to the left, rotating them right just means reversing the signs in rotate_pt_left:

def display(shape):    for y in range(-3,3):        print '\n',        for x in range(-3,3):            if (x, y) in shape: print '#',            else: print ' ',def rotate_pt_left(point):    return (point[1], -point[0])def rotate_left(shape):    return [rotate_pt_left(point) for point in shape]


Now, let's make a function that shows us the four rotations for a given shape. Also, let's define two test shapes, the triangle and the z shape:

triangle = [ (0,0), (-1,0), (1,0), (0,1) ]z_shape = [ (0,0), (1, 0), (0, 1), (-1, 1) ]def show_rotations(shape):    current = shape    for i in range(4):        display(current)        current = rotate_left(current)


And now, let's display them!

>>> show_rotations(triangle)                                        # # #         #                                               #           # #         #                                               #         # # #                                                         #         # #           #                >>> show_rotations(z_shape)                                          # #       # #                                               #           # #           #                                             # #       # #                                                         #           # #           #                >>> 


Easy. Don't make it harder than it has to be.

So you keep track of shape positions instead of individual blocks in your tetris world, and just add the shape's position to each block to get the block's position in the tetris world. Rotation just depends on the shape, so it needs no knowledge of the tetris world (the converse is not true: a rotation might be illegal, but the rotation function needn't know about it).
Quote:Original post by Anonymous Poster
Quote:I've used this rotation-around-center-brick method as well, but it then resulted in a kind of weird semantics. For instance, imagine rotating the square block ('O' block) -- in classic Tetris it doesn't rotate when you press the rotate key, however with this design, you have to choose a 'central' brick and the block then rotates around one of its corners.
With all due respect, the weird semantics are entirely your doing.

If the square block shouldn't rotate, erm, don't rotate it?

You don't have to choose a 'central' brick or anything of the sort. You just have to NOT DO ANYTHING in response to the rotate command if the shape is a square. How hard is doing nothing at all? :)


Yes, I could've hardcoded some switch block that would just look what kind of block it is and perform the appropriate action, only I thought that'd be an ugly solution and tried to make a generic block rotation method. [smile]
Am I right in thinking that that code only works in a 9 (3x3) block area? In which case, if I changed the -3 to -4 and 3 to 4 would it work for the long shape?
Quote:Am I right in thinking that that code only works in a 9 (3x3) block area? In which case, if I changed the -3 to -4 and 3 to 4 would it work for the long shape?
No and yes. :)

It only DISPLAYS the points in the range x in (-3, 2) and y in (-3, 2). That's just my crummy display function, which isn't important. Make it bigger if you want, as you said. But you'll be using a different, non-ascii display function anyway.

The actual rotation doesn't depend on magic numbers; try the point (-234, 1000) and see for yourself. It works for any coordinate (ergo, shapes can have coordinates within any range).
When I wrote tetris using XNA, I decided that all blocks would be represented by 2D arrays, which may or may not be completely filled.

Thus, the 'L' block became

#@###@###@@#####


Which gives us a neat 4x4 square which is very easy to rotate. For collision detection, just only consider the 'filled' areas of the array. In the original tetris, am I right in assuming this piece rotates around the mid point of this 4x4, as opposed to any one square in the piece itself? I'm pretty sure it works that way.

Similarly, the 'square' shape can be represented by something like

#####@@##@@#####


or even

@@@@


and so doesn't move when you rotate it. You can take a similar appraoch with all the shapes. They don't have to be all the same size array, or even to have the same width as they do height. Just play around with the size until you're happy with the rotation behaviour. No need to code exceptions for any of the shapes that way.

This topic is closed to new replies.

Advertisement