A simple one-level platform game. Developed as part of ELEC2645 at University of Leeds, spring 2015.

Dependencies:   N5110 PinDetect PowerControl mbed

An ARM mbed LPC1768 microcontroller have been used to develop a handheld arcade game in the style of an old-school platformer. This project is entirely my own independent work in all stages of the development; including design, defining project specifications, breadboard prototyping, schematic and PCB layout using CAD, assembly, testing and software development. Due to this being part of the ELEC2645 Embedded Systems Project module at University of Leeds, spring 2015, limitations were given on the available hardware components. Credit is due to the authors of the dependent libraries (N5110, Pin Detect, PowerControl and mbed). I would also like to thank the author of Game Programming Patterns as well as the authors of SFML Game Development for providing me with useful sources for programming design patterns.

/media/uploads/Siriagus/game_assembled.jpg

Project aims

  • Implement simple gameplay:
    • A single, fixed (no scrolling) level.
    • Player can move left to right, jump and shoot.
    • Enemies will drop from the top of the screen.
    • The player gets points for shooting enemies.
    • The player dies when it gets hits by an enemy.
  • Implement a simple menu system.
  • Enable the user to adjust the brightness of the display.
  • Output sound to enhance the user experience.

Software

The program flow is controlled by a finite state machine. The implemented design was inspired by the State design pattern from the books Game Programming Patterns and SFML Game Development. The StateManager class is responsible for updating and rendering the current selected state. It also changes the state based on request from the current state. The framework built for the state machine used in this project makes it easy to add new screens. The different main states (indicated by the background colour) and how the user interaction is shown below: /media/uploads/Siriagus/arcadegameuserinteraction.png

Hardware

Schematic:

/media/uploads/Siriagus/schematic.png

Printed circuit board (PCB):

/media/uploads/Siriagus/pcb.png

Images

A seperate program was written to convert images (png) to text-representation of the maps. Enemies and numbers on the screen are also collected from a sprite-sheet created in the same manner.

/media/uploads/Siriagus/unileeds3.png /media/uploads/Siriagus/newmap2.png

