SpaceInvaders

This commit is contained in:
Patrick Haßel 2021-12-26 23:01:43 +01:00
parent aad95e9292
commit 9eb7d17d6f
5 changed files with 256 additions and 10 deletions

View File

@ -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)

View File

@ -3,6 +3,9 @@
#include <Arduino.h>
#define X true
#define _ false
typedef unsigned long millis_t;
#endif

View File

@ -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());
}

View File

@ -3,9 +3,6 @@
#include "mode/Mode.h"
#define X true
#define _ false
class Clock : public Mode {
private:

View File

@ -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