A simple game
Dependencies: 4DGL-uLCD-SE mbed-rtos mbed
Fork of rtos_signals by
main.cpp
- Committer:
- gmiles3
- Date:
- 2016-11-04
- Revision:
- 4:2bb5deb83b81
- Parent:
- 1:6a8fcc666593
File content as of revision 4:2bb5deb83b81:
#include "mbed.h"
#include "rtos.h"
#include <mpr121.h>
#include "uLCD_4DGL.h"
#include "Speaker.h"
uLCD_4DGL uLCD(p9,p10,p11);
int curr_x_u, curr_y_u, old_x_u, old_y_u; //user's current and old x,y coordinates
int curr_x_e, curr_y_e, old_x_e, old_y_e; //enemy's current and old x,y coordinates
float dx_e, dy_e, fx_e, fy_e; //x,y velocity of enemy and floating point representation of x,y coordinates
int curr_x_b, curr_y_b, old_x_b, old_y_b; //payload's current and old x,y coordinates
int curr_x_p, curr_y_p, old_x_p, old_y_p; //portal's current and old x,y coordinates
Mutex lcd_mutex;
Thread box; //handles user-payload collision logic
Thread portal; //handles portal logic
Speaker amp(p21);
Serial pc(USBTX, USBRX); //debugging
InterruptIn interrupt(p26);
I2C i2c(p28, p27);
Mpr121 mpr121(&i2c, Mpr121::ADD_VSS);
int score = 0;
int oldscore = 0;
volatile int key = -1; //key pressed from keypad; set via interrupts -> volatile so compiler doesn't optimize out value reads
enum State { START, LIVE, PAUSE, DEAD }; //game status
volatile State s = START;
//interrupt method that runs when keypad has a new value
void fallInterrupt() {
int key_code=0;
int i=0;
int value=mpr121.read(0x00);
value +=mpr121.read(0x01)<<8;
i=0;
for (i=0; i<12; i++) {
if (((value>>i)&0x01)==1) key_code=i+1;
}
key = key_code;
}
//handles portal logic
void portal_thread() {
while(s == LIVE || s == PAUSE) {
while(s == PAUSE);
//if payload and portal collide, score a point and randomize portal location
if (((curr_x_p-3 <= curr_x_b+3 && curr_x_p >= curr_x_b) || (curr_x_p+3 >= curr_x_b-3 && curr_x_p <= curr_x_b)) && ((curr_y_p+3 >= curr_y_b-3 && curr_y_p <= curr_y_b) || (curr_y_p-3 <= curr_y_b+3 && curr_y_p >= curr_y_b))) {
score++;
curr_x_p = (rand() % 106) + 10;
curr_y_p = (rand() % 106) + 10;
} else if (((curr_y_p-3 <= curr_y_b+3 && curr_y_p >= curr_y_b) || (curr_y_p+3 >= curr_y_b-3 && curr_y_p <= curr_y_b)) && ((curr_x_p+3 >= curr_x_b-3 && curr_x_p <= curr_x_b) || (curr_x_p-3 <= curr_x_b+3 && curr_x_p >= curr_x_b))) {
score++;
curr_x_p = (rand() % 106) + 10;
curr_y_p = (rand() % 106) + 10;
}
lcd_mutex.lock();
//redraw portal and play note if score has changed
uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
if (score != oldscore) {
amp.PlayNote(1000.0,0.025,0.5);
oldscore = score;
uLCD.rectangle(old_x_p-5, old_y_p-5, old_x_p+5, old_y_p+5, BLACK);
old_x_p = curr_x_p;
old_y_p = curr_y_p;
}
//print score
uLCD.locate(1,15);
uLCD.color(0xFF00FF);
uLCD.printf("%d", score);
lcd_mutex.unlock();
}
}
//handles user-payload collisions
void box_thread() {
while(s == LIVE) {
old_x_u = curr_x_u;
old_y_u = curr_y_u;
old_x_b = curr_x_b;
old_y_b = curr_y_b;
//if 3 pressed, then go into pause state
if (key==3+1) {
s = PAUSE;
lcd_mutex.lock();
uLCD.locate(1, 2);
uLCD.color(0xFFFF00);
uLCD.printf("PAUSE");
//wait until 3 is unpressed
while(key==3+1);
//wait until 3 is pressed
while(key!=3+1);
//wait until 3 is unpressed
while(key==3+1);
uLCD.locate(1, 2);
uLCD.color(BLACK);
uLCD.printf("PAUSE");
uLCD.color(WHITE);
//countdown from 3
for (int i=3; i>=1; i--) {
uLCD.locate(1, 2);
uLCD.printf("%2D", i);
amp.PlayNote(800.0,0.025,0.5);
wait(1);
}
uLCD.locate(1,2);
uLCD.color(BLACK);
uLCD.printf(" ");
lcd_mutex.unlock();
//play on
s = LIVE;
}
//really complicated collision logic
//basically checks each way the boxes could be colliding based on which key is pressed (which direction user is moving)
if (key==0+1) {
if ((curr_x_b+6 > curr_x_u-6 && curr_x_u > curr_x_b) && ((curr_y_u+6 > curr_y_b-6 && curr_y_u <= curr_y_b) || (curr_y_u-6 < curr_y_b+6 && curr_y_u >= curr_y_b))) {
curr_x_b-=1;
curr_x_u = curr_x_b+12;
if (curr_x_b < 7) {
curr_x_b = 25;
curr_y_b = 63;
}
} else {
curr_x_u-=1;
}
if (curr_x_u < 7) curr_x_u = 7;
} else if (key==8+1) {
if ((curr_x_b-6 < curr_x_u+6 && curr_x_u < curr_x_b) && ((curr_y_u+6 > curr_y_b-6 && curr_y_u <= curr_y_b) || (curr_y_u-6 < curr_y_b+6 && curr_y_u >= curr_y_b))) {
curr_x_b+=1;
curr_x_u = curr_x_b-12;
if (curr_x_b > 120) {
curr_x_b = 25;
curr_y_b = 63;
}
} else {
curr_x_u+=1;
}
if (curr_x_u > 120) curr_x_u = 120;
} else if (key==4+1) {
if ((curr_y_b-6 < curr_y_u+6 && curr_y_u < curr_y_b) && ((curr_x_u+6 > curr_x_b-6 && curr_x_u <= curr_x_b) || (curr_x_u-6 < curr_x_b+6 && curr_x_u >= curr_x_b))) {
curr_y_b+=1;
curr_y_u = curr_y_b-12;
if (curr_y_b > 120) {
curr_x_b = 25;
curr_y_b = 63;
}
} else {
curr_y_u+=1;
}
if (curr_y_u > 120) curr_y_u = 120;
} else if (key==5+1) {
if ((curr_y_b+6 > curr_y_u-6 && curr_y_u > curr_y_b) && ((curr_x_u+6 > curr_x_b-6 && curr_x_u <= curr_x_b) || (curr_x_u-6 < curr_x_b+6 && curr_x_u >= curr_x_b))) {
curr_y_b-=1;
curr_y_u = curr_y_b+12;
if (curr_y_b < 7) {
curr_x_b = 25;
curr_y_b = 63;
}
} else {
curr_y_u-=1;
}
if (curr_y_u < 7) curr_y_u = 7;
}
//redraw user and payload
lcd_mutex.lock();
uLCD.rectangle(20, 58, 30, 68, WHITE);
uLCD.filled_rectangle(old_x_u-5, old_y_u-5, old_x_u+5, old_y_u+5, BLACK);
uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
uLCD.filled_rectangle(old_x_b-5, old_y_b-5, old_x_b+5, old_y_b+5, BLACK);
uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
lcd_mutex.unlock();
}
}
//main thread handles setup of game, angry ball collision logic, gameover, and game resets
int main() {
//setup keypad interrupts
interrupt.fall(&fallInterrupt);
interrupt.mode(PullUp);
uLCD.baudrate(3000000);
//load in Angry Balls image from SD
uLCD.media_init();
uLCD.set_sector_address(0x003B, 0xD400);
uLCD.display_image(0,0);
//wait 3 seconds to allow keypad time to set up while player can look at the pretty image
wait(3);
uLCD.textbackground_color(WHITE);
uLCD.text_width(1);
uLCD.text_height(1);
uLCD.color(RED);
uLCD.locate(0, 0);
uLCD.printf("Angry Balls!");
uLCD.locate(0, 1);
uLCD.color(BLACK);
uLCD.printf("Press 3 to play");
//start tone
amp.PlayNote(700.0,0.2,0.5);
amp.PlayNote(1000.0,0.2,0.0);
amp.PlayNote(600.0,0.1,0.5);
amp.PlayNote(1000.0,0.1,0.0);
amp.PlayNote(700.0,0.5,0.5);
//wait until 3 is pressed
while(key != 3+1);
//wait until 3 is unpressed
while(key == 3+1);
//clear screen
uLCD.cls();
uLCD.textbackground_color(BLACK);
uLCD.background_color(BLACK);
//setup initial state for every object
curr_x_u = 9;
curr_y_u = 63;
curr_x_b = 25;
curr_y_b = 63;
curr_x_e = 75;
curr_y_e = 75;
dx_e = -0.75;
dy_e = 0.75;
curr_x_p = 110;
curr_y_p = 15;
old_x_p = curr_x_p;
old_y_p = curr_y_p;
//draw everything
uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
uLCD.color(WHITE);
uLCD.locate(1,2);
uLCD.printf("Place ball!");
//logic for placing ball before game starts based on key presses; prevents ball from going through walls & middle boundary
while(key != 3+1) {
old_x_e = curr_x_e;
old_y_e = curr_y_e;
if (key==0+1) {
curr_x_e-=2;
if (curr_x_e < 71) curr_x_e = 71;
}
if (key==8+1) {
curr_x_e+=2;
if (curr_x_e > 120) curr_x_e = 120;
}
if (key==4+1) {
curr_y_e+=2;
if (curr_y_e > 120) curr_y_e = 120;
}
if (key==5+1) {
curr_y_e-=2;
if (curr_y_e < 7) curr_y_e = 7;
}
//draw ball
uLCD.locate(1,2);
uLCD.printf("Place ball!");
uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
}
//wait until 3 is unpressed
while(key==3+1);
uLCD.locate(1,2);
uLCD.color(BLACK);
uLCD.printf("Place ball!");
fx_e = curr_x_e;
fy_e = curr_y_e;
uLCD.color(WHITE);
//countdown til start
for (int i=3; i>=1; i--) {
uLCD.locate(1, 2);
uLCD.printf("%2D", i);
amp.PlayNote(800.0,0.025,0.5);
wait(1);
}
uLCD.locate(1,2);
uLCD.color(BLACK);
uLCD.printf(" ");
//game is live, start other two threads
s = LIVE;
box.start(box_thread);
portal.start(portal_thread);
//collision logic for angry ball
//check if ball has collided with a wall, the user, or the payload (gameover)
while(s == LIVE || s == PAUSE) {
while (s==PAUSE);
old_x_e = curr_x_e;
old_y_e = curr_y_e;
if (curr_x_e < 7) {
curr_x_e = 7;
dx_e = -dx_e;
} else if (curr_x_e > 120) {
curr_x_e = 120;
dx_e = -dx_e;
} else if (curr_y_e > 120) {
curr_y_e = 120;
dy_e = -dy_e;
} else if (curr_y_e < 7) {
curr_y_e = 7;
dy_e = -dy_e;
} else if (((dx_e < 0 && curr_x_e-6 < curr_x_u+6 && curr_x_e > curr_x_u) || (dx_e > 0 && curr_x_e+6 > curr_x_u-6 && curr_x_e < curr_x_u)) && ((curr_y_e+6 > curr_y_u-6 && curr_y_e < curr_y_u) || (curr_y_e-6 < curr_y_u+6 && curr_y_e > curr_y_u))) {
dx_e = -dx_e;
} else if (((dy_e < 0 && curr_y_e-6 < curr_y_u+6 && curr_y_e > curr_y_u) || (dy_e > 0 && curr_y_e+6 > curr_y_u-6 && curr_y_e < curr_y_u)) && ((curr_x_e+6 > curr_x_u-6 && curr_x_e < curr_x_u) || (curr_x_e-6 < curr_x_u+6 && curr_x_e > curr_x_u))) {
dy_e = -dy_e;
}
if (((dx_e < 0 && curr_x_e-6 < curr_x_b+6 && curr_x_e > curr_x_b) || (dx_e > 0 && curr_x_e+6 > curr_x_b-6 && curr_x_e < curr_x_b)) && ((curr_y_e+6 > curr_y_b-6 && curr_y_e < curr_y_b) || (curr_y_e-6 < curr_y_b+6 && curr_y_e > curr_y_b))) {
s = DEAD;
} else if (((dy_e < 0 && curr_y_e-6 < curr_y_b+6 && curr_y_e > curr_y_b) || (dy_e > 0 && curr_y_e+6 > curr_y_b-6 && curr_y_e < curr_y_b)) && ((curr_x_e+6 > curr_x_b-6 && curr_x_e < curr_x_b) || (curr_x_e-6 < curr_x_b+6 && curr_x_e > curr_x_b))) {
s = DEAD;
}
//update floating point coordinates with velocity values
fx_e += dx_e;
fy_e += dy_e;
//cast floating coordinates to integer coordinates
curr_x_e = (int) fx_e;
curr_y_e = (int) fy_e;
//redraw ball
lcd_mutex.lock();
uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
lcd_mutex.unlock();
//if ball-payload collision has occurred, game is now dead
if (s == DEAD) {
//print gameover, score, instructions to start over, and play gameover sound
lcd_mutex.lock();
uLCD.cls();
uLCD.locate(1,2);
uLCD.color(RED);
uLCD.printf("GAMEOVER");
uLCD.locate(1,4);
uLCD.color(0x4B0082);
uLCD.printf("Score:%d", score);
uLCD.locate(1,8);
uLCD.color(WHITE);
uLCD.printf("Press 3 to retry");
amp.PlayNote(300.0,0.5,0.5);
amp.PlayNote(300.0,0.05,0.0);
amp.PlayNote(280.0,0.5,0.5);
amp.PlayNote(300.0,0.05,0.0);
amp.PlayNote(260.0,1.0,0.5);
//wait until 3 is pressed
while(key != 3+1);
//wait until 3 is unpressed
while(key == 3+1);
//reinitialize game state for new game; same code as above
uLCD.cls();
score = 0;
oldscore = 0;
curr_x_u = 9;
curr_y_u = 63;
curr_x_b = 25;
curr_y_b = 63;
curr_x_e = 75;
curr_y_e = 75;
dx_e = -0.75;
dy_e = 0.75;
curr_x_p = 110;
curr_y_p = 15;
old_x_p = curr_x_p;
old_y_p = curr_y_p;
uLCD.filled_rectangle(curr_x_u-5, curr_y_u-5, curr_x_u+5, curr_y_u+5, GREEN);
uLCD.filled_rectangle(curr_x_b-5, curr_y_b-5, curr_x_b+5, curr_y_b+5, DGREY);
uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
uLCD.color(WHITE);
uLCD.locate(1,2);
uLCD.printf("Place ball!");
while(key != 3+1) {
old_x_e = curr_x_e;
old_y_e = curr_y_e;
if (key==0+1) {
curr_x_e-=2;
if (curr_x_e < 71) curr_x_e = 71;
}
if (key==8+1) {
curr_x_e+=2;
if (curr_x_e > 120) curr_x_e = 120;
}
if (key==4+1) {
curr_y_e+=2;
if (curr_y_e > 120) curr_y_e = 120;
}
if (key==5+1) {
curr_y_e-=2;
if (curr_y_e < 7) curr_y_e = 7;
}
uLCD.locate(1,2);
uLCD.printf("Place ball!");
uLCD.rectangle(curr_x_p-5, curr_y_p-5, curr_x_p+5, curr_y_p+5, BLUE);
uLCD.filled_circle(old_x_e, old_y_e, 6, BLACK);
uLCD.filled_circle(curr_x_e, curr_y_e, 6, RED);
}
while(key==3+1);
uLCD.locate(1,2);
uLCD.color(BLACK);
uLCD.printf("Place ball!");
fx_e = curr_x_e;
fy_e = curr_y_e;
uLCD.color(WHITE);
for (int i=3; i>=1; i--) {
uLCD.locate(1, 2);
uLCD.printf("%2D", i);
amp.PlayNote(800.0,0.025,0.5);
wait(1);
}
uLCD.locate(1,2);
uLCD.color(BLACK);
uLCD.printf(" ");
lcd_mutex.unlock();
//game is live again
//restart the other threads
s = LIVE;
box.start(box_thread);
portal.start(portal_thread);
}
}
}
