Thursday, October 15, 2015

Arduino Pong

While on summer vacation I ordered an Arduino kit when my oldest nephew expressed some interest in programming and electronics. We worked our way up to wiring the 8x8 LED matrix display. He had a fun time plugging wires into the solderless breadboard. Since he’s a gamer, I wrote up a single-player pong game to show him the basics of video game programming. A demo is on YouTube and the sketch is available on GitHub. Read on for a description of the software.



The sketch’s loop() contains three sections. The first updates the ball's direction based on its position. For example, if it hits the left wall, the horizontal direction is reversed. When the ball hits the paddle, the vertical direction is reversed upwards. To add some chance to the game (and make it nontrivial), the horizontal direction is randomized when the ball strikes the paddle. On a second variation, nephew suggested the ball direction depend on where it hits the paddle. In that version, the ball goes up if it hits the middle and left and right at 45 degree angles when it hits either end. Finally, if the ball misses the paddle, it’s position is reset to above the play field with a random direction so it enters the screen in a surprise location.

The second section in loop() updates the ball’s position based on the direction settings. To make it simple, the ball has one speed and can only move in 8 directions (up, up & right, right, down & right, and so on), The ball and paddle geometry are also matched to the 8x8 display, i.e. they are constrained from 0 to 7 in x and y coordinates. After the ball position is updated, the paddle position is read from analog pin 0. Arduino C’s handy map() function is used to determine the location of the paddle along the bottom row. Because the paddle is 3 pixels wide, the middle pixel position can vary from 1 to 6.  

The third part of loop() updates the bitmap and then pushes the bitmap to the display. The bitmap is an array of 8 unsigned ints inherited from the demo code for the 8x8 LED dot-matrix. In hindsight, it should strictly be bytes to fully realize the 64-bit video buffer. Fortunately, the shiftOut() function outputs the lower byte of an int. But first, the ball and paddle are inserted into the bit map. The video memory is arranged with each byte representing a column on the display. To draw the paddle, a for-loop iterates through each column of the memory and zeroes out the display memory. At the loop iteration where the paddle is located, the bit 0’s of the three columns for the paddle are set to 1. To draw the ball, a 1 is XOR’d into the horizontal position of the ball’s column byte. The XOR operator is used so the ball will light up an LED when it’s in the air and will blank out the LED if it hits the paddle. (Interestingly, the use of XOR on CRTs for this purpose was patented.) You can see this effect in the video -- it adds a little visual interest and increases the play area by an entire row (which is 12.5% of the available area).

Finally, the video memory is pushed to the LED matrix using shiftOut(). The functional purpose of this code is equivalent to the video card on a modern machine, but of course is much simpler. The LED matrix is controlled by scanning through the columns and selecting the rows to be lit. The code loops through each column of the display and uses two shiftOut() calls to select the column and the push the rows of that column. The shiftOut() sends the data serially to a pair of 8-bit serial to parallel converters, which latch the outputs to drive the LED array. Each column is held on for 1 millisecond and the display loop is repeated 10 times, for an elapsed time of about 80 milliseconds per frame or 12.5 frames per second. Then it starts all over again.

We made a second version of the game that uses a rotary encoder to control the paddle instead of the potentiometer. That's interesting because the encoder provides relative motion rather than absolute position and doesn’t have physical stops to limit the turning. By using modulo arithmetic, the paddle can then wrap around the display. A third variation adds a row of dots to the top of the display and becomes a breakout style game.

No comments:

Post a Comment