Загрузка данных


#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// --- ПИНЫ ---
#define JOY_X A0
#define JUMP_BTN 2
#define BUZZER 8

// --- ИГРОК ---
struct Player {
  float x, y, vx, vy;
  int w, h;
  bool onGround;
};
Player player;

// --- ПЛАТФОРМЫ ---
struct Platform {
  int x, y, w;
  int speed; // 0 = статичная, иначе движется
};
const int NUM_PLATFORMS = 10;
Platform platforms[NUM_PLATFORMS];

// --- ШИПЫ ---
struct Spike { int x, y; bool active; };
const int NUM_SPIKES = 5;
Spike spikes[NUM_SPIKES];

// --- МОНЕТЫ ---
struct Coin { int x, y; bool collected; };
const int NUM_COINS = 5;
Coin coins[NUM_COINS];

// --- ФИЗИКА ---
const float GRAVITY = 0.4;
const float JUMP_FORCE = -6.0;
const float MOVE_SPEED = 2.0;
const float FRICTION = 0.85;

// --- СОСТОЯНИЯ ---
enum GameState { STATE_PLAYING, STATE_DEAD, STATE_WIN };
GameState currentState = STATE_PLAYING;

// --- ПЕРЕМЕННЫЕ ---
unsigned long lastUpdate = 0;
int lastJumpState = HIGH;
int currentLevel = 0;
int score = 0;

void setup() {
  Serial.begin(9600);
  pinMode(JUMP_BTN, INPUT_PULLUP);
  pinMode(BUZZER, OUTPUT);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED FAIL"));
    for (;;);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println(F("PLATFORMER v2.0"));
  display.println(F("5 Levels"));
  display.println(F("Spikes + Coins"));
  display.display();
  delay(1500);

  loadLevel(0);
}

void loop() {
  if (millis() - lastUpdate < 16) return;
  lastUpdate = millis();

  switch (currentState) {
    case STATE_PLAYING:
      handleInput();
      updatePlatforms();
      updatePhysics();
      checkCollisions();
      checkSpikes();
      checkCoins();
      checkWinCondition();
      render();
      break;
    case STATE_DEAD:
      display.clearDisplay();
      display.setCursor(25, 25);
      display.setTextSize(2);
      display.print(F("GAME OVER"));
      display.display();
      delay(1500);
      loadLevel(currentLevel);
      break;
    case STATE_WIN:
      display.clearDisplay();
      display.setCursor(30, 25);
      display.setTextSize(2);
      display.print(F("YOU WIN"));
      display.display();
      delay(1500);
      currentLevel++;
      if (currentLevel >= 5) currentLevel = 0;
      loadLevel(currentLevel);
      break;
  }
}

void loadLevel(int level) {
  currentState = STATE_PLAYING;
  player.x = 10;
  player.y = 40;
  player.vx = 0;
  player.vy = 0;
  player.w = 6;
  player.h = 8;
  player.onGround = false;
  score = 0;

  for (int i = 0; i < NUM_PLATFORMS; i++) platforms[i] = {0, 0, 0, 0};
  for (int i = 0; i < NUM_SPIKES; i++) spikes[i] = {0, 0, false};
  for (int i = 0; i < NUM_COINS; i++) coins[i] = {0, 0, false};

  if (level == 0) {
    // Уровень 1: простой
    platforms[0] = {0, 56, 40, 0};
    platforms[1] = {50, 48, 30, 0};
    platforms[2] = {90, 40, 38, 0};
  } 
  else if (level == 1) {
    // Уровень 2: шипы
    platforms[0] = {0, 56, 30, 0};
    platforms[1] = {40, 50, 25, 0};
    platforms[2] = {75, 44, 25, 0};
    platforms[3] = {105, 36, 23, 0};
    spikes[0] = {32, 52, true};
    spikes[1] = {67, 46, true};
  }
  else if (level == 2) {
    // Уровень 3: монеты
    platforms[0] = {0, 56, 25, 0};
    platforms[1] = {35, 50, 20, 0};
    platforms[2] = {65, 44, 20, 0};
    platforms[3] = {95, 38, 20, 0};
    platforms[4] = {115, 30, 13, 0};
    coins[0] = {40, 46, true};
    coins[1] = {70, 40, true};
    coins[2] = {100, 34, true};
  }
  else if (level == 3) {
    // Уровень 4: движущиеся платформы
    platforms[0] = {0, 56, 25, 0};
    platforms[1] = {40, 48, 20, 1};
    platforms[2] = {80, 40, 20, 1};
    platforms[3] = {110, 32, 18, 0};
    spikes[0] = {28, 52, true};
    spikes[1] = {75, 36, true};
    coins[0] = {45, 44, true};
    coins[1] = {85, 36, true};
  }
  else {
    // Уровень 5: всё вместе
    platforms[0] = {0, 56, 20, 0};
    platforms[1] = {30, 50, 15, 1};
    platforms[2] = {60, 42, 15, 1};
    platforms[3] = {90, 34, 15, 1};
    platforms[4] = {115, 28, 13, 0};
    spikes[0] = {22, 52, true};
    spikes[1] = {50, 46, true};
    spikes[2] = {80, 38, true};
    coins[0] = {35, 46, true};
    coins[1] = {65, 38, true};
    coins[2] = {95, 30, true};
  }

  Serial.print(F("LEVEL "));
  Serial.println(level + 1);
}

