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:
Sun May 03 11:48:42 2015 +0000
Revision:
9:da608ae65df9
Parent:
8:9ac6a428fa26
Child:
11:adb68da98262
Player movement + bullets added

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