Загрузка данных
#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();
}