A space invaders game using the micro:bit. This example uses buttons, 'fibers' (like threads or tasks), the display, and the sleep functions. It is one of the more elaborate examples useful for someone wanting to build a game or understand the scheduler. This is a one-way translation of the microbit-samples repository on GitHub. Please don't try to push changes here, instead push them to the source repo at https://github.com/lancaster-university/microbit-samples

Dependencies:   microbit

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*
00002 The MIT License (MIT)
00003 
00004 Copyright (c) 2016 Lancaster University.
00005 
00006 Permission is hereby granted, free of charge, to any person obtaining a
00007 copy of this software and associated documentation files (the "Software"),
00008 to deal in the Software without restriction, including without limitation
00009 the rights to use, copy, modify, merge, publish, distribute, sublicense,
00010 and/or sell copies of the Software, and to permit persons to whom the
00011 Software is furnished to do so, subject to the following conditions:
00012 
00013 The above copyright notice and this permission notice shall be included in
00014 all 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
00019 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00020 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00021 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00022 DEALINGS IN THE SOFTWARE.
00023 */
00024 
00025 
00026 //
00027 //
00028 // A simple game of Space Invaders for the BBC micro:bit, using the
00029 // accelerometer and buttons to control the player's ship.
00030 //
00031 // As well as illustrating the use of sensors, the display and events,
00032 // this demonstration also provides a highly elegant example of
00033 // how fibers can be used to create safe and elegant programs... Note the
00034 // use of event handlers when the player fires, and the indepenent fibers
00035 // used to control the players ship, aliens, bullets and screen refresh...
00036 //
00037 //
00038 
00039 #include "MicroBit.h"
00040 
00041 #define GAME_ON         0
00042 #define GAME_OVER       1
00043 
00044 struct Point
00045 {
00046     int     x;
00047     int     y;
00048 };
00049 
00050 MicroBit        uBit;
00051 MicroBitImage   invaders(5,5);
00052 int             score;
00053 int             game_over;
00054 int             level;
00055 int             INVADER_SPEED = 750;
00056 int             PLAYER_SPEED = 150;
00057 int             BULLET_SPEED = 50;
00058 Point           player;
00059 Point           bullet;
00060 
00061 /**
00062  * Add a new row of space invaders to the game.
00063  */
00064 int
00065 addRow()
00066 {
00067     // If we're adding a row of invaders, but we're out of space, it's game over!!
00068     for (int x=0; x<5; x++)
00069         if (invaders.getPixelValue(x,4))
00070             return GAME_OVER;
00071 
00072     // Otherwise, move down the invaders, and add a new role at the top.
00073     invaders.shiftDown(1);
00074 
00075     for (int x=1; x<4; x++)
00076         invaders.setPixelValue(x,0,255);
00077 
00078     return GAME_ON;
00079 }
00080 
00081 /*
00082  * Display Game Over and show the player's score.
00083  */
00084 void
00085 gameOver()
00086 {
00087     uBit.display.clear();
00088 
00089     uBit.display.scroll("GAME OVER! SCORE:");
00090     uBit.display.scroll(score);
00091 }
00092 
00093 /*
00094  * Calculate the speed of an invaders movement, based on the game level
00095  */
00096 int
00097 invaderSpeed()
00098 {
00099     return max(INVADER_SPEED - level*50, 50);
00100 }
00101 
00102 /*
00103  * Determine if the are any space invaders in the given column
00104  */
00105 bool
00106 invadersInColumn(int x)
00107 {
00108     for (int y = 0; y < 5; y++)
00109         if (invaders.getPixelValue(x,y))
00110             return true;
00111 
00112     return false;
00113 }
00114 
00115 /*
00116  * Determine the number of space invaders currently on screen
00117  */
00118 bool
00119 invaderCount()
00120 {
00121     int count = 0;
00122 
00123     for (int x=0; x<5; x++)
00124         for (int y=0; y<5; y++)
00125             if (invaders.getPixelValue(x,y))
00126                 count++;
00127 
00128     return count;
00129 }
00130 
00131 /*
00132  * Move space invaders on the screen.
00133  */
00134 void
00135 invaderUpdate()
00136 {
00137     bool movingRight = true;
00138 
00139     while(!game_over)
00140     {
00141         // Wait for next update;    
00142         uBit.sleep(invaderSpeed());
00143 
00144         if (movingRight)
00145         {
00146             if(invadersInColumn(4))
00147             {
00148                 movingRight = false;
00149                 if (addRow() == GAME_OVER)
00150                 {
00151                     game_over = true;
00152                     return;
00153                 }
00154             }
00155             else
00156             {
00157                 invaders.shiftRight(1);
00158             }
00159         }
00160         else
00161         {
00162             if(invadersInColumn(0))
00163             {
00164                 movingRight = true;
00165                 if (addRow() == GAME_OVER)
00166                 {
00167                     game_over = true;
00168                     return;
00169                 }
00170             }
00171             else
00172             {
00173                 invaders.shiftLeft(1);
00174             }
00175         }
00176 
00177         if (invaderCount() == 0)
00178         {
00179             level++;
00180             addRow();
00181         }
00182     }
00183 }
00184 
00185 /* 
00186  * Move the bullet up the screen
00187  */
00188 void
00189 bulletUpdate()
00190 {
00191     while (!game_over)
00192     {
00193         uBit.sleep(BULLET_SPEED);
00194         if (bullet.y != -1)
00195             bullet.y--;
00196 
00197         if (invaders.getPixelValue(bullet.x, bullet.y) > 0)
00198         {
00199             score++;
00200             invaders.setPixelValue(bullet.x, bullet.y, 0);
00201             bullet.x = -1;
00202             bullet.y = -1;
00203         }
00204     }
00205 }
00206 
00207 /*
00208  * Move the player across the screen.
00209  */
00210 void
00211 playerUpdate()
00212 {
00213     while (!game_over)
00214     {
00215         uBit.sleep(PLAYER_SPEED);
00216 
00217         if(uBit.accelerometer.getX() < -300 && player.x > 0)
00218             player.x--;
00219 
00220         if(uBit.accelerometer.getX() > 300 && player.x < 4)
00221             player.x++;
00222     }
00223 }
00224 
00225 /*
00226  * Fire a new missile from the player
00227  */
00228 void
00229 fire(MicroBitEvent)
00230 {
00231     if (bullet.y == -1)
00232     {
00233         bullet.y = 4;
00234         bullet.x = player.x;
00235     }
00236 }
00237 
00238 /*
00239  * A simple game of space invaders
00240  */
00241 void 
00242 spaceInvaders()
00243 {   
00244     // Reset all game state.
00245     game_over = 0;
00246     level = 0;
00247     score = 0;
00248     player.x = 2;
00249     player.y = 4;
00250 
00251     bullet.x = -1;
00252     bullet.y = -1;
00253 
00254     // Add a single row of invaders at the start.. cannon fodder!
00255     invaders.clear();
00256     addRow();
00257 
00258     // Spawn independent fibers to handle the movement of each player
00259     create_fiber(invaderUpdate);
00260     create_fiber(bulletUpdate);
00261     create_fiber(playerUpdate);
00262 
00263     // Register event handlers for button presses (either button fires!)
00264     uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, fire);
00265     uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, fire);
00266 
00267     // Now just keep the screen refreshed.
00268     while (!game_over)
00269     {    
00270         uBit.sleep(10);
00271         uBit.display.image.paste(invaders);
00272         uBit.display.image.setPixelValue(player.x, player.y, 255);
00273         uBit.display.image.setPixelValue(bullet.x, bullet.y, 255);
00274     }
00275 
00276     // Display GAME OVER and score
00277     gameOver();
00278 }
00279 
00280 int main()
00281 {
00282     // Initialise the micro:bit runtime.
00283     uBit.init();
00284 
00285     // Welcome message
00286     uBit.display.scroll("INVADERS!");
00287 
00288     // Keep playing forever
00289     while(1)
00290         spaceInvaders();
00291 }
00292