#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "MillisTimer.h"
#include "GameObject.h"
#include "AlienFleet.h"
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C//0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define MAX_NUM_BULLETS 6
const uint8_t LEFT_BUTTON = 4;
const uint8_t RIGHT_BUTTON = 5;
const uint8_t TRIGGER_BUTTON = 2;
const uint8_t SPEAKER_PIN = 10;
bool weapon_fired = false;
bool explosion = false;
bool spaceship_move = false;
bool soundActivated = false;
uint16_t weapon_fired_length = 3;
uint16_t explosion_length = 4;
uint16_t spaceship_move_length = 2;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
typedef enum game_state_t {
WAITING_TO_START,
START_LEVEL,
GAME_ON,
GAME_OVER
} game_state_t;
game_state_t game_state = WAITING_TO_START;
const uint8_t space_ship[] PROGMEM = {
0b00011000,
0b00011000,
0b10011001,
0b10111101,
0b11011011,
0b01111110,
0b00111100,
0b00011000,
};
//
// GameObject(uint8_t x_pos, uint8_t y_pos,
// uint8_t *graphic, uint8_t width,
// uint8_t height, Adafruit_SSD1306 &display)
GameObject spaceShip(10, 55, space_ship, 8,8, &display);
const unsigned char alien_ship[] PROGMEM = {
0b00100000, 0b01000000,
0b00010000, 0b10000000,
0b00001001, 0b00000000,
0b00011111, 0b10000000,
0b00111111, 0b11000000,
0b00110110, 0b11000000,
0b00011111, 0b10000000,
0b00110000, 0b11000000,
0b01100000, 0b01100000,
0b11000000, 0b00110000
};
const uint8_t bullet[] PROGMEM = {
0xC0,
0xC0,
0xC0,
0xC0
};
//GameObject alienFleet[NUM_ALIEN_ROWS][NUM_ALIEN_COLUMNS];
AlienFleet alienFleet(4, 10, alien_ship, 12,10, &display);
GameObject bullets[MAX_NUM_BULLETS];
void draw_alien_fleet();
void move_alien_fleet();
void move_space_ship();
void move_bullets();
void soundLoop();
void moveLoop();
void renderLoop();
void controlLoop();
void init_buttons();
void game_loop();
bool poll_for_input();
void render_load_screen();
void render_game_over_screen();
void render_start_level_screen();
void draw_score();
void reset_game();
void end_game();
uint16_t currentScore = 0;
uint8_t currentLevel = 0;
MillisTimer createBulletTimer(400);
MillisTimer alienMoveTimer(200);
MillisTimer spaceShipMoveTimer(100);
//refresh rate display 30fps -- 1000/30
MillisTimer displayUpdateTimer(33);
MillisTimer soundUpdateTimer(10);
void setup() {
Serial.begin(9600);
//Serial.println(ALIEN_STATUS_ARRAY_SIZE);
init_buttons();
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
// Clear the buffer
display.clearDisplay();
display.display();
for(int i = 0; i < MAX_NUM_BULLETS; i++) {
bullets[i] = GameObject(0, 0, bullet, 2, 4, &display);
bullets[i].set_visibility(false);
bullets[i].set_y_speed(5);
bullets[i].set_x_speed(0);
}
render_load_screen();
}
void loop() {
switch(game_state) {
case WAITING_TO_START:
if(poll_for_input()) {
game_state = START_LEVEL;
//set score to 0
reset_game();
}
break;
case START_LEVEL:
currentLevel++;
set_level(currentLevel);
render_start_level_screen();
delay(1000);
game_state = GAME_ON;
break;
case GAME_ON:
game_loop();
break;
case GAME_OVER:
noTone(SPEAKER_PIN);
if(poll_for_input()) {
game_state = WAITING_TO_START;
render_load_screen();
delay(500);
}
break;
default:
Serial.println("ERROR: unsupported game state");
}
}
void game_loop() {
controlLoop();
moveLoop();
if(game_state == GAME_ON) {
renderLoop();
if(alienFleet.get_num_active_aliens() == 0) {
game_state = START_LEVEL;
}
}
soundLoop();
delay(1);
}
void soundLoop() {
static uint16_t counter = 0;
if(soundUpdateTimer.timeUp() || soundActivated) {
soundUpdateTimer.reset();
if(soundActivated) {
soundActivated = false;
if(weapon_fired) {
counter = weapon_fired_length;
tone(SPEAKER_PIN,800);
} else if(explosion){
tone(SPEAKER_PIN,50);
counter = explosion_length;
} else {
tone(SPEAKER_PIN,200);
counter = spaceship_move_length;
}
}
counter--;
if(counter <= 0 && (weapon_fired || explosion || spaceship_move)) {
weapon_fired = false;
explosion = false;
spaceship_move = false;
noTone(SPEAKER_PIN);
}
}
}
void reset_game() {
//set all bullets to inactive
for(int i = 0; i < MAX_NUM_BULLETS; i++) {
bullets[i].set_visibility(false);
}
//set all aliens to active
alienFleet.reset_aliens();
//reset the alien position
alienFleet.set_x_pos(4);
alienFleet.set_y_pos(10);
//reset the spaceship positions
spaceShip.set_x_pos(10);
spaceShip.set_y_pos(55);
//reset speeds
alienFleet.set_x_speed(1);
alienFleet.set_y_speed(1);
spaceShip.set_x_speed(5);
spaceShip.set_y_speed(0);
currentScore = 0;
currentLevel = 0;
}
void set_level(uint8_t currentLevel) {
//set all bullets to inactive
for(int i = 0; i < MAX_NUM_BULLETS; i++) {
bullets[i].set_visibility(false);
}
//set all aliens to active
alienFleet.reset_aliens();
//reset the alien position
alienFleet.set_x_pos(4);
alienFleet.set_y_pos(10);
//reset the spaceship positions
spaceShip.set_x_pos(10);
spaceShip.set_y_pos(55);
//reset speeds
spaceShip.set_x_speed(5);
spaceShip.set_y_speed(0);
alienFleet.set_x_speed(currentLevel);
alienFleet.set_y_speed(1);
}
bool createBullet(uint8_t x_pos) {
for(int i = 0; i < MAX_NUM_BULLETS; i++) {
if(!bullets[i].get_visibility()) {
bullets[i].set_visibility(true);
bullets[i].set_x_pos(x_pos);
bullets[i].set_y_pos(60);
soundActivated = true;
weapon_fired = true;
return true;
}
}
return false;
}
int currentMoveX = NONE;
int currentMoveY = NONE;
void controlLoop() {
static bool fireWeapon = false;
//if the weapon was fired
if(fireWeapon) {
fireWeapon = false;
//create the bullet at the current spaceship position
if(createBullet(spaceShip.get_x_pos() + spaceShip.get_width()/2 - bullets[0].get_width()/2)) {
Serial.println("bullet created");
}
}
//read movement buttons
if(!digitalRead(LEFT_BUTTON)) {
soundActivated = true;
spaceship_move = true;
currentMoveX = MOVE_LEFT;
} else if(!digitalRead(RIGHT_BUTTON)) {
soundActivated = true;
spaceship_move = true;
currentMoveX = MOVE_RIGHT;
} else {
currentMoveX = NONE;
}
//read trigger if enough time has passed
if(!digitalRead(TRIGGER_BUTTON) && createBulletTimer.timeUp()) {
createBulletTimer.reset();
fireWeapon = true;
}
}
void check_bullet_collisions() {
for(int i = 0; i < MAX_NUM_BULLETS; i++) {
if(bullets[i].get_visibility()) {
bool collision = alienFleet.collidesWithAlien(bullets[i]);
if(collision) {
bullets[i].set_visibility(false);
currentScore++;
soundActivated = true;
explosion = true;
//set explosion animation, get position from bullet position
}
}
}
}
void moveLoop() {
if(alienMoveTimer.timeUp()) {
alienMoveTimer.reset();
move_alien_fleet();
check_bullet_collisions();
}
if(spaceShipMoveTimer.timeUp()) {
spaceShipMoveTimer.reset();
move_space_ship();
move_bullets();
}
}
void draw_bullets() {
for(int i = 0; i < MAX_NUM_BULLETS; i++) {
bullets[i].draw(WHITE);
// Serial.print(i);
// Serial.print(": ");
// Serial.print(bullets[i].get_visibility());
// Serial.print(" ");
// Serial.print(" (");
// Serial.print(bullets[i].get_x_pos());
// Serial.print(",");
// Serial.print(bullets[i].get_y_pos());
// Serial.print(") ");
}
// Serial.println();
}
//render the game objects on the display
void renderLoop() {
//every x ms
if(displayUpdateTimer.timeUp()) {
displayUpdateTimer.reset();
//clear display
display.clearDisplay();
//draw game objects
draw_alien_fleet();
spaceShip.draw(WHITE);
draw_bullets();
draw_score();
//show game objects on display
display.display();
}
}
// WIDTH * ((HEIGHT + 7) / 8)
// 128 * (64 + 7) / 8 -- 1136
// 2048 - 830 - 1136 -- 82
void draw_alien_fleet() {
alienFleet.draw(WHITE);
}
void move_space_ship() {
spaceShip.move_horizontally_stop_at_wall(currentMoveX);
//check for collision
if(alienFleet.collidesWithAlien(spaceShip)) {
end_game();
}
}
void move_alien_fleet() {
static bool at_edge = false;
static int direction = MOVE_RIGHT;
if(at_edge) {
at_edge = false;
direction = -1 * direction;
if(alienFleet.move_fleet_vertically(MOVE_DOWN)) {
end_game();
}
}
at_edge = alienFleet.move_fleet_horizontally(direction);
}
void move_bullets() {
for(int i = 0; i < MAX_NUM_BULLETS; i++) {
if(bullets[i].move_vertically(MOVE_UP)) {
bullets[i].set_visibility(false);
}
}
}
void init_buttons() {
pinMode(LEFT_BUTTON, INPUT_PULLUP);
pinMode(RIGHT_BUTTON, INPUT_PULLUP);
pinMode(TRIGGER_BUTTON, INPUT_PULLUP);
}
bool poll_for_input() {
return !digitalRead(LEFT_BUTTON) ||
!digitalRead(RIGHT_BUTTON) ||
!digitalRead(TRIGGER_BUTTON);
}
void render_load_screen() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(10, 10);
display.print("ALIEN");
display.setCursor(50, 28);
display.print("ATTACK");
display.setTextSize(1);
display.setCursor(1, 50);
display.print("press button to begin");
display.display();
}
void render_game_over_screen() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(5, 10);
display.print("GAME OVER");
display.setTextSize(1);
display.setCursor(50, 28);
display.print("SCORE: ");
display.print(currentScore);
display.setCursor(1, 45);
display.print("press button");
display.setCursor(1, 55);
display.print("to continue");
display.display();
}
void end_game() {
game_state = GAME_OVER;
render_game_over_screen();
}
void draw_score() {
display.setTextSize(1);
display.setCursor(0, 0);
display.print(currentScore);
}
void render_start_level_screen() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(10, 10);
display.print("LEVEL ");
display.print(currentLevel);
display.setTextSize(1);
display.setCursor(50, 28);
display.print("SCORE: ");
display.print(currentScore);
display.display();
}