Metroid Sidescroller Game
Overview
For our project, we made a sidescroller very loosely based on Metroid. This game uses the joystick to control the character and fire a shot at the oncoming metroids. If the metroids touch the player character, the character loses one point of health. At the loss of all points of health, the game ends. The joystick center button is used to fire the weapon, and cardinal directions are used for jumping, crouching, and movement left and right. One SD card is used for the image at the end, the other is used for the audio.

Components
- mbed LPC1768
- uLCD-144-G2 128 by 128 Smart Color LCD
- Sparkfun 5-Way Tactile Switch Breakout
- Sparkfun microSD Transflash Breakout
- Sparkfun PCB mount Speaker (8 Ohm, .1W)
- Either (2N3904 BJT) or (TI TPA2005D1 Class D Audio Amplifier)
- (Optional) 10KOhm Potentiometer for Volume Control
Wiring
| Mbed | SD Card Reader |
|---|---|
| p5 | DI |
| p6 | DO |
| p7 | SCK |
| p8 | CS |
| GND | GND |
| Vout | VCC |
| Mbed | uLCD |
|---|---|
| p9 TX | RX |
| p10 RX | TX |
| p11 | Reset |
| Vu | 5V |
| GND | GND |
| Mbed | Joystick |
|---|---|
| p12 | Up |
| p13 | Center |
| p14 | Left |
| p15 | Down |
| p16 | Right |
| GND | - |
| NC | + |
If Using Transistor and Speaker
| Mbed | BJT | Speaker |
|---|---|---|
| p18 | Base | |
| GND | Emitter | |
| Collector | - | |
| Vout | + |
If Using Audio Breakout and Potentiometer
| Mbed | Audio Breakout | Potentiometer | Speaker |
|---|---|---|---|
| p18 | In+ | ||
| GND | In-, Pwr-, | ||
| Vu | Pwr+ | ||
| Out+ | + | ||
| Out- | - | ||
| VOL pins | 1 pin/VOL pin |
Program
Main.cpp
main.cpp
#include <mbed.h>
#include "uLCD_4DGL.h"
#include "SDFileSystem.h"
#include "wave_player.h"
#include <rtos.h>
#include <graphics.h>
//define joystick
DigitalIn up(p12);
DigitalIn fire(p13);
DigitalIn left(p14);
DigitalIn down(p15);
DigitalIn right(p16);
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
//define uLCD
uLCD_4DGL ulcd(p9,p10,p11);
//define sd card reader
SDFileSystem sd(p5, p6, p7, p8, "sd");
//define speaker
AnalogOut DACout(p18);
wave_player waver(&DACout);
//Mutex for uLCD
Mutex uLCD_mutex;
FILE *wave_file;
Samus samus;
Gunshot gunshot;
Metroid metroid;
bool GameOver=0;
bool dFormula(int ux, int uy, int mx, int my)
{
double ans,dx,dy;
dx = mx-ux;
dy = my-uy;
ans = sqrt((dx*dx)+(dy*dy));
if(ans<=8) return 1;
else return 0;
}
void sound(void const *args){
while(true){
wave_file=fopen("/sd/brinstar.wav", "r");
led1 =1;
waver.play(wave_file);
led2 = 1;
fclose(wave_file);
Thread::wait(500);
}
}
int main(){
Thread soundthread(sound);
Thread::wait(1000);
int score = 0;
up.mode(PullUp);
fire.mode(PullUp);
left.mode(PullUp);
right.mode(PullUp);
down.mode(PullUp);
Thread::wait(25);
ulcd.filled_rectangle(samus.xpos,samus.ypos,samus.xpos-4,samus.ypos-samus.currentheight,0xFFA500);
ulcd.filled_circle(118, 8, 3, RED);
ulcd.filled_circle(108, 8, 3, RED);
ulcd.filled_circle(98, 8, 3, RED);
// while(1);
while(!GameOver){
ulcd.locate(0,0);
ulcd.printf("Score: %i", score);
if(samus.isJumping){
samus.prevypos = samus.ypos;
samus.prevxpos = samus.xpos;
if(samus.jump<5){
samus.ypos -= 3;
samus.jump += 1;
ulcd.filled_rectangle(samus.prevxpos+1,samus.prevypos,samus.prevxpos-5,samus.prevypos-samus.height, BLACK);
}
else if(samus.jump<10){
samus.ypos += 3;
samus.jump += 1;
ulcd.filled_rectangle(samus.prevxpos+1,samus.prevypos,samus.prevxpos-5,samus.prevypos-samus.height, BLACK);
}
else{
samus.jump = 0;
samus.isJumping = 0;
}
}
ulcd.filled_rectangle(samus.xpos,samus.ypos,samus.xpos-4,samus.ypos-samus.currentheight,0xFFA500);
if((up==0) && (!samus.crouch))
samus.isJumping = 1;
if(down==0 && (!samus.isJumping)){
samus.crouch = !samus.crouch;
if (samus.crouch==1){
samus.currentheight=samus.crouchheight;
ulcd.filled_rectangle(samus.xpos,samus.ypos,samus.xpos-4,samus.ypos-samus.height, BLACK);
ulcd.filled_rectangle(samus.xpos,samus.ypos,samus.xpos-4,samus.ypos-samus.currentheight,0xFFA500);
}
else{
samus.currentheight=samus.height;
}
}
if(right==0){
samus.prevxpos=samus.xpos;
samus.xpos=samus.xpos+1;
ulcd.filled_rectangle(samus.prevxpos, samus.prevypos, samus.prevxpos-4, samus.prevypos-samus.currentheight, BLACK);
ulcd.filled_rectangle(samus.xpos,samus.ypos,samus.xpos-4,samus.ypos-samus.currentheight,0xFFA500);
}
if(left==0){
samus.prevxpos=samus.xpos;
samus.xpos=samus.xpos-1;
ulcd.filled_rectangle(samus.prevxpos, samus.prevypos, samus.prevxpos-4, samus.prevypos-samus.currentheight, BLACK);
ulcd.filled_rectangle(samus.xpos,samus.ypos,samus.xpos-4,samus.ypos-samus.currentheight,0xFFA500);
}
if((fire==0)&&(!gunshot.isOnScreen)){
gunshot.isOnScreen = 1;
gunshot.xpos=samus.xpos;
gunshot.ypos=samus.ypos-(samus.currentheight*.5);
gunshot.prevxpos=gunshot.xpos;
gunshot.prevypos=gunshot.ypos;
}
for(int j=0; j<3;j++){
if((rand()%30 == 1)&&(!metroid.isAlive[j])){
metroid.isAlive[j] = 1;
metroid.xpos[j]=0;
metroid.ypos[j]=(123-(rand()%15));
ulcd.filled_circle(metroid.xpos[j], metroid.ypos[j], metroid.radius, WHITE);
}
}
if (gunshot.xpos<=0){
gunshot.isOnScreen=0;
ulcd.filled_circle(gunshot.xpos, gunshot.ypos, gunshot.radius, BLACK);
}
if (gunshot.isOnScreen){
ulcd.filled_circle(gunshot.xpos, gunshot.ypos, gunshot.radius, BLACK);
gunshot.Move();
ulcd.filled_circle(gunshot.xpos, gunshot.ypos, gunshot.radius, BLUE);
}
for(int j =0; j<3; j++){
if ((dFormula(gunshot.xpos,gunshot.ypos,metroid.xpos[j],metroid.ypos[j])) && (metroid.isAlive[j]) && (gunshot.isOnScreen)){
metroid.isAlive[j]=0;
gunshot.isOnScreen=0;
score++;
ulcd.filled_circle(gunshot.xpos, gunshot.ypos, gunshot.radius, BLACK);
ulcd.filled_circle(metroid.xpos[j], metroid.ypos[j], metroid.radius, BLACK);
}
}
for(int j =0; j<3; j++){
if (metroid.isAlive[j]){
ulcd.filled_circle(metroid.xpos[j],metroid.ypos[j],metroid.radius,BLACK);
metroid.Move(j);
ulcd.filled_circle(metroid.xpos[j],metroid.ypos[j],metroid.radius,WHITE);
}
}
for(int j =0; j<3; j++){
if ((metroid.isAlive[j]) && (metroid.xpos[j]>=128)){
metroid.isAlive[j] = 0;
ulcd.filled_circle(metroid.xpos[j],metroid.ypos[j],metroid.radius,BLACK);
}
}
for(int j =0; j<3; j++){
if ((dFormula(samus.xpos-2,samus.ypos-.5*samus.currentheight,metroid.xpos[j],metroid.ypos[j])) && metroid.isAlive[j]){
metroid.isAlive[j] = 0;
ulcd.filled_circle(metroid.xpos[j],metroid.ypos[j],metroid.radius,BLACK);
samus.health=samus.health-1;}
}
if (samus.health==2)
ulcd.filled_circle(118, 8, 3, BLACK);
else if (samus.health==1)
ulcd.filled_circle(108, 8, 3, BLACK);
if(samus.health==0){
GameOver=1;
soundthread.terminate();
}
}
DACout = 0.0;
Thread::wait(50);
ulcd.cls();
//ulcd.media_init();
//ulcd.set_sector_address(0x0000, 0x0000);
//ulcd.display_image(0,0);
ulcd.printf("Game Over");
Thread::wait(3000);
ulcd.cls();
FILE *fp = fopen("/sd/highscore.txt", "rw"); // open the file in 'read' mode
char c[1];
//string sc;
fgets(c,1,fp); // get a character/byte from the file
if(score > atoi(c)){
ulcd.printf("You Beat the High Score\nYour Score: %i",score);
sprintf(c,"%d",score);
fprintf(fp,c);}
else
ulcd.printf("You Did Not Beat the High Score\nHigh Score: %c",c);
fclose(fp);
}
Please log in to post comments.
