BadgerHack: Gaming Add-On Kit
Contributors:
Shawn Hymel
The Code
Plug the USB side of your BadgerStick into your computer. Make sure "BadgerStick" and the associated COM port are selected in the Arduino IDE, and click upload.
/** * BadgerHack Breakout * Shawn Hymel @ SparkFun Electronics * September 23, 2015 * * A clone of the famous "Breakout" game on the BadgerHack platform. Use the * joystick to move the paddle and try to knock out the upper pixels with the * ball. * * License: http://opensource.org/licenses/MIT * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ // Constants // Pin definitions // Global variables byte led_pins[] = {2, 3, 4, 5, 6, 7, 8, 9}; // Pins for LEDs uint16_t horz_zero; uint16_t vert_zero; uint8_t paddle_size; // Setup void setup() { Serial.begin(9600); Serial.println(F("Breakout demo for BadgerHack")); // Initialize and clear display Plex.init(led_pins); Plex.clear(); Plex.display(); // Seed our random number generator randomSeed(analogRead(2)); // Calibrate our joystick by finding the center horz_zero = analogRead(X_PIN); vert_zero = analogRead(Y_PIN); } // Loop - play the game forever void loop() { // Play the game inifinity times playGame(); } /**************************************************************** * Functions ***************************************************************/ // Play the game void playGame() { // Calculate the new x max based on paddle size paddle_size = PADDLE_SIZE; uint16_t field_max = MAX_X_SPAN - (paddle_size - 1) * (MAX_X_SPAN / 7); // Create new game variables boolean playing; boolean win; unsigned long frame_start; uint8_t millis_per_frame = 1000/FPS; unsigned long game_start; boolean ball_moving; int16_t paddle_move; int16_t paddle_field = field_max / 2; uint8_t paddle_x; float ball_x = 3; float ball_y = 5; float inc_x; float inc_y; float ball_speed; uint8_t ball_round_x; uint8_t ball_round_y; uint16_t ball_theta; boolean can_deflect = true; int i; uint8_t x; uint8_t y; byte the_wall[] = { 1,1,1,1,1,1,1, 1,1,1,1,1,1,1, 1,1,1,1,1,1,1, 1,1,1,1,1,1,1, 0,0,0,0,0,0,0, 0,0,0,0,0,0,0, 0,0,0,0,0,0,0, 0,0,0,0,0,0,0 }; Serial.println("New game!"); Serial.print("Field span: "); Serial.println(field_max); // Note when we start the game (so we can shoot the ball) game_start = millis(); ball_moving = false; // Assign an initial direction and speed to the ball ball_theta = initBallTheta(); ball_speed = INITIAL_BALL_SPEED; // Play the game until we win or lose playing = true; while ( playing ) { // For each frame, aim for refresh rate frame_start = millis(); // Check for a win condition win = true; for ( i = 0; i < FIELD_SIZE; i++ ) { if ( the_wall[i] > 0 ) { win = false; break; } } if ( win ) { Plex.clear(); Plex.scrollText("You win!", 1); delay(5000); Plex.stopScrolling(); playing = false; } // Read the value of the joystick and map to a movement paddle_move = analogRead(X_PIN) - horz_zero; paddle_move = paddle_move / SENSITIVITY; Serial.print("Moving: "); Serial.println(paddle_move); // Move the paddle and calculate its real x position paddle_field = paddle_field + paddle_move; if ( paddle_field <= 0 ) { paddle_field = 0; } if ( paddle_field >= field_max ) { paddle_field = field_max; } paddle_x = map(paddle_field, 0, field_max, 0, 6 - (paddle_size - 1)); // If the ball has been shot, move it if ( ball_moving ) { // Calculate the ball's new position ball_x += ball_speed * cos(ball_theta * (M_PI / 180)); ball_y += ball_speed * sin(ball_theta * (M_PI / 180)); // Check the ball against the paddle if ( (ball_y > 6) && (ball_x >= paddle_x) && (ball_x <= (paddle_x + (paddle_size - 1))) && can_deflect ) { ball_y = 6 - abs(6 - ball_y); ball_theta = 360 - ball_theta; can_deflect = false; } // Allow ball to be deflected once it leaves paddle range if ( ball_y <= 6 ) { can_deflect = true; } // Check if the ball moved past the paddle (lose) if ( ball_y > 7 ) { Serial.print("LOSE! x="); Serial.print(ball_x); Serial.print(" y="); Serial.print(ball_y); Serial.print(" Paddle:"); Serial.print(paddle_x); Serial.print("-"); Serial.println(paddle_x + paddle_size - 1); playing = false; } // Check the ball against the walls (and bounce!) if ( ball_y < 0 ) { ball_y = abs(ball_y); ball_theta = 360 - ball_theta; } if ( ball_x < 0 ) { ball_x = abs(ball_x); ball_theta = (540 - ball_theta) % 360; } if ( ball_x > 6 ) { ball_x = 6 - abs(6 - ball_x); ball_theta = (540 - ball_theta) % 360; } // Bounce if we hit a block above the ball i = (floor(ball_y) * ROW_SIZE) + roundFloat(ball_x); if ( the_wall[i] > 0 ) { the_wall[i]--; ball_y = (i / ROW_SIZE) + abs((i / ROW_SIZE) - ball_y); ball_theta = 360 - ball_theta; ball_speed += BALL_SPEED_INC; } // Bounce if we hit a block below the ball i = (ceil(ball_y) * ROW_SIZE) + roundFloat(ball_x); if ( the_wall[i] > 0 ) { the_wall[i]--; ball_y = (i / ROW_SIZE) - abs((i / ROW_SIZE) - ball_y); ball_theta = 360 - ball_theta; ball_speed += BALL_SPEED_INC; } // Bounce if we hit a block to the left the ball i = (roundFloat(ball_y) * ROW_SIZE) + floor(ball_x); if ( the_wall[i] > 0 ) { the_wall[i]--; ball_y = (i / ROW_SIZE) + abs((i / ROW_SIZE) - ball_y); ball_theta = (540 - ball_theta) % 360; ball_speed += BALL_SPEED_INC; } // Bounce if we hit a block to the right the ball i = (roundFloat(ball_y) * ROW_SIZE) + ceil(ball_x); if ( the_wall[i] > 0 ) { the_wall[i]--; ball_y = (i / ROW_SIZE) - abs((i / ROW_SIZE) - ball_y); ball_theta = (540 - ball_theta) % 360; ball_speed += BALL_SPEED_INC; } } else { // See if we need to start moving the ball if ( millis() >= game_start + PAUSE_BEFORE_SHOOT ) { ball_moving = true; } } // Round the ball's position to the nearest pixel ball_round_x = roundFloat(ball_x); ball_round_y = roundFloat(ball_y); // Draw tbe wall, the paddle, and the ball Plex.clear(); for ( y = 0; y < COL_SIZE; y++ ) { for ( x = 0; x < ROW_SIZE; x++ ) { if ( the_wall[(x * ROW_SIZE) + (ROW_SIZE - 1 - y)] > 0 ) { Plex.pixel(x, y); } } } for ( i = 0; i < paddle_size; i++ ) { Plex.pixel(7, map(paddle_x + i, 0, 6, 6, 0)); } Plex.pixel(ball_round_y, map(ball_round_x, 0, 6, 6, 0)); Plex.display(); // Wait until we reach our target end of frame while ( millis() < frame_start + millis_per_frame ) { delay(1); } Serial.print("FPS: "); Serial.println( 1000 / (millis() - frame_start) ); } } // Create a randomized ball launch angle unsigned int initBallTheta() { unsigned int theta; // Choose an angle in the range of 210-239 deg or 301-330 deg theta = random(0, 60); Serial.print("RNG:"); Serial.print(theta); if ( theta < 30 ) { theta = 210 + theta; } else { theta = 271 + theta; } Serial.print(" Theta:"); Serial.println(theta); return theta; } // Rounds a floating value to an integer int roundFloat(float x) { if ( x >= 0 ) { return (int) (x + 0.5); } return (int) (x - 0.5); }