Gesture-driven Tetris Using Atmega32
Our project takes a classic video game and adds a twist with a handheld, gesture based controller. IT’S SUPER TERRIFIC AMAZING TETRIS EXTREEEEEEEEEEEME!!!!!!!
We decided to undertake this project because the idea of combining the massive coding required for the Tetris game with the elegance of coordinating the processes of a wearable controller made for an impressive final hurrah in ECE 476. Creating the game from the building blocks of video generation that we learned in lab was a challenge. It took a lot of trial and error, as well as taking great care regarding timing, to get the game functioning well. To make our project stand out, we decided to make the controls for the game similar to how you would move the Tetris blocks in the real world, grabbing them, rotating them and so on.
Tetris is a game that was developed in the 80s. It became very popular when it was released on the Nintendo Gameboy. The point of Tetris is to create a solid horizontal line of blocks. When this is done, the line is cleared and all the previous block that were above the line move down by one line. You lose the game if the stack of blocks reaches the tope of the screen. Tetris is made up of 7 different blocks referred to as I,J,L,Z,S,T, and O based on their shape. Each block is made up of 4 smaller blocks arranged in a specific shape. The standard Tetris screen is 10 blocks wide by 20 blocks high.
We didn’t require any serious math to accomplish our goals. The averaging used in the glove to interpret the accelerometer data is a plain averager, adding five samples at a time and dividing by five.
We implemented our Tetris on a standard black and white television using an area of 100 pixels high by 50 pixels wide. Each smaller block is made up of a 5by5 pixel square. Our design is based on the idea that the program does not need to know where all the blocks are. Once they are done moving, they are found again by reading the pixels on the screen. There is no array that stores the location of all the blocks. Each block is made up of 4 smaller blocks that move together. What is tracked is the falling block. The program keeps track of the top left x and y position of each block, the x and y coordinates of the sides and bottom of each block. The sides and bottom coordinates are used to make sure the block does not go through other blocks from the side and to make sure that the falling block stops when it hits another. The program also has methods that define how the block rotates from one position to another. Each object orientation contains its own boundary values.
The timing for the blocks falling is based on the 231 line refresh for the televisions. There is a counter that increments every 231 lines and compares the value to the variable ‘speed’. Blocks only fall down one line when this value is reached. The falling of the blocks can be sped up by decreasing this number.
Before integrating the glove into the game, the game was controlled with buttons for clockwise/counterclockwise rotation, left/right movement, and slam-down operation. This actually lent itself well to integration with the glove because it used flags to make sure values were only sent once which makes it so the glove can wirelessly send one command.
On each button press, before the block moves, the position of where the block will move to is checked. On move left and move right buttons, the program calls the move_left() method and move_right() method which go through the left_side and right_side arrays (lefts and rights stores the number of left side and right side blocks to check) to check if the block is already against the wall using the video_set method. This method checks the screen array to see if there is a pixel at that location. For the two rotate operations, the program calls the check_can_rotate() method to make sure that the block does not rotate into blocks that were already on the screen. If the next position would interfere with something, the block won’t rotate. The slam_down operation takes advantage of the ‘speed’ variable that is used to control the block fall speed. When slam_down is activated, the speed variable is lowered to 1, so that the block falls by 5 pixels every 231 lines. It is then set back to its original value when the block hits another.
Our program randomizes which block it chooses. By randomizing the next block one block earlier, we are able to display the upcoming block on the left side of the screen. Since there is no actual time on the cpu, we use the clock and take the value from when the user presses a button to start the game. This makes it so that the randomizer always seeds with a different value. The hardest part of the program was the line clears. In Tetris, when a player makes a complete line of blocks from one side to the other, the line disappears and all of the blocks above it move down one line. To do the line clear successfully, each line is checked and/or replace in its own cycle. This helps to avoid artifacts but could result in the program needing up to 20 cycles to move all lines down, about 1/3 of a second per line that is cleared. To make things more complicated, it is possible for a player to get multiple line clears in one turn. Our code handles this in the check_line method by first scanning up to 4 lines, one for each different y value of the most recent block to land. This is taken from the left_sidey array. The only possible lines that could have been completed are the lines where the block just landed. This saves us the trouble of having to check every single line. We also then only check from left to right for x positions from 50 to 100 (our screen width) every 5th value. Because the blocks are 5 pixels wide, there is no reason to check every value. After the cleared line is found, the method gets more complicated. The check_line method calls the move_line_down() method. This method starts from where the complete line was found and works its way up the screen copying what is on the line 5 pixels above it (either a square starting there, or no square). By only doing one line each screen refresh, artifacts are avoided and the processor can easily handle the task.
Our glove is built onto a rollerblading wrist guard. This is intentional to limit wrist movement up and down and to give us a sturdy base to mount all of the components onto. Our glove consists of two accelerometers, an x/y accelerometer and a z axis accelerometer. The z axis accelerometer runs off of 5V which we were able to draw right off of the MCU. The x/y accelerometer however, runs off of roughly 3V. We were able to get a consistent 3.15 V for the accelerometer by using 3 diodes and a 1K resistor to drop down the voltage.
The reason that we could not do sensing of left right movement is due to the fact that the acceleration of gravity is so strong that any slight rotation of the accelerometers greatly changes the acceleration during left and right movement. Rotation on the other hand can be accurately measured. We use two buttons to differentiate the diferent motions. When no button is depressed, moving the glove does nothing. This is the simulation of not holding onto the block. Depressing the button on the finger alone allows left and right motion with a twist of the wrist. Depressing both buttons, accompanied with twisting the wrist rotates the block. Holding down just the on-glove button, accompanied with a rapid downward acceleration slams the block down to the bottom of the screen.
The glove takes advantage of averaging, to pass along a single value of rotate left or right, move left or right, or slam down. This works perfect with the Tetris code that was designed to look for a single button press signal.
To make the glove more robust, we gave the user the option of either using it with wireless or a tethered connection. This allows the user to use wireless if they are in a situation where wired would be uncomfortable or annoying or to use wired if they are in an area with large amounts of interference. Our system takes advantage of wireless using 433 Mhz radio. To do wireless, we also decided to use a third Mega32 board. We use this third board as a node to receive the wireless signal and to port it onto the input pin of the board running the Tetris.
The protocol we used for wireless communication was largely borrowed from Meghan Desai’s wireless project page on the course website. His project used the Radiotronix RCT-433-AS transmitter and the Radiotronix RCR-433-RP receiver, which we were able to get from Professor Land. We used Meghan’s code, very slightly modified. The communication tool for this protocol was the chip’s USART communicator. The packet structure that his code called for was a bit longer than was necessary for us, but we felt it was alright to leave it the way it was because we might find need for some of the extra functions at a later time. For instance, we only transmit one byte of data in every transmission, so a byte of length data is not necessary at this time. Also, we were able to transmit a sufficient number of packets per time period without trimming the packet structure.
One minor change to the code we referenced was the BAUD of the transmission. We increased ours from 4000 to 4800. The transmitter and receiver datasheets said that this transmission rate was appropriate, and the more often we transmit, the more accurate our game motions will be.
At first, wireless communication seemed out of reach because of the strict timing restrictions of video production code. Once we realized that we were not going to have budgetary problems, we decided to simply use a third MCU to help shuffle data back and forth. This “middle” board was programmed to receive the serial wireless data and send it to the video MCU via port to port communication. This way the video board could read the port whenever it wanted. Our code was already set up to handle the issue of multiple readings of the same data. We had planned ahead to transmit pulses of changes, as opposed to transmitting the hard data. For instance, we transmit “rotate right once” as opposed to “in the left rotated position”, and “move right one” as opposed to “at the fourth location from the left”.
We started using the wireless as our primary communication tool about a week and a half before the end of the project, and we always experienced a problem with interference. Other students were transmitting at the same frequency in the lab. When the glove was the closest transmitter, we had a small amount of interference, but it was still unpleasant. We decided to add the feature of having both wired and wireless communication. We can easily switch between them with just the change of one cable. It goes either directly to the glove MCU, or to the wireless receiver.
For more detail: Gesture-driven Tetris Using Atmega32
Leave a Comment
You must be logged in to post a comment.