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.
main.cpp@1:89b525515734, 2018-01-26 (annotated)
- Committer:
- novecento
- Date:
- Fri Jan 26 23:48:45 2018 +0000
- Revision:
- 1:89b525515734
- Parent:
- 0:53f603890bd2
Disabled BLE stack
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
novecento | 0:53f603890bd2 | 1 | /* |
novecento | 0:53f603890bd2 | 2 | MIT License |
novecento | 0:53f603890bd2 | 3 | |
novecento | 0:53f603890bd2 | 4 | Copyright (c) 2018 Alexander Dölz |
novecento | 0:53f603890bd2 | 5 | |
novecento | 0:53f603890bd2 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy |
novecento | 0:53f603890bd2 | 7 | of this software and associated documentation files (the "Software"), to deal |
novecento | 0:53f603890bd2 | 8 | in the Software without restriction, including without limitation the rights |
novecento | 0:53f603890bd2 | 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
novecento | 0:53f603890bd2 | 10 | copies of the Software, and to permit persons to whom the Software is |
novecento | 0:53f603890bd2 | 11 | furnished to do so, subject to the following conditions: |
novecento | 0:53f603890bd2 | 12 | |
novecento | 0:53f603890bd2 | 13 | The above copyright notice and this permission notice shall be included in all |
novecento | 0:53f603890bd2 | 14 | copies or substantial portions of the Software. |
novecento | 0:53f603890bd2 | 15 | |
novecento | 0:53f603890bd2 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
novecento | 0:53f603890bd2 | 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
novecento | 0:53f603890bd2 | 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
novecento | 0:53f603890bd2 | 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
novecento | 0:53f603890bd2 | 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
novecento | 0:53f603890bd2 | 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
novecento | 0:53f603890bd2 | 22 | SOFTWARE. |
novecento | 0:53f603890bd2 | 23 | */ |
novecento | 0:53f603890bd2 | 24 | |
novecento | 0:53f603890bd2 | 25 | /* |
novecento | 0:53f603890bd2 | 26 | micro:drop - A simple game for the BBC micro:bit |
novecento | 0:53f603890bd2 | 27 | |
novecento | 0:53f603890bd2 | 28 | Fruits falling from the sky. Use buttons A & B to control the player |
novecento | 0:53f603890bd2 | 29 | and catch them all. |
novecento | 0:53f603890bd2 | 30 | |
novecento | 0:53f603890bd2 | 31 | Drop speed increases every 10 catches. How much can you get? |
novecento | 0:53f603890bd2 | 32 | |
novecento | 0:53f603890bd2 | 33 | After game over press AB to restart. |
novecento | 0:53f603890bd2 | 34 | */ |
novecento | 0:53f603890bd2 | 35 | #include "MicroBit.h" |
novecento | 0:53f603890bd2 | 36 | |
novecento | 0:53f603890bd2 | 37 | struct Sprite { |
novecento | 0:53f603890bd2 | 38 | int x; |
novecento | 0:53f603890bd2 | 39 | int y; |
novecento | 0:53f603890bd2 | 40 | }; |
novecento | 0:53f603890bd2 | 41 | |
novecento | 0:53f603890bd2 | 42 | 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"); |
novecento | 0:53f603890bd2 | 43 | 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"); |
novecento | 0:53f603890bd2 | 44 | |
novecento | 0:53f603890bd2 | 45 | MicroBit uBit; |
novecento | 0:53f603890bd2 | 46 | Sprite player; |
novecento | 0:53f603890bd2 | 47 | Sprite fruit; |
novecento | 0:53f603890bd2 | 48 | |
novecento | 0:53f603890bd2 | 49 | int game_over; |
novecento | 0:53f603890bd2 | 50 | int paused; |
novecento | 0:53f603890bd2 | 51 | int life; |
novecento | 0:53f603890bd2 | 52 | int score; |
novecento | 0:53f603890bd2 | 53 | int level; |
novecento | 0:53f603890bd2 | 54 | int game_speed; |
novecento | 0:53f603890bd2 | 55 | int high_score; |
novecento | 0:53f603890bd2 | 56 | int new_high_score; |
novecento | 0:53f603890bd2 | 57 | |
novecento | 0:53f603890bd2 | 58 | /* Move player sprite to the left */ |
novecento | 0:53f603890bd2 | 59 | void onButtonA(MicroBitEvent e) |
novecento | 0:53f603890bd2 | 60 | { |
novecento | 0:53f603890bd2 | 61 | if (player.x > 0 && !paused && !game_over) { |
novecento | 0:53f603890bd2 | 62 | uBit.display.image.setPixelValue(player.x, player.y, 0); |
novecento | 0:53f603890bd2 | 63 | player.x--; |
novecento | 0:53f603890bd2 | 64 | uBit.display.image.setPixelValue(player.x, player.y, 255); |
novecento | 0:53f603890bd2 | 65 | } |
novecento | 0:53f603890bd2 | 66 | } |
novecento | 0:53f603890bd2 | 67 | |
novecento | 0:53f603890bd2 | 68 | /* Move player sprite to the right */ |
novecento | 0:53f603890bd2 | 69 | void onButtonB(MicroBitEvent e) |
novecento | 0:53f603890bd2 | 70 | { |
novecento | 0:53f603890bd2 | 71 | if (player.x < 4 && !paused && !game_over) { |
novecento | 0:53f603890bd2 | 72 | uBit.display.image.setPixelValue(player.x, player.y, 0); |
novecento | 0:53f603890bd2 | 73 | player.x++; |
novecento | 0:53f603890bd2 | 74 | uBit.display.image.setPixelValue(player.x, player.y, 255); |
novecento | 0:53f603890bd2 | 75 | } |
novecento | 0:53f603890bd2 | 76 | } |
novecento | 0:53f603890bd2 | 77 | |
novecento | 0:53f603890bd2 | 78 | /* Move fruit down and check for missed fruits */ |
novecento | 0:53f603890bd2 | 79 | void dropFruit() |
novecento | 0:53f603890bd2 | 80 | { |
novecento | 0:53f603890bd2 | 81 | while(!game_over) { |
novecento | 0:53f603890bd2 | 82 | if(!paused) { |
novecento | 0:53f603890bd2 | 83 | uBit.display.image.setPixelValue(fruit.x, fruit.y, 0); |
novecento | 0:53f603890bd2 | 84 | fruit.y++; |
novecento | 0:53f603890bd2 | 85 | // When the fruit is moved below the screen, it's a miss |
novecento | 0:53f603890bd2 | 86 | if (fruit.y > 4) { |
novecento | 0:53f603890bd2 | 87 | // Create a new fruit |
novecento | 0:53f603890bd2 | 88 | fruit.x=uBit.random(5); |
novecento | 0:53f603890bd2 | 89 | fruit.y=0; |
novecento | 0:53f603890bd2 | 90 | |
novecento | 0:53f603890bd2 | 91 | // Show a cross to indicate the miss |
novecento | 0:53f603890bd2 | 92 | uBit.display.print(imageCross); |
novecento | 0:53f603890bd2 | 93 | uBit.sleep(25); |
novecento | 0:53f603890bd2 | 94 | uBit.display.clear(); |
novecento | 0:53f603890bd2 | 95 | |
novecento | 0:53f603890bd2 | 96 | // Reduce life |
novecento | 0:53f603890bd2 | 97 | life--; |
novecento | 0:53f603890bd2 | 98 | |
novecento | 0:53f603890bd2 | 99 | // No more lifes means game over |
novecento | 0:53f603890bd2 | 100 | if (life == 0) game_over = true; |
novecento | 0:53f603890bd2 | 101 | } |
novecento | 0:53f603890bd2 | 102 | uBit.display.image.setPixelValue(fruit.x, fruit.y, 255); |
novecento | 0:53f603890bd2 | 103 | } |
novecento | 0:53f603890bd2 | 104 | // Wait some time to give the human a chance |
novecento | 0:53f603890bd2 | 105 | uBit.sleep(game_speed); |
novecento | 0:53f603890bd2 | 106 | } |
novecento | 0:53f603890bd2 | 107 | } |
novecento | 0:53f603890bd2 | 108 | |
novecento | 0:53f603890bd2 | 109 | /* Update player sprite and check for catched fruits*/ |
novecento | 0:53f603890bd2 | 110 | void updatePlayer() |
novecento | 0:53f603890bd2 | 111 | { |
novecento | 0:53f603890bd2 | 112 | while(!game_over) { |
novecento | 0:53f603890bd2 | 113 | uBit.display.image.setPixelValue(player.x, player.y, 255); |
novecento | 0:53f603890bd2 | 114 | // Check if the player catched the fruit |
novecento | 0:53f603890bd2 | 115 | if (player.x==fruit.x && player.y==fruit.y) { |
novecento | 0:53f603890bd2 | 116 | // Create a new fruit above the screen; dropFruit will do the rest |
novecento | 0:53f603890bd2 | 117 | fruit.x=uBit.random(5); |
novecento | 0:53f603890bd2 | 118 | fruit.y=-1; |
novecento | 0:53f603890bd2 | 119 | |
novecento | 0:53f603890bd2 | 120 | // Flash all LEDs to indicate the catch |
novecento | 0:53f603890bd2 | 121 | uBit.display.print(imageFlash); |
novecento | 0:53f603890bd2 | 122 | uBit.sleep(25); |
novecento | 0:53f603890bd2 | 123 | uBit.display.clear(); |
novecento | 0:53f603890bd2 | 124 | |
novecento | 0:53f603890bd2 | 125 | // Score a point |
novecento | 0:53f603890bd2 | 126 | score++; |
novecento | 0:53f603890bd2 | 127 | |
novecento | 0:53f603890bd2 | 128 | // Level and speed will be incressed every 10 points |
novecento | 0:53f603890bd2 | 129 | if (score % 10 == 0) { |
novecento | 0:53f603890bd2 | 130 | // Pause the game during the "Next Level" message |
novecento | 0:53f603890bd2 | 131 | paused = true; |
novecento | 0:53f603890bd2 | 132 | level++; |
novecento | 0:53f603890bd2 | 133 | if (game_speed > 50) game_speed -= 50; |
novecento | 0:53f603890bd2 | 134 | uBit.display.clear(); |
novecento | 0:53f603890bd2 | 135 | uBit.display.scroll("LEVEL:"); |
novecento | 0:53f603890bd2 | 136 | uBit.display.scroll(level); |
novecento | 0:53f603890bd2 | 137 | // Bring Play back to the initial position |
novecento | 0:53f603890bd2 | 138 | uBit.display.image.setPixelValue(player.x, player.y, 0); |
novecento | 0:53f603890bd2 | 139 | player.x = 2; |
novecento | 0:53f603890bd2 | 140 | uBit.display.image.setPixelValue(player.x, player.y, 255); |
novecento | 0:53f603890bd2 | 141 | // Resume the game |
novecento | 0:53f603890bd2 | 142 | paused = false; |
novecento | 0:53f603890bd2 | 143 | } |
novecento | 0:53f603890bd2 | 144 | } |
novecento | 0:53f603890bd2 | 145 | // Give the CPU a rest |
novecento | 0:53f603890bd2 | 146 | uBit.sleep(25); |
novecento | 0:53f603890bd2 | 147 | } |
novecento | 0:53f603890bd2 | 148 | } |
novecento | 0:53f603890bd2 | 149 | /* Game over message */ |
novecento | 0:53f603890bd2 | 150 | void gameOver() |
novecento | 0:53f603890bd2 | 151 | { |
novecento | 0:53f603890bd2 | 152 | // Concatenate everthiny to ensure it will be in the right order at async |
novecento | 0:53f603890bd2 | 153 | // output |
novecento | 0:53f603890bd2 | 154 | const int cScore = score; |
novecento | 0:53f603890bd2 | 155 | ManagedString msGameOver = ManagedString("GAME OVER! SCORE: ") + ManagedString(cScore); |
novecento | 0:53f603890bd2 | 156 | if (new_high_score) msGameOver = msGameOver + ManagedString(" NEW HIGH SCORE!"); |
novecento | 0:53f603890bd2 | 157 | |
novecento | 0:53f603890bd2 | 158 | uBit.display.scrollAsync(msGameOver); |
novecento | 0:53f603890bd2 | 159 | } |
novecento | 0:53f603890bd2 | 160 | |
novecento | 0:53f603890bd2 | 161 | /* The Game */ |
novecento | 0:53f603890bd2 | 162 | void dropGame() |
novecento | 0:53f603890bd2 | 163 | { |
novecento | 0:53f603890bd2 | 164 | // Show current high score |
novecento | 0:53f603890bd2 | 165 | if (high_score > 0) { |
novecento | 0:53f603890bd2 | 166 | uBit.display.scroll("HIGH SCORE:"); |
novecento | 0:53f603890bd2 | 167 | uBit.display.scroll(high_score); |
novecento | 0:53f603890bd2 | 168 | } |
novecento | 0:53f603890bd2 | 169 | uBit.display.scroll("LEVEL: 1"); |
novecento | 0:53f603890bd2 | 170 | |
novecento | 0:53f603890bd2 | 171 | // Initialize player sprite |
novecento | 0:53f603890bd2 | 172 | player.x=2; |
novecento | 0:53f603890bd2 | 173 | player.y=4; |
novecento | 0:53f603890bd2 | 174 | uBit.display.image.setPixelValue(player.x, player.y, 255); |
novecento | 0:53f603890bd2 | 175 | |
novecento | 0:53f603890bd2 | 176 | // Initialize fruit sprite above the screen; dropFruit will do the rest |
novecento | 0:53f603890bd2 | 177 | fruit.x=uBit.random(5); |
novecento | 0:53f603890bd2 | 178 | fruit.y=-1; |
novecento | 0:53f603890bd2 | 179 | |
novecento | 0:53f603890bd2 | 180 | // Reset game state |
novecento | 0:53f603890bd2 | 181 | paused = false; |
novecento | 0:53f603890bd2 | 182 | game_speed = 400; |
novecento | 0:53f603890bd2 | 183 | level = 1; |
novecento | 0:53f603890bd2 | 184 | score = 0; |
novecento | 0:53f603890bd2 | 185 | life = 3; |
novecento | 0:53f603890bd2 | 186 | new_high_score = false; |
novecento | 0:53f603890bd2 | 187 | game_over = false; |
novecento | 0:53f603890bd2 | 188 | |
novecento | 0:53f603890bd2 | 189 | //Create background tasks for player and fruit update |
novecento | 0:53f603890bd2 | 190 | create_fiber(updatePlayer); |
novecento | 0:53f603890bd2 | 191 | create_fiber(dropFruit); |
novecento | 0:53f603890bd2 | 192 | |
novecento | 0:53f603890bd2 | 193 | // Game logik is in fibers updatePlayer and dropFruit |
novecento | 0:53f603890bd2 | 194 | // So, while playing do nothing |
novecento | 0:53f603890bd2 | 195 | while(!game_over) |
novecento | 0:53f603890bd2 | 196 | uBit.sleep(200); |
novecento | 0:53f603890bd2 | 197 | |
novecento | 0:53f603890bd2 | 198 | // Check for new high score |
novecento | 0:53f603890bd2 | 199 | if (score > high_score) { |
novecento | 0:53f603890bd2 | 200 | high_score = score; |
novecento | 0:53f603890bd2 | 201 | // Maker it persistent |
novecento | 0:53f603890bd2 | 202 | uBit.storage.put("high_score", (uint8_t *)&high_score, sizeof(int)); |
novecento | 0:53f603890bd2 | 203 | new_high_score = true; |
novecento | 0:53f603890bd2 | 204 | } |
novecento | 0:53f603890bd2 | 205 | |
novecento | 0:53f603890bd2 | 206 | // Show "Game Over" screen and score until A&B ist pressed together |
novecento | 0:53f603890bd2 | 207 | uBit.display.clear(); |
novecento | 0:53f603890bd2 | 208 | |
novecento | 0:53f603890bd2 | 209 | while(!uBit.buttonAB.isPressed()) { |
novecento | 0:53f603890bd2 | 210 | gameOver(); |
novecento | 0:53f603890bd2 | 211 | uBit.sleep(200); |
novecento | 0:53f603890bd2 | 212 | } |
novecento | 0:53f603890bd2 | 213 | uBit.display.stopAnimation(); |
novecento | 0:53f603890bd2 | 214 | } |
novecento | 0:53f603890bd2 | 215 | |
novecento | 0:53f603890bd2 | 216 | int main(void) |
novecento | 0:53f603890bd2 | 217 | { |
novecento | 0:53f603890bd2 | 218 | // Initialize the micro:bit runtime |
novecento | 0:53f603890bd2 | 219 | uBit.init(); |
novecento | 0:53f603890bd2 | 220 | |
novecento | 0:53f603890bd2 | 221 | // Game titel |
novecento | 0:53f603890bd2 | 222 | uBit.display.scroll("MICRO:DROP"); |
novecento | 0:53f603890bd2 | 223 | |
novecento | 0:53f603890bd2 | 224 | // Get current high score from storage |
novecento | 0:53f603890bd2 | 225 | KeyValuePair* stroredHighScore = uBit.storage.get("high_score"); |
novecento | 0:53f603890bd2 | 226 | if(stroredHighScore == NULL) { |
novecento | 0:53f603890bd2 | 227 | high_score = 0; // Seems to be the first boot; there is no high score |
novecento | 0:53f603890bd2 | 228 | } else { |
novecento | 0:53f603890bd2 | 229 | // Copy high score from key value pair into int var |
novecento | 0:53f603890bd2 | 230 | memcpy(&high_score, stroredHighScore->value, sizeof(int)); |
novecento | 0:53f603890bd2 | 231 | delete stroredHighScore; |
novecento | 0:53f603890bd2 | 232 | } |
novecento | 0:53f603890bd2 | 233 | |
novecento | 0:53f603890bd2 | 234 | //Disable buttons & background tasks at startup |
novecento | 0:53f603890bd2 | 235 | game_over = true; |
novecento | 0:53f603890bd2 | 236 | |
novecento | 0:53f603890bd2 | 237 | //Register functions to buttons A & B |
novecento | 0:53f603890bd2 | 238 | uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_DOWN, onButtonA); |
novecento | 0:53f603890bd2 | 239 | uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_DOWN, onButtonB); |
novecento | 0:53f603890bd2 | 240 | |
novecento | 0:53f603890bd2 | 241 | // Play forever ;-) |
novecento | 0:53f603890bd2 | 242 | while(1) { |
novecento | 0:53f603890bd2 | 243 | dropGame(); |
novecento | 0:53f603890bd2 | 244 | } |
novecento | 0:53f603890bd2 | 245 | } |