I worked under the assumption that a block will only collide with another block or the edges of the screen if its not hidden, and I used blocks because after the Tetris shape hits the bottom of the screen, or another block, the Tetris(composed by four blocks) explodes(like in AutoCAD explode feature) and every block contained in the Tetris will be saved in an ArrayList that gets constantly iterated in search for a completed row. This was my way of implementing it, using what I've learned so far about OOP.
The main class, that just adds the frame and runs the program.
The Frame class, that adds the panel and sets the size of the window and the playing area.
The Block class. Since a Tetris shape contains four blocks, I created this class, attempting a bottoms-up design. Only thing tricky about this class are the virtual future block methods, in which I return a block with new coordinates in the direction I want to move it(left, right or down), in order to intersect the virtual block with any other block before they overlap each other.
The TetrisShape class, which is the abstract class from which all other shapes will be created. This class works a lot like the Block class, only 10 times more difficult, because it works with a two dimension array of blocks. The same methods as the Block class just iterate the array and call the Block methods with the same name, like, hide(), draw(), move(), etc.. The intersect method iterates all the active blocks in the TetrisShape and checks if any of them intersect with another Block (not another TetrisShape, because the Tetris will be transformed into blocks after the player no longer controls them). I spend a lot of time in this class, because of a shallow copy problem with the virtual future Tetris shapes, I tried to used the cloneable interface but failed, so I tried something easier: iterate the Tetris, looking for all the blocks that are active, and save them into an ArrayList, then, I can change the coordinates of the blocks, moving them in any direction.. Created methods that receive an ArrayList to work around the shallow copy issue. Also, for the rotation of the Tetris, I rotate it, then I save the active blocks in a new ArrayList, and return the original Tetris to its previous rotation. I then use the copy of the future rotation of the Tetris to check for collisions. All Tetris share the same methods, except the ones that display and rotate the shape, so I made those abstract.
The TetrisShapeI class, which was the first I implemented and also the hardest, since its the largest one. Since I'm working with a 4x4 array even when the shape is only 1x4 or 4x1, I had to spend a little time on the method to rotate the shape, in order to prevent the shape from rotating and going out of bounds or colliding with another block.
The TetrisShapeT class
The TetrisShapeO class
The TetrisShapeL class
The TetrisShapeJ class
The TetrisShapeS class
The TetrisShapeZ class
The panel class, where the game loop is and all the methods and variables to make it work.
I invested about 30 hours to get this working, because I got stuck getting the collisions to work properly and the method to delete a line and move all the blocks above.. after the first shape was working, implementing the other 6 took me like 20 minutes. And they all worked well, in fact I could expand the game and add any 4x4 or smaller shape and it will work. If I were to improve this program, I would change the draw method in the Block class and choose a better looking block, maybe add an image and use that instead. Also, the game has no sound, does native Java handle sounds? either way, its out of my league at the moment lol.
Also, the game has no sound, does native Java handle sounds?
Here is a simple example that demonstrates how to play a .wav file:
1. TetrisIcon - custom painting of an Icon to represent a single block. It is designed to look like the Tetris block found on the Tetris game in Windows 7.
2. TetrisPiece - contains a 4x4 array indication which cell of the array should be painted. It also support a generic rotation algorithm to rotate any piece
3. TetrisBoard - contains the list of individual TetrisIcons to be painted. Also contains the current TestisPiece that can be rotated, moved left/right or dropped. Is also responsible for removed a row from the board when full
4. Tetris - create the game and add initial TetrisPiece to the TetrisBoard
This forum won' t allow me to post my code here since I use "r" as a variable in some of my looping code and the forums wants me to "speak English".
If you want to take a look at my code you can find it here: http://stackoverflow.com/questions/42517544/adding-additional-shapes-in-a-tetris-project-loop-logic-assistance/42518798#42518798
You are right, your implementation is a lot like mine.. You did a better job at drawing the block though, mine is pretty basic. Also, good idea with the rotation method, I hard coded my rotations, your implementation is far better, It didn't even cross my mind, switching rows for columns..
In your implementation, when a row is completed, it gets deleted, how did you solve the problem of moving all the Tetris icons above that row? does it do it? move all blocks above?
I see this, yet is not that clear as to what this is doing, it re adds all the columns in the board, only they are displaced one position down? you should try to comment your code more, at least the tricky parts.
You should try to comment your code more, at least the tricky parts.
Yes, I just wrote the code for fun for myself so I wasn't to worried about documentation.
when a row is completed, it gets deleted, how did you solve the problem of moving all the Tetris icons above that row?
The first variable of the TetrisBoard class is the "board" variable which a List. Each item in the List represents a row of TetrisPieces.
When you delete a "row" from a List the items below that row move up one position in the List. (ie. see the for loop above the code you pasted)
So the code you quoted then shows how I add an empty row back to the top of the List so the board always contains the specified number of rows.
So the net effect is the rows below the deleted rows move up and then all the rows move down after the empty rows have been added. Of course repaint() is only invoked once so you only see all the rows in the proper place.
Its great how there can be so many different paths to program something.
Which is one reason why I spend time answering questions. Always learning new/different ways to do things.
but I don't see how, the timer allows me to pause the game, but since the game loop is already inside the ActionListener, I don't think its possible, right?
If you have a reference to the Timer variable then you should be able to pause it. Somewhere in the game you must speed up the Timer when you move to a new level. So you should also be able to pause it and display fancy graphics before restarting.