Committer:
Siriagus
Date:
Tue Apr 28 15:42:22 2015 +0000
Revision:
3:4e3f342a135c
Parent:
2:0ae5ac8b0cac
Child:
4:d6661b976359
More compact main menu FSM

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Siriagus 0:1f92de30d43e 1 /**
Siriagus 0:1f92de30d43e 2 @brief Simple platform game developed for ELEC2645 Embedded Systems Project at University of Leeds
Siriagus 0:1f92de30d43e 3
Siriagus 0:1f92de30d43e 4 @author Andreas Garmannslund
Siriagus 0:1f92de30d43e 5 **/
Siriagus 0:1f92de30d43e 6
Siriagus 0:1f92de30d43e 7 #include "mbed.h"
Siriagus 0:1f92de30d43e 8 #include "N5110.h"
Siriagus 1:0cfe2255092a 9 #include "PowerControl.h"
Siriagus 1:0cfe2255092a 10 #include "PinDetect.h"
Siriagus 1:0cfe2255092a 11
Siriagus 0:1f92de30d43e 12 #include "Joystick.h"
Siriagus 1:0cfe2255092a 13 #include <string>
Siriagus 1:0cfe2255092a 14
Siriagus 1:0cfe2255092a 15 #include "State.h"
Siriagus 1:0cfe2255092a 16 #include "MainMenu.h"
Siriagus 0:1f92de30d43e 17 #include "map.h"
Siriagus 1:0cfe2255092a 18 #include "images.h"
Siriagus 1:0cfe2255092a 19
Siriagus 0:1f92de30d43e 20 #include <ctime>
Siriagus 0:1f92de30d43e 21
Siriagus 1:0cfe2255092a 22 // Redefine pin names for simple access.
Siriagus 1:0cfe2255092a 23 #define JOY_H p17
Siriagus 1:0cfe2255092a 24 #define JOY_V p16
Siriagus 1:0cfe2255092a 25 #define JOY_BTN p15
Siriagus 1:0cfe2255092a 26
Siriagus 1:0cfe2255092a 27 #define LED_POT p20
Siriagus 1:0cfe2255092a 28
Siriagus 2:0ae5ac8b0cac 29 #define BUTTON_A p28
Siriagus 2:0ae5ac8b0cac 30 #define BUTTON_B p27
Siriagus 1:0cfe2255092a 31 #define BUTTON_C p29
Siriagus 1:0cfe2255092a 32
Siriagus 0:1f92de30d43e 33 // Components
Siriagus 0:1f92de30d43e 34 N5110 *lcd; // VCC, SCE, RST, D/C, MOSI, SCLK and LED
Siriagus 0:1f92de30d43e 35 Joystick *joystick;
Siriagus 0:1f92de30d43e 36
Siriagus 0:1f92de30d43e 37 // Brightness potentiometer
Siriagus 1:0cfe2255092a 38 AnalogIn ledPot(LED_POT);
Siriagus 0:1f92de30d43e 39
Siriagus 1:0cfe2255092a 40 /*
Siriagus 0:1f92de30d43e 41 // Buttons
Siriagus 0:1f92de30d43e 42 DigitalIn btnA(p27);
Siriagus 0:1f92de30d43e 43 DigitalIn btnB(p28);
Siriagus 0:1f92de30d43e 44 DigitalIn btnC(p29);
Siriagus 1:0cfe2255092a 45 */
Siriagus 0:1f92de30d43e 46
Siriagus 0:1f92de30d43e 47 // Debugging
Siriagus 0:1f92de30d43e 48 Serial pc(USBTX, USBRX);
Siriagus 0:1f92de30d43e 49 BusOut leds(LED1, LED2, LED3, LED4);
Siriagus 0:1f92de30d43e 50
Siriagus 1:0cfe2255092a 51
Siriagus 1:0cfe2255092a 52 // @brief Clears the screen and fill it with the image in the argument.
Siriagus 1:0cfe2255092a 53 void clearAndDrawImage(const int img[BANKS][WIDTH])
Siriagus 0:1f92de30d43e 54 {
Siriagus 0:1f92de30d43e 55 for (int i = 0; i < BANKS; ++i)
Siriagus 0:1f92de30d43e 56 {
Siriagus 0:1f92de30d43e 57 for (int j = 0; j < WIDTH; ++j)
Siriagus 0:1f92de30d43e 58 {
Siriagus 1:0cfe2255092a 59 lcd->buffer[j][i] = img[i][j];
Siriagus 0:1f92de30d43e 60 }
Siriagus 0:1f92de30d43e 61 }
Siriagus 0:1f92de30d43e 62 lcd->refresh();
Siriagus 0:1f92de30d43e 63 }
Siriagus 0:1f92de30d43e 64
Siriagus 3:4e3f342a135c 65 //State *stateObj; // current state object
Siriagus 1:0cfe2255092a 66
Siriagus 2:0ae5ac8b0cac 67 // States
Siriagus 2:0ae5ac8b0cac 68
Siriagus 3:4e3f342a135c 69 //enum MenuState {SELECT_MENU, HIGH_SCORES, CONTROLS, LOAD_GAME};
Siriagus 2:0ae5ac8b0cac 70
Siriagus 3:4e3f342a135c 71 enum MenuState {SELECT_PLAY, SELECT_HIGHSCORES, SELECT_CONTROLS, LOAD_GAME, HIGHSCORES, CONTROLS};
Siriagus 3:4e3f342a135c 72 int currentState; // current state
Siriagus 3:4e3f342a135c 73
Siriagus 3:4e3f342a135c 74 enum NewMenuState;
Siriagus 3:4e3f342a135c 75
Siriagus 3:4e3f342a135c 76 // fsm[state][input] where input are 0, 1, 2 corresponding to buttons A, B, C
Siriagus 3:4e3f342a135c 77 int fsm[6][3] = {
Siriagus 3:4e3f342a135c 78 {LOAD_GAME, SELECT_PLAY, SELECT_HIGHSCORES}, // State: SELECT_PLAY
Siriagus 3:4e3f342a135c 79 {HIGHSCORES, SELECT_HIGHSCORES, SELECT_CONTROLS}, // State: SELECT_HIGHSCORES
Siriagus 3:4e3f342a135c 80 {CONTROLS, SELECT_CONTROLS, SELECT_PLAY}, // State: SELECT_CONTROLS
Siriagus 3:4e3f342a135c 81 {LOAD_GAME, LOAD_GAME, LOAD_GAME}, // State: LOAD_GAME
Siriagus 3:4e3f342a135c 82 {SELECT_HIGHSCORES, SELECT_HIGHSCORES, HIGHSCORES}, // State: HIGHSCORES
Siriagus 3:4e3f342a135c 83 {SELECT_CONTROLS, SELECT_CONTROLS, CONTROLS} // State: CONTROLS
Siriagus 3:4e3f342a135c 84 };
Siriagus 1:0cfe2255092a 85
Siriagus 2:0ae5ac8b0cac 86 PinDetect btnA(BUTTON_A);
Siriagus 2:0ae5ac8b0cac 87 PinDetect btnB(BUTTON_B);
Siriagus 1:0cfe2255092a 88 PinDetect btnC(BUTTON_C);
Siriagus 2:0ae5ac8b0cac 89
Siriagus 3:4e3f342a135c 90 void btnAPressed() {currentState = fsm[currentState][0];}
Siriagus 3:4e3f342a135c 91 void btnBPressed() {currentState = fsm[currentState][1];}
Siriagus 3:4e3f342a135c 92 void btnCPressed() {currentState = fsm[currentState][2];}
Siriagus 2:0ae5ac8b0cac 93
Siriagus 3:4e3f342a135c 94 void init();
Siriagus 3:4e3f342a135c 95 void cleanUp();
Siriagus 1:0cfe2255092a 96
Siriagus 1:0cfe2255092a 97 int main()
Siriagus 1:0cfe2255092a 98 {
Siriagus 1:0cfe2255092a 99 init();
Siriagus 1:0cfe2255092a 100
Siriagus 0:1f92de30d43e 101 while(true)
Siriagus 0:1f92de30d43e 102 {
Siriagus 1:0cfe2255092a 103 lcd->clear();
Siriagus 1:0cfe2255092a 104
Siriagus 0:1f92de30d43e 105 joystick->update();
Siriagus 0:1f92de30d43e 106 lcd->setBrightness(1.0 - ledPot); // Update brightness of screen
Siriagus 0:1f92de30d43e 107
Siriagus 2:0ae5ac8b0cac 108 // Update
Siriagus 2:0ae5ac8b0cac 109 switch (currentState)
Siriagus 3:4e3f342a135c 110 {
Siriagus 3:4e3f342a135c 111 case SELECT_PLAY:
Siriagus 3:4e3f342a135c 112 lcd->printString(">Play", 10, 1);
Siriagus 3:4e3f342a135c 113 lcd->printString("High Scores", 10, 2);
Siriagus 3:4e3f342a135c 114 lcd->printString("Controls", 10, 3);
Siriagus 2:0ae5ac8b0cac 115 break;
Siriagus 2:0ae5ac8b0cac 116
Siriagus 3:4e3f342a135c 117 case SELECT_HIGHSCORES:
Siriagus 3:4e3f342a135c 118 lcd->printString("Play", 10, 1);
Siriagus 3:4e3f342a135c 119 lcd->printString(">High Scores", 10, 2);
Siriagus 3:4e3f342a135c 120 lcd->printString("Controls", 10, 3);
Siriagus 3:4e3f342a135c 121 break;
Siriagus 3:4e3f342a135c 122
Siriagus 3:4e3f342a135c 123 case SELECT_CONTROLS:
Siriagus 3:4e3f342a135c 124 lcd->printString("Play", 10, 1);
Siriagus 3:4e3f342a135c 125 lcd->printString("High Scores", 10, 2);
Siriagus 3:4e3f342a135c 126 lcd->printString(">Controls", 10, 3);
Siriagus 3:4e3f342a135c 127 break;
Siriagus 3:4e3f342a135c 128
Siriagus 3:4e3f342a135c 129 case HIGHSCORES:
Siriagus 2:0ae5ac8b0cac 130 // Placeholder TODO: Actually high scores
Siriagus 2:0ae5ac8b0cac 131 lcd->printString("High Scores", 10, 0);
Siriagus 2:0ae5ac8b0cac 132 lcd->printString("AND 1000000", 10, 1);
Siriagus 2:0ae5ac8b0cac 133 lcd->printString("AND 500000", 10, 2);
Siriagus 2:0ae5ac8b0cac 134 lcd->printString("AND 100", 10, 3);
Siriagus 3:4e3f342a135c 135 lcd->printString(">Back", 10, 4);
Siriagus 2:0ae5ac8b0cac 136 break;
Siriagus 2:0ae5ac8b0cac 137
Siriagus 2:0ae5ac8b0cac 138 case CONTROLS:
Siriagus 2:0ae5ac8b0cac 139 lcd->printString("Controls", 10, 0);
Siriagus 2:0ae5ac8b0cac 140 lcd->printString("A: Jump", 10, 1);
Siriagus 2:0ae5ac8b0cac 141 lcd->printString("B: Shoot", 10, 2);
Siriagus 2:0ae5ac8b0cac 142 lcd->printString("C: Pause", 10, 3);
Siriagus 3:4e3f342a135c 143 lcd->printString(">Back", 10, 4);
Siriagus 2:0ae5ac8b0cac 144 break;
Siriagus 2:0ae5ac8b0cac 145
Siriagus 2:0ae5ac8b0cac 146 case LOAD_GAME:
Siriagus 2:0ae5ac8b0cac 147 clearAndDrawImage(logo);
Siriagus 2:0ae5ac8b0cac 148 break;
Siriagus 2:0ae5ac8b0cac 149
Siriagus 1:0cfe2255092a 150 }
Siriagus 1:0cfe2255092a 151
Siriagus 1:0cfe2255092a 152 lcd->refresh();
Siriagus 2:0ae5ac8b0cac 153 Sleep();
Siriagus 0:1f92de30d43e 154 }
Siriagus 0:1f92de30d43e 155
Siriagus 1:0cfe2255092a 156 cleanUp();
Siriagus 0:1f92de30d43e 157
Siriagus 0:1f92de30d43e 158 return 0;
Siriagus 3:4e3f342a135c 159 }
Siriagus 3:4e3f342a135c 160
Siriagus 3:4e3f342a135c 161 void init()
Siriagus 3:4e3f342a135c 162 {
Siriagus 3:4e3f342a135c 163 // Init LCD
Siriagus 3:4e3f342a135c 164 lcd = new N5110(p7, p8, p9, p10, p11, p13, p26);
Siriagus 3:4e3f342a135c 165 lcd->init();
Siriagus 3:4e3f342a135c 166 lcd->normalMode();
Siriagus 3:4e3f342a135c 167 lcd->setBrightness(1.0 - ledPot); // Update brightness of screen
Siriagus 3:4e3f342a135c 168
Siriagus 3:4e3f342a135c 169 // Init joystick
Siriagus 3:4e3f342a135c 170 joystick = new Joystick(JOY_H, JOY_V, JOY_BTN);
Siriagus 3:4e3f342a135c 171 joystick->calibrate();
Siriagus 3:4e3f342a135c 172
Siriagus 3:4e3f342a135c 173 // Init buttons
Siriagus 3:4e3f342a135c 174 btnA.attach_asserted(&btnAPressed);
Siriagus 3:4e3f342a135c 175 btnB.attach_asserted(&btnBPressed);
Siriagus 3:4e3f342a135c 176 btnC.attach_asserted(&btnCPressed);
Siriagus 3:4e3f342a135c 177
Siriagus 3:4e3f342a135c 178 btnA.setSampleFrequency();
Siriagus 3:4e3f342a135c 179 btnB.setSampleFrequency();
Siriagus 3:4e3f342a135c 180 btnC.setSampleFrequency();
Siriagus 3:4e3f342a135c 181
Siriagus 3:4e3f342a135c 182 // Set initial state
Siriagus 3:4e3f342a135c 183 currentState = SELECT_PLAY;
Siriagus 3:4e3f342a135c 184 }
Siriagus 3:4e3f342a135c 185
Siriagus 3:4e3f342a135c 186 void cleanUp()
Siriagus 3:4e3f342a135c 187 {
Siriagus 3:4e3f342a135c 188 delete lcd;
Siriagus 3:4e3f342a135c 189 delete joystick;
Siriagus 1:0cfe2255092a 190 }