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:
Sat May 02 00:22:43 2015 +0000
Revision:
8:9ac6a428fa26
Parent:
7:678873947b29
Child:
9:da608ae65df9
Added Splash/Title Screen + Prototyping player movement

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Siriagus 7:678873947b29 1 #include "Game.h"
Siriagus 7:678873947b29 2
Siriagus 8:9ac6a428fa26 3 Serial pc(USBTX, USBRX); // TO DELETE - DEBUGGING ONLY
Siriagus 8:9ac6a428fa26 4
Siriagus 8:9ac6a428fa26 5 bool Game::btnAPressed = false;
Siriagus 8:9ac6a428fa26 6 bool Game::btnBPressed = false;
Siriagus 8:9ac6a428fa26 7
Siriagus 8:9ac6a428fa26 8 void Game::btnAPress() {btnAPressed = true;}
Siriagus 8:9ac6a428fa26 9
Siriagus 8:9ac6a428fa26 10 void Game::btnBPress() {btnBPressed = true;}
Siriagus 8:9ac6a428fa26 11
Siriagus 8:9ac6a428fa26 12 // BUG: Registrer jo bare btn press, må være en bedre måte, men er trøtt nå
Siriagus 8:9ac6a428fa26 13
Siriagus 8:9ac6a428fa26 14 void Game::init()
Siriagus 8:9ac6a428fa26 15 {
Siriagus 8:9ac6a428fa26 16 input->addBtnPressInterrupt(input->btnA, &btnAPress);
Siriagus 8:9ac6a428fa26 17 input->addBtnPressInterrupt(input->btnB, &btnBPress);
Siriagus 8:9ac6a428fa26 18 }
Siriagus 8:9ac6a428fa26 19
Siriagus 8:9ac6a428fa26 20 // Functions
Siriagus 7:678873947b29 21 void Game::update(float dt)
Siriagus 7:678873947b29 22 {
Siriagus 8:9ac6a428fa26 23 // Handle input, should be its own function
Siriagus 8:9ac6a428fa26 24 switch(input->joystick->getDirection())
Siriagus 8:9ac6a428fa26 25 {
Siriagus 8:9ac6a428fa26 26
Siriagus 8:9ac6a428fa26 27 case LEFT:
Siriagus 8:9ac6a428fa26 28 case UP_LEFT:
Siriagus 8:9ac6a428fa26 29 case DOWN_LEFT:
Siriagus 8:9ac6a428fa26 30 player.vx = -2;
Siriagus 8:9ac6a428fa26 31 break;
Siriagus 8:9ac6a428fa26 32
Siriagus 8:9ac6a428fa26 33 case RIGHT:
Siriagus 8:9ac6a428fa26 34 case UP_RIGHT:
Siriagus 8:9ac6a428fa26 35 case DOWN_RIGHT:
Siriagus 8:9ac6a428fa26 36 player.vx = 2;
Siriagus 8:9ac6a428fa26 37 break;
Siriagus 8:9ac6a428fa26 38
Siriagus 8:9ac6a428fa26 39
Siriagus 8:9ac6a428fa26 40 case UP:
Siriagus 8:9ac6a428fa26 41 //player.vy = -4;
Siriagus 8:9ac6a428fa26 42 break;
Siriagus 8:9ac6a428fa26 43 case DOWN:
Siriagus 8:9ac6a428fa26 44 player.vy += 1;
Siriagus 8:9ac6a428fa26 45 break;
Siriagus 8:9ac6a428fa26 46
Siriagus 8:9ac6a428fa26 47 case CENTER:
Siriagus 8:9ac6a428fa26 48 player.vx = 0;
Siriagus 8:9ac6a428fa26 49 break;
Siriagus 8:9ac6a428fa26 50 }
Siriagus 7:678873947b29 51
Siriagus 8:9ac6a428fa26 52
Siriagus 8:9ac6a428fa26 53
Siriagus 8:9ac6a428fa26 54 if (player.y < HEIGHT-10) player.vy += 1; // gravity
Siriagus 8:9ac6a428fa26 55 else if (player.vy > 0) {player.vy = 0; player.onGround = true;}
Siriagus 8:9ac6a428fa26 56
Siriagus 8:9ac6a428fa26 57
Siriagus 8:9ac6a428fa26 58 if (Game::btnAPressed) // && player.onGround)
Siriagus 8:9ac6a428fa26 59 {
Siriagus 8:9ac6a428fa26 60 player.vy = -4;
Siriagus 8:9ac6a428fa26 61 btnAPressed = player.onGround = false;
Siriagus 8:9ac6a428fa26 62 pc.printf("Her er jeg\n");
Siriagus 8:9ac6a428fa26 63 }
Siriagus 8:9ac6a428fa26 64
Siriagus 8:9ac6a428fa26 65 if (player.vy > 3) player.vy = 3;
Siriagus 8:9ac6a428fa26 66
Siriagus 8:9ac6a428fa26 67 player.x += player.vx;
Siriagus 8:9ac6a428fa26 68 player.y += player.vy;
Siriagus 8:9ac6a428fa26 69
Siriagus 8:9ac6a428fa26 70 // Bullets - TODO: Give the bullets a direction - need to delete them when they go off the screen
Siriagus 8:9ac6a428fa26 71 if (btnBPressed)
Siriagus 8:9ac6a428fa26 72 {
Siriagus 8:9ac6a428fa26 73 Point* bullet = new Point;
Siriagus 8:9ac6a428fa26 74 bullet->x = player.x-1;
Siriagus 8:9ac6a428fa26 75 bullet->y = player.y + 2;
Siriagus 8:9ac6a428fa26 76
Siriagus 8:9ac6a428fa26 77 bullets.push_back(bullet);
Siriagus 8:9ac6a428fa26 78 btnBPressed = false;
Siriagus 8:9ac6a428fa26 79 }
Siriagus 8:9ac6a428fa26 80
Siriagus 8:9ac6a428fa26 81 // Loop through bullets and move them
Siriagus 8:9ac6a428fa26 82 for (std::vector<Point*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
Siriagus 8:9ac6a428fa26 83 {
Siriagus 8:9ac6a428fa26 84 (*it)->x -= 4;
Siriagus 8:9ac6a428fa26 85
Siriagus 8:9ac6a428fa26 86 // TODO: Check for collisions
Siriagus 8:9ac6a428fa26 87 // TODO: Go both ways
Siriagus 8:9ac6a428fa26 88 // TODO: Delete the pointers on erase!
Siriagus 8:9ac6a428fa26 89 }
Siriagus 7:678873947b29 90 }
Siriagus 7:678873947b29 91
Siriagus 7:678873947b29 92 void Game::render()
Siriagus 7:678873947b29 93 {
Siriagus 8:9ac6a428fa26 94
Siriagus 8:9ac6a428fa26 95 // Draw player - TODO: Make this a part of sprite class (so they can draw themselves)
Siriagus 8:9ac6a428fa26 96 int x0, x1, y0, y1;
Siriagus 8:9ac6a428fa26 97 x0 = (player.x < 0) ? 0 : player.x; // x0 = max(0,x);
Siriagus 8:9ac6a428fa26 98 y0 = (player.y < 0) ? 0 : player.y; // y0 = max(0,y);
Siriagus 8:9ac6a428fa26 99 x1 = (player.width + player.x > WIDTH) ? WIDTH : player.width + player.x; //x1 = min(WIDTH, width);
Siriagus 8:9ac6a428fa26 100 y1 = (player.height + player.y > HEIGHT) ? HEIGHT : player.height + player.y; //y1 = min(HEIGHT, height);
Siriagus 8:9ac6a428fa26 101
Siriagus 8:9ac6a428fa26 102 for (int i = y0; i < y1; ++i)
Siriagus 8:9ac6a428fa26 103 {
Siriagus 8:9ac6a428fa26 104 for (int j = x0; j < x1; ++j)
Siriagus 8:9ac6a428fa26 105 {
Siriagus 8:9ac6a428fa26 106 if (Image::Player[i-y0][j-x0])
Siriagus 8:9ac6a428fa26 107 lcd->setPixel(j,i);
Siriagus 8:9ac6a428fa26 108 }
Siriagus 8:9ac6a428fa26 109 }
Siriagus 8:9ac6a428fa26 110
Siriagus 8:9ac6a428fa26 111 // bullets
Siriagus 8:9ac6a428fa26 112 for (std::vector<Point*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
Siriagus 8:9ac6a428fa26 113 {
Siriagus 8:9ac6a428fa26 114 int x, y;
Siriagus 8:9ac6a428fa26 115 x = (*it)->x;
Siriagus 8:9ac6a428fa26 116 y = (*it)->y;
Siriagus 8:9ac6a428fa26 117
Siriagus 8:9ac6a428fa26 118
Siriagus 8:9ac6a428fa26 119 if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) // Boundary check
Siriagus 8:9ac6a428fa26 120 {
Siriagus 8:9ac6a428fa26 121 lcd->setPixel(x,y);
Siriagus 8:9ac6a428fa26 122 }
Siriagus 8:9ac6a428fa26 123 // else // TODO: delete bullet - maybe iterate backwards?
Siriagus 8:9ac6a428fa26 124 }
Siriagus 7:678873947b29 125 }