void updatePlatforms() {
  for (int i = 0; i < NUM_PLATFORMS; i++) {
    if (platforms[i].speed != 0) {
      platforms[i].x += platforms[i].speed;
      if (platforms[i].x < 10 || platforms[i].x + platforms[i].w > SCREEN_WIDTH - 10) {
        platforms[i].speed = -platforms[i].speed;
      }
    }
  }
}

void handleInput() {
  int x = analogRead(JOY_X);
  if (x < 300) player.vx = -MOVE_SPEED;
  else if (x > 700) player.vx = MOVE_SPEED;
  else player.vx *= FRICTION;

  int jumpReading = digitalRead(JUMP_BTN);
  if (jumpReading == LOW && lastJumpState == HIGH && player.onGround) {
    player.vy = JUMP_FORCE;
    player.onGround = false;
    tone(BUZZER, 800, 50);
  }
  lastJumpState = jumpReading;
}

void updatePhysics() {
  player.vy += GRAVITY;
  player.x += player.vx;
  player.y += player.vy;

  if (player.x < 0) player.x = 0;
  if (player.x + player.w > SCREEN_WIDTH) player.x = SCREEN_WIDTH - player.w;

  if (player.y > SCREEN_HEIGHT) {
    currentState = STATE_DEAD;
    tone(BUZZER, 200, 500);
    Serial.println(F("DEAD"));
  }
}

void checkCollisions() {
  player.onGround = false;
  for (int i = 0; i < NUM_PLATFORMS; i++) {
    if (platforms[i].w == 0) continue;
    if (player.x + player.w > platforms[i].x &&
        player.x < platforms[i].x + platforms[i].w) {
      int top = platforms[i].y;
      if (player.vy >= 0 && player.y + player.h >= top && player.y + player.h <= top + 10) {
        player.y = top - player.h;
        player.vy = 0;
        player.onGround = true;
      }
    }
  }
}

void checkSpikes() {
  for (int i = 0; i < NUM_SPIKES; i++) {
    if (!spikes[i].active) continue;
    if (player.x + player.w > spikes[i].x &&
        player.x < spikes[i].x + 5 &&
        player.y + player.h > spikes[i].y &&
        player.y < spikes[i].y + 4) {
      currentState = STATE_DEAD;
      tone(BUZZER, 300, 500);
      Serial.println(F("DEAD: Spike"));
    }
  }
}

void checkCoins() {
  for (int i = 0; i < NUM_COINS; i++) {
    if (!coins[i].collected) continue;
    if (player.x + player.w > coins[i].x &&
        player.x < coins[i].x + 4 &&
        player.y + player.h > coins[i].y &&
        player.y < coins[i].y + 4) {
      coins[i].collected = false;
      score++;
      tone(BUZZER, 1200, 50);
      Serial.print(F("COIN: "));
      Serial.println(score);
    }
  }
}

void checkWinCondition() {
  if (player.x + player.w >= SCREEN_WIDTH - 5) {
    currentState = STATE_WIN;
    tone(BUZZER, 1500, 200);
    Serial.println(F("WIN"));
  }
}

void render() {
  display.clearDisplay();

  for (int i = 0; i < NUM_PLATFORMS; i++) {
    if (platforms[i].w == 0) continue;
    display.fillRect(platforms[i].x, platforms[i].y, platforms[i].w, 2, SSD1306_WHITE);
  }

  for (int i = 0; i < NUM_SPIKES; i++) {
    if (!spikes[i].active) continue;
    display.fillTriangle(spikes[i].x, spikes[i].y + 4,
                         spikes[i].x + 5, spikes[i].y + 4,
                         spikes[i].x + 2, spikes[i].y, SSD1306_WHITE);
  }

  for (int i = 0; i < NUM_COINS; i++) {
    if (!coins[i].collected) continue;
    display.drawCircle(coins[i].x + 2, coins[i].y + 2, 2, SSD1306_WHITE);
  }

  display.fillRect((int)player.x, (int)player.y, player.w, player.h, SSD1306_WHITE);

  display.drawLine(SCREEN_WIDTH - 5, 0, SCREEN_WIDTH - 5, SCREEN_HEIGHT, SSD1306_WHITE);
  display.fillTriangle(SCREEN_WIDTH - 5, 5, SCREEN_WIDTH - 5, 15, SCREEN_WIDTH - 15, 10, SSD1306_WHITE);

  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print(F("L"));
  display.print(currentLevel + 1);
  display.print(F(" C:"));
  display.print(score);

  display.display();
}