From 9eb7d17d6f4f5d68bef3cd7c88016b2979f8d5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Sun, 26 Dec 2021 23:01:43 +0100 Subject: [PATCH] SpaceInvaders --- CMakeLists.txt | 2 +- src/BASICS.h | 3 + src/main.cpp | 17 +- src/mode/Clock/Clock.h | 3 - src/mode/SpaceInvaders/SpaceInvaders.h | 241 +++++++++++++++++++++++++ 5 files changed, 256 insertions(+), 10 deletions(-) create mode 100644 src/mode/SpaceInvaders/SpaceInvaders.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a3954c4..53dc743 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,4 +30,4 @@ add_custom_target( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) -add_executable(Z_DUMMY_TARGET ${SRC_LIST} src/mode/Test/Border.h src/mode/Clock/Clock.h) +add_executable(Z_DUMMY_TARGET ${SRC_LIST} src/mode/Test/Border.h src/mode/Clock/Clock.h src/mode/SpaceInvaders/SpaceInvaders.h) diff --git a/src/BASICS.h b/src/BASICS.h index 2d3fb59..7e66f39 100644 --- a/src/BASICS.h +++ b/src/BASICS.h @@ -3,6 +3,9 @@ #include +#define X true +#define _ false + typedef unsigned long millis_t; #endif diff --git a/src/main.cpp b/src/main.cpp index 57b4a09..9329dbe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "mode/Pong/Pong.h" #include "mode/Test/Border.h" #include "mode/Clock/Clock.h" +#include "mode/SpaceInvaders/SpaceInvaders.h" enum ModeId { NONE, @@ -14,12 +15,13 @@ enum ModeId { GAME_OF_LIFE_WHITE_FADE, GAME_OF_LIFE_COLOR_FADE, GAME_OF_LIFE_RANDOM_COLOR, - PONG + PONG, + SPACE_INVADERS, }; Display display(32, 8); -ModeId newModeId = GAME_OF_LIFE_WHITE_FADE; +ModeId newModeId = SPACE_INVADERS; ModeId currentModeId = NONE; @@ -27,6 +29,10 @@ millis_t lastMillis = 0; Mode *mode = nullptr; +double speed = 1.0; + +bool connected = false; + void checkMode(); void stepMode(); @@ -47,10 +53,6 @@ void setup() { void setSpeed(double value); -double speed = 1.0; - -bool connected = false; - void loop() { bool hasIp = (uint32_t) WiFi.localIP() != 0; if (!connected) { @@ -152,6 +154,9 @@ void loadNewMode() { case PONG: mode = new Pong(&display); break; + case SPACE_INVADERS: + mode = new SpaceInvaders(&display); + break; } Serial.printf("Mode: %s\n", mode == nullptr ? "None" : mode->getName()); } diff --git a/src/mode/Clock/Clock.h b/src/mode/Clock/Clock.h index 862c5f4..e9cd4d4 100644 --- a/src/mode/Clock/Clock.h +++ b/src/mode/Clock/Clock.h @@ -3,9 +3,6 @@ #include "mode/Mode.h" -#define X true -#define _ false - class Clock : public Mode { private: diff --git a/src/mode/SpaceInvaders/SpaceInvaders.h b/src/mode/SpaceInvaders/SpaceInvaders.h new file mode 100644 index 0000000..9e07f41 --- /dev/null +++ b/src/mode/SpaceInvaders/SpaceInvaders.h @@ -0,0 +1,241 @@ +#ifndef SPACEINVADERS_H +#define SPACEINVADERS_H + +#define ROCKET_MAX 20 + +#include "mode/Mode.h" + +struct Rocket { + volatile bool alive; + uint8_t x; + uint8_t y; +}; + +struct Invader { + bool alive; + uint8_t x; + uint8_t y; +}; + +class SpaceInvaders : public Mode { + +private: + + millis_t heroRuntime = 0; + uint8_t heroX = 0; + bool heroLeft = false; + uint8_t heroShoot = 0; + + uint8_t invadersCountX; + uint8_t invadersCountY; + uint8_t invadersAlive = 0; + + millis_t swarmRuntime = 0; + uint8_t swarmY = 0; + bool swarmLeft = false; + bool swarmDown = false; + uint8_t swarmX = 0; + Invader *swarmBegin; + Invader *swarmEnd; + + millis_t rocketRuntime = 0; + Rocket *rocketsBegin; + Rocket *rocketsEnd; + +public: + + explicit SpaceInvaders(Display *display) : + Mode(display), + invadersCountX(display->width / 3), + invadersCountY(display->height / 4) { + + swarmBegin = (Invader *) malloc(sizeof(Invader) * invadersCountX * invadersCountY); + swarmEnd = swarmBegin + invadersCountX * invadersCountY; + + rocketsBegin = (Rocket *) malloc(sizeof(Rocket) * ROCKET_MAX); + rocketsEnd = rocketsBegin + ROCKET_MAX; + + reset(); + } + + ~SpaceInvaders() override { + free(swarmBegin); + free(rocketsBegin); + }; + + const char *getName() override { + return "Space Invaders"; + } + + void step(millis_t dt) override { + stepRockets(dt); + stepInvaders(dt); + collide(); + stepHero(dt); + if (invadersAlive == 0) { + Serial.println("WINNER!"); + reset(); + } + if (swarmY + (invadersCountY - 1) * 2 >= display->height - 1) { + Serial.println("GAME OVER"); + reset(); + } + draw(); + } + + void stepRockets(millis_t dt) { + rocketRuntime += dt; + if (rocketRuntime > 200) { + rocketRuntime = 0; + for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) { + if (rocket->alive) { + if (rocket->y == 0) { + rocket->alive = false; + } else { + rocket->y -= 1; + } + } + } + } + } + + void stepInvaders(millis_t dt) { + swarmRuntime += dt; + + if (swarmDown && swarmRuntime > 500) { + swarmDown = false; + swarmY++; + } + + if (swarmRuntime >= 1000) { + swarmRuntime = 0; + if (swarmLeft) { + swarmX--; + if (swarmX == 0) { + swarmLeft = false; + } + } else { + swarmX++; + if (swarmX == 3) { + swarmLeft = true; + } + } + if (swarmX == 0) { + swarmDown = true; + } + } + } + + void stepHero(millis_t dt) { + heroRuntime += dt; + if (heroRuntime >= 50) { + heroRuntime = 0; + if (heroLeft) { + heroX--; + if (heroX <= 1) { + heroLeft = false; + } + } else { + heroX++; + if (heroX >= display->width - 2) { + heroLeft = true; + } + } + + heroShoot++; + if (heroShoot >= 10) { + heroShoot = 0; + shoot(); + } + } + } + + void shoot() { + for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) { + if (!rocket->alive) { + rocket->alive = true; + rocket->x = heroX; + rocket->y = display->height - 2; + } + } + } + + void collide() { + for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) { + for (Invader *invader = swarmBegin; invader < swarmEnd; invader++) { + if (invader->alive && rocket->alive && collide(rocket, invader)) { + rocket->alive = false; + invader->alive = false; + invadersAlive--; +// Serial.printf("Killed invader at (%2u/%2u): %2u/%2u alive.\n", invader->x, invader->y, invadersAlive, invadersCountX * invadersCountY); + } + } + } + } + + bool collide(const Rocket *rocket, const Invader *invader) const { + return swarmY + invader->y * 2 == rocket->y && swarmX + invader->x * 3 <= rocket->x && rocket->x <= swarmX + invader->x * 3 + 1; + } + + void draw() { + display->clear(); + drawInvaders(); + drawRockets(); + drawHero(); + } + + void drawInvaders() { + for (Invader *invader = swarmBegin; invader < swarmEnd; invader++) { + if (invader->alive) { + display->set(swarmX + invader->x * 3 + 0, swarmY + invader->y * 2, 255, 0, 0); + display->set(swarmX + invader->x * 3 + 1, swarmY + invader->y * 2, 255, 0, 0); + } + } + } + + void drawRockets() { + for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) { + if (!rocket->alive) { + continue; + } + display->set(rocket->x, rocket->y, 255, 255, 0); + } + } + + void drawHero() { + display->set(heroX - 1, display->height - 1, 0, 0, 255); + display->set(heroX + 0, display->height - 1, 0, 0, 255); + display->set(heroX + 1, display->height - 1, 0, 0, 255); + } + + void reset() { + heroRuntime = 0; + heroLeft = false; + heroShoot = 0; + heroX = display->width / 2; + + rocketRuntime = 0; + for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) { + rocket->alive = false; + rocket->x = 0; + rocket->y = 0; + } + + swarmRuntime = 0; + invadersAlive = invadersCountX * invadersCountY; + swarmX = 0; + swarmY = 0; + swarmLeft = false; + swarmDown = false; + uint8_t n = 0; + for (Invader *invader = swarmBegin; invader < swarmEnd; invader++) { + invader->alive = true; + invader->x = n % invadersCountX; + invader->y = n / invadersCountX; + n++; + } + } + +}; + +#endif