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:
Mon May 11 04:40:23 2015 +0000
Revision:
18:709ea375b0df
Parent:
17:d6a3b29cab31
Fixed some formatting errors in documentation.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Siriagus 0:1f92de30d43e 1 /**
Siriagus 10:f2488a0ecab7 2 * @file main.cpp
Siriagus 10:f2488a0ecab7 3 * @brief Simple platform game developed for ELEC2645 Embedded Systems Project at University of Leeds
Siriagus 10:f2488a0ecab7 4 * @author Andreas Garmannslund
Siriagus 17:d6a3b29cab31 5 * @date April/May 2015
Siriagus 10:f2488a0ecab7 6 */
Siriagus 0:1f92de30d43e 7
Siriagus 0:1f92de30d43e 8 #include "mbed.h"
Siriagus 0:1f92de30d43e 9 #include "N5110.h"
Siriagus 17:d6a3b29cab31 10 #include "PowerControl/PowerControl.h"
Siriagus 17:d6a3b29cab31 11 #include "PowerControl/EthernetPowerControl.h"
Siriagus 1:0cfe2255092a 12 #include "PinDetect.h"
Siriagus 4:d6661b976359 13 #include <string>
Siriagus 7:678873947b29 14 #include <sstream>
Siriagus 4:d6661b976359 15 #include <ctime>
Siriagus 1:0cfe2255092a 16
Siriagus 0:1f92de30d43e 17 #include "Joystick.h"
Siriagus 5:100d960fc6d5 18 #include "StateManager.h"
Siriagus 1:0cfe2255092a 19 #include "State.h"
Siriagus 4:d6661b976359 20 #include "InputManager.h"
Siriagus 17:d6a3b29cab31 21 #include "Sound.h"
Siriagus 0:1f92de30d43e 22
Siriagus 1:0cfe2255092a 23 // Redefine pin names for simple access.
Siriagus 1:0cfe2255092a 24 #define JOY_H p17
Siriagus 1:0cfe2255092a 25 #define JOY_V p16
Siriagus 1:0cfe2255092a 26 #define JOY_BTN p15
Siriagus 1:0cfe2255092a 27
Siriagus 1:0cfe2255092a 28 #define LED_POT p20
Siriagus 1:0cfe2255092a 29
Siriagus 2:0ae5ac8b0cac 30 #define BUTTON_A p28
Siriagus 2:0ae5ac8b0cac 31 #define BUTTON_B p27
Siriagus 1:0cfe2255092a 32 #define BUTTON_C p29
Siriagus 1:0cfe2255092a 33
Siriagus 4:d6661b976359 34 // Input and Output
Siriagus 18:709ea375b0df 35 /// Display
Siriagus 18:709ea375b0df 36 N5110 *lcd;
Siriagus 4:d6661b976359 37
Siriagus 18:709ea375b0df 38 /// Sound: Piezo buzzer
Siriagus 18:709ea375b0df 39 Sound *sound;
Siriagus 4:d6661b976359 40
Siriagus 18:709ea375b0df 41 /// Responsible for managing user input
Siriagus 18:709ea375b0df 42 InputManager *input;
Siriagus 0:1f92de30d43e 43
Siriagus 18:709ea375b0df 44 /// Brightness potentiometer
Siriagus 18:709ea375b0df 45 AnalogIn ledPot(LED_POT);
Siriagus 5:100d960fc6d5 46
Siriagus 14:b4fed570abaf 47 AnalogIn randomPin(p18); // Unconnected pin => noise, used for generating a random seed.
Siriagus 14:b4fed570abaf 48 AnalogIn randomPin2(p19); // Unconnected pin
Siriagus 14:b4fed570abaf 49
Siriagus 18:709ea375b0df 50 /// Set up initial variables
Siriagus 18:709ea375b0df 51 void init();
Siriagus 18:709ea375b0df 52
Siriagus 18:709ea375b0df 53 /// Frees remaining allocated memory
Siriagus 18:709ea375b0df 54 void cleanUp();
Siriagus 18:709ea375b0df 55
Siriagus 18:709ea375b0df 56 /// Finite state machine
Siriagus 18:709ea375b0df 57 StateManager* fsm;
Siriagus 18:709ea375b0df 58
Siriagus 18:709ea375b0df 59 /// Local file system
Siriagus 18:709ea375b0df 60 LocalFileSystem local("local");
Siriagus 17:d6a3b29cab31 61
Siriagus 14:b4fed570abaf 62 /// Function for generating a random seed by using a unconnected analog input pin
Siriagus 17:d6a3b29cab31 63 // Problem: Not trully random as pin is fairly stable. It is unfortunately connected to the ground layer (often returning 0). Would be better to connect it to a "loose wire"/bad connection-
Siriagus 17:d6a3b29cab31 64 unsigned int getRandSeed();
Siriagus 18:709ea375b0df 65
Siriagus 18:709ea375b0df 66 /// Read and load the high score file.
Siriagus 17:d6a3b29cab31 67 void readHighscoreFile();
Siriagus 14:b4fed570abaf 68
Siriagus 1:0cfe2255092a 69 int main()
Siriagus 14:b4fed570abaf 70 {
Siriagus 14:b4fed570abaf 71 srand(getRandSeed());
Siriagus 14:b4fed570abaf 72
Siriagus 1:0cfe2255092a 73 init();
Siriagus 1:0cfe2255092a 74
Siriagus 7:678873947b29 75 Timer timer;
Siriagus 7:678873947b29 76 timer.start();
Siriagus 4:d6661b976359 77
Siriagus 0:1f92de30d43e 78 while(true)
Siriagus 7:678873947b29 79 {
Siriagus 8:9ac6a428fa26 80 // We need to call Sleep() repeatedly, because periodic interrupts in InputManager will cause it to wake up every 20 ms.
Siriagus 8:9ac6a428fa26 81 while (timer.read() < 0.08)
Siriagus 8:9ac6a428fa26 82 Sleep();
Siriagus 8:9ac6a428fa26 83
Siriagus 7:678873947b29 84 // update
Siriagus 0:1f92de30d43e 85 lcd->setBrightness(1.0 - ledPot); // Update brightness of screen
Siriagus 7:678873947b29 86 fsm->update(timer.read());
Siriagus 7:678873947b29 87 input->joystick->update();
Siriagus 4:d6661b976359 88
Siriagus 4:d6661b976359 89 // render
Siriagus 5:100d960fc6d5 90 lcd->clear();
Siriagus 7:678873947b29 91 fsm->render();
Siriagus 5:100d960fc6d5 92 lcd->refresh();
Siriagus 1:0cfe2255092a 93
Siriagus 7:678873947b29 94 fsm->processRequest(); // Change the state if requested.
Siriagus 7:678873947b29 95
Siriagus 7:678873947b29 96 timer.reset();
Siriagus 0:1f92de30d43e 97 }
Siriagus 0:1f92de30d43e 98
Siriagus 17:d6a3b29cab31 99 cleanUp(); // Not really reached as the program never terminates. Added for completeness.
Siriagus 0:1f92de30d43e 100
Siriagus 0:1f92de30d43e 101 return 0;
Siriagus 3:4e3f342a135c 102 }
Siriagus 3:4e3f342a135c 103
Siriagus 3:4e3f342a135c 104 void init()
Siriagus 3:4e3f342a135c 105 {
Siriagus 17:d6a3b29cab31 106 // Disable ethernet to save power
Siriagus 17:d6a3b29cab31 107 PHY_PowerDown();
Siriagus 17:d6a3b29cab31 108
Siriagus 3:4e3f342a135c 109 // Init LCD
Siriagus 3:4e3f342a135c 110 lcd = new N5110(p7, p8, p9, p10, p11, p13, p26);
Siriagus 3:4e3f342a135c 111 lcd->init();
Siriagus 3:4e3f342a135c 112 lcd->normalMode();
Siriagus 3:4e3f342a135c 113 lcd->setBrightness(1.0 - ledPot); // Update brightness of screen
Siriagus 3:4e3f342a135c 114
Siriagus 4:d6661b976359 115 // Input
Siriagus 8:9ac6a428fa26 116 input = new InputManager(BUTTON_A, BUTTON_B, BUTTON_C, JOY_H, JOY_V, JOY_BTN);
Siriagus 3:4e3f342a135c 117
Siriagus 17:d6a3b29cab31 118 // Sound
Siriagus 17:d6a3b29cab31 119 sound = new Sound(p21);
Siriagus 17:d6a3b29cab31 120
Siriagus 17:d6a3b29cab31 121 // Finite state machine
Siriagus 17:d6a3b29cab31 122 fsm = new StateManager(lcd, input, sound, TITLE_SCREEN);
Siriagus 17:d6a3b29cab31 123
Siriagus 17:d6a3b29cab31 124 readHighscoreFile(); // load highscores from file system
Siriagus 17:d6a3b29cab31 125
Siriagus 17:d6a3b29cab31 126 sound->playNote(SFX::RESTART);
Siriagus 17:d6a3b29cab31 127 }
Siriagus 17:d6a3b29cab31 128
Siriagus 17:d6a3b29cab31 129 void readHighscoreFile()
Siriagus 17:d6a3b29cab31 130 {
Siriagus 17:d6a3b29cab31 131 FILE *fp = fopen("/local/highscores.txt", "r"); // read from local file
Siriagus 17:d6a3b29cab31 132
Siriagus 17:d6a3b29cab31 133 if (!fp) // if file is not created yet the highscores will default values as defined in Global.cpp
Siriagus 17:d6a3b29cab31 134 return;
Siriagus 17:d6a3b29cab31 135
Siriagus 17:d6a3b29cab31 136 char initials[4];
Siriagus 17:d6a3b29cab31 137 char scoreStr[8];
Siriagus 17:d6a3b29cab31 138
Siriagus 17:d6a3b29cab31 139 for (int i = 0; i < 3; ++i)
Siriagus 17:d6a3b29cab31 140 {
Siriagus 17:d6a3b29cab31 141 fscanf(fp, "%s %s", initials, scoreStr); // read from file
Siriagus 17:d6a3b29cab31 142
Siriagus 17:d6a3b29cab31 143 int score = atoi(scoreStr); // convert to int
Siriagus 17:d6a3b29cab31 144
Siriagus 17:d6a3b29cab31 145 // update global highscore table
Siriagus 17:d6a3b29cab31 146 Global::highscores[i].initials = initials;
Siriagus 17:d6a3b29cab31 147 Global::highscores[i].score = score;
Siriagus 17:d6a3b29cab31 148 }
Siriagus 17:d6a3b29cab31 149
Siriagus 17:d6a3b29cab31 150 fclose(fp);
Siriagus 3:4e3f342a135c 151 }
Siriagus 3:4e3f342a135c 152
Siriagus 3:4e3f342a135c 153 void cleanUp()
Siriagus 3:4e3f342a135c 154 {
Siriagus 3:4e3f342a135c 155 delete lcd;
Siriagus 4:d6661b976359 156 delete input;
Siriagus 17:d6a3b29cab31 157 delete sound;
Siriagus 17:d6a3b29cab31 158 }
Siriagus 17:d6a3b29cab31 159
Siriagus 18:709ea375b0df 160 /** Get a random seed based on unconnected analog input pins
Siriagus 18:709ea375b0df 161 * @see https://developer.mbed.org/questions/2886/Why-is-the-rand-function-not-the-least-b/
Siriagus 18:709ea375b0df 162 */
Siriagus 17:d6a3b29cab31 163 unsigned int getRandSeed()
Siriagus 17:d6a3b29cab31 164 {
Siriagus 17:d6a3b29cab31 165 unsigned int randNum = 0;
Siriagus 17:d6a3b29cab31 166 unsigned int lsb; // least significant bit
Siriagus 17:d6a3b29cab31 167
Siriagus 17:d6a3b29cab31 168 for (unsigned int i = 0; i < 16; ++i)
Siriagus 17:d6a3b29cab31 169 {
Siriagus 17:d6a3b29cab31 170 lsb = 1 & (randomPin.read_u16()); // Read least significant bit in randomInput - value depends on noise
Siriagus 17:d6a3b29cab31 171 randNum |= (lsb << i); // Set bit nr.i to lsb in randomInput
Siriagus 17:d6a3b29cab31 172
Siriagus 17:d6a3b29cab31 173 lsb = 1 & (randomPin2.read_u16());
Siriagus 17:d6a3b29cab31 174 randNum |= (lsb << (16+i));
Siriagus 17:d6a3b29cab31 175 }
Siriagus 17:d6a3b29cab31 176
Siriagus 17:d6a3b29cab31 177 return randNum;
Siriagus 1:0cfe2255092a 178 }