micro:drop - A simple game for the BBC micro:bit Fruits falling from the sky. Use buttons A & B to control the player and catch them all. Drop speed increases every 10 catches. How much can you get? After game over press AB to restart.

Dependencies:   microbit

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*
00002 MIT License
00003 
00004 Copyright (c) 2018 Alexander Dölz
00005 
00006 Permission is hereby granted, free of charge, to any person obtaining a copy
00007 of this software and associated documentation files (the "Software"), to deal
00008 in the Software without restriction, including without limitation the rights
00009 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00010 copies of the Software, and to permit persons to whom the Software is
00011 furnished to do so, subject to the following conditions:
00012 
00013 The above copyright notice and this permission notice shall be included in all
00014 copies or substantial portions of the Software.
00015 
00016 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00017 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00018 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00019 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00020 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00021 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00022 SOFTWARE.
00023 */
00024 
00025 /*
00026    micro:drop - A simple game for the BBC micro:bit
00027 
00028    Fruits falling from the sky. Use buttons A & B to control the player
00029    and catch them all.
00030 
00031    Drop speed increases every 10 catches. How much can you get?
00032    
00033    After game over press AB to restart.
00034 */
00035 #include "MicroBit.h"
00036 
00037 struct Sprite {
00038     int     x;
00039     int     y;
00040 };
00041 
00042 const MicroBitImage imageCross("255,0,0,0,255\n0,255,0,255,0\n0,0,255,0,0\n0,255,0,255,0\n255,0,0,0,255\n");
00043 const MicroBitImage imageFlash("255,255,255,255,255\n255,255,255,255,255\n255,255,255,255,255\n255,255,255,255,255\n255,255,255,255,255\n");
00044 
00045 MicroBit uBit;
00046 Sprite player;
00047 Sprite fruit;
00048 
00049 int game_over;
00050 int paused;
00051 int life;
00052 int score;
00053 int level;
00054 int game_speed;
00055 int high_score;
00056 int new_high_score;
00057 
00058 /* Move player sprite to the left */
00059 void onButtonA(MicroBitEvent e)
00060 {
00061     if (player.x > 0 && !paused && !game_over) {
00062         uBit.display.image.setPixelValue(player.x, player.y, 0);
00063         player.x--;
00064         uBit.display.image.setPixelValue(player.x, player.y, 255);
00065     }
00066 }
00067 
00068 /* Move player sprite to the right */
00069 void onButtonB(MicroBitEvent e)
00070 {
00071     if (player.x < 4 && !paused && !game_over) {
00072         uBit.display.image.setPixelValue(player.x, player.y, 0);
00073         player.x++;
00074         uBit.display.image.setPixelValue(player.x, player.y, 255);
00075     }
00076 }
00077 
00078 /* Move fruit down and check for missed fruits */
00079 void dropFruit()
00080 {
00081     while(!game_over) {
00082         if(!paused) {
00083             uBit.display.image.setPixelValue(fruit.x, fruit.y, 0);
00084             fruit.y++;
00085             // When the fruit is moved below the screen, it's a miss
00086             if (fruit.y > 4) {
00087                 // Create a new fruit
00088                 fruit.x=uBit.random(5);
00089                 fruit.y=0;
00090 
00091                 // Show a cross to indicate the miss
00092                 uBit.display.print(imageCross);
00093                 uBit.sleep(25);
00094                 uBit.display.clear();
00095 
00096                 // Reduce life
00097                 life--;
00098 
00099                 // No more lifes means game over
00100                 if (life == 0) game_over = true;
00101             }
00102             uBit.display.image.setPixelValue(fruit.x, fruit.y, 255);
00103         }
00104         // Wait some time to give the human a chance
00105         uBit.sleep(game_speed);
00106     }
00107 }
00108 
00109 /* Update player sprite and check for catched fruits*/
00110 void updatePlayer()
00111 {
00112     while(!game_over) {
00113         uBit.display.image.setPixelValue(player.x, player.y, 255);
00114         // Check if the player catched the fruit
00115         if (player.x==fruit.x && player.y==fruit.y) {
00116             // Create a new fruit above the screen; dropFruit will do the rest
00117             fruit.x=uBit.random(5);
00118             fruit.y=-1;
00119 
00120             // Flash all LEDs to indicate the catch
00121             uBit.display.print(imageFlash);
00122             uBit.sleep(25);
00123             uBit.display.clear();
00124 
00125             // Score a point
00126             score++;
00127 
00128             // Level and speed will be incressed every 10 points
00129             if (score % 10 == 0) {
00130                 // Pause the game during the "Next Level" message
00131                 paused = true;
00132                 level++;
00133                 if (game_speed > 50) game_speed -= 50;
00134                 uBit.display.clear();
00135                 uBit.display.scroll("LEVEL:");
00136                 uBit.display.scroll(level);
00137                 // Bring Play back to the initial position
00138                 uBit.display.image.setPixelValue(player.x, player.y, 0);
00139                 player.x = 2;
00140                 uBit.display.image.setPixelValue(player.x, player.y, 255);
00141                 // Resume the game
00142                 paused = false;
00143             }
00144         }
00145         // Give the CPU a rest
00146         uBit.sleep(25);
00147     }
00148 }
00149 /* Game over message */
00150 void gameOver()
00151 {
00152     // Concatenate everthiny to ensure it will be in the right order at async
00153     // output
00154     const int cScore = score;
00155     ManagedString msGameOver = ManagedString("GAME OVER! SCORE: ") +  ManagedString(cScore);
00156     if (new_high_score) msGameOver = msGameOver + ManagedString(" NEW HIGH SCORE!");
00157 
00158     uBit.display.scrollAsync(msGameOver);
00159 }
00160 
00161 /* The Game */
00162 void dropGame()
00163 {
00164     // Show current high score
00165     if (high_score > 0) {
00166         uBit.display.scroll("HIGH SCORE:");
00167         uBit.display.scroll(high_score);
00168     }
00169     uBit.display.scroll("LEVEL: 1");
00170     
00171     // Initialize player sprite
00172     player.x=2;
00173     player.y=4;
00174     uBit.display.image.setPixelValue(player.x, player.y, 255);
00175 
00176     // Initialize fruit sprite above the screen; dropFruit will do the rest
00177     fruit.x=uBit.random(5);
00178     fruit.y=-1;
00179 
00180     // Reset game state
00181     paused = false;
00182     game_speed = 400;
00183     level = 1;
00184     score = 0;
00185     life = 3;
00186     new_high_score = false;
00187     game_over = false;
00188 
00189     //Create background tasks for player and fruit update
00190     create_fiber(updatePlayer);
00191     create_fiber(dropFruit);
00192 
00193     // Game logik is in fibers updatePlayer and dropFruit
00194     // So, while playing do nothing
00195     while(!game_over)
00196         uBit.sleep(200);
00197 
00198     // Check for new high score
00199     if (score > high_score) {
00200         high_score = score;
00201         // Maker it persistent
00202         uBit.storage.put("high_score", (uint8_t *)&high_score, sizeof(int));
00203         new_high_score = true;
00204     }
00205 
00206     // Show "Game Over" screen and score until A&B ist pressed together
00207     uBit.display.clear();
00208 
00209     while(!uBit.buttonAB.isPressed()) {
00210         gameOver();
00211         uBit.sleep(200);
00212     }
00213     uBit.display.stopAnimation();
00214 }
00215 
00216 int main(void)
00217 {
00218     // Initialize the micro:bit runtime
00219     uBit.init();
00220 
00221     // Game titel
00222     uBit.display.scroll("MICRO:DROP");
00223 
00224     // Get current high score from storage
00225     KeyValuePair* stroredHighScore = uBit.storage.get("high_score");
00226     if(stroredHighScore  == NULL) {
00227         high_score = 0; // Seems to be the first boot; there is no high score
00228     } else {
00229         // Copy high score from key value pair into int var
00230         memcpy(&high_score, stroredHighScore->value, sizeof(int));
00231         delete stroredHighScore;
00232     }
00233 
00234     //Disable buttons & background tasks at startup
00235     game_over = true;
00236 
00237     //Register functions to buttons A & B
00238     uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_DOWN, onButtonA);
00239     uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_DOWN, onButtonB);
00240 
00241     // Play forever ;-)
00242     while(1) {
00243         dropGame();
00244     }
00245 }