|
| 1 | +#include "examples/Platformer.hpp" |
| 2 | +#include "LineSegment.hpp" |
| 3 | +#include "Point.hpp" |
| 4 | +#include "Polygon.hpp" |
| 5 | +#include "Profiler.hpp" |
| 6 | +#include "RegularPolygon.hpp" |
| 7 | +#include "Renderer.hpp" |
| 8 | +#include "Shapes/Circle.hpp" |
| 9 | +#include "Shapes/Collection.hpp" |
| 10 | +#include "Shapes/Rectangle.hpp" |
| 11 | +#include "Utils.hpp" |
| 12 | +#include "driver/gpio.h" |
| 13 | +#include "esp_log.h" |
| 14 | +#include "esp_timer.h" |
| 15 | +#include "freertos/FreeRTOS.h" |
| 16 | +#include "freertos/idf_additions.h" |
| 17 | +#include "freertos/projdefs.h" |
| 18 | +#include "freertos/task.h" |
| 19 | +#include "portmacro.h" |
| 20 | +#include <cstdint> |
| 21 | +#include <cstdio> |
| 22 | +#include <cstdlib> |
| 23 | +#include <stdio.h> |
| 24 | +#include <vector> |
| 25 | + |
| 26 | +// Copied from main.cpp, required by the examples |
| 27 | +void init_input_gpio(int gpio_num) { |
| 28 | + gpio_config_t io_conf = {.pin_bit_mask = (1ULL << gpio_num), |
| 29 | + .mode = GPIO_MODE_INPUT, |
| 30 | + .pull_up_en = GPIO_PULLUP_ENABLE, |
| 31 | + .pull_down_en = GPIO_PULLDOWN_DISABLE, |
| 32 | + .intr_type = GPIO_INTR_DISABLE}; |
| 33 | + gpio_config(&io_conf); |
| 34 | +} |
| 35 | + |
| 36 | +void runPlatformer() { |
| 37 | +#define INPUT_LEFT_GPIO GPIO_NUM_19 |
| 38 | +#define INPUT_CENTER_GPIO GPIO_NUM_20 |
| 39 | +#define INPUT_RIGHT_GPIO GPIO_NUM_21 |
| 40 | +#define TAG "PLATFORMER" |
| 41 | + |
| 42 | + init_input_gpio(INPUT_LEFT_GPIO); |
| 43 | + init_input_gpio(INPUT_CENTER_GPIO); |
| 44 | + init_input_gpio(INPUT_RIGHT_GPIO); |
| 45 | + |
| 46 | + ESP_LOGI(TAG, "Starting Platformer..."); |
| 47 | + |
| 48 | + // --- Game Setup --- |
| 49 | + const int width = 64; |
| 50 | + const int height = 64; |
| 51 | + Renderer renderer(width, height, |
| 52 | + Color(40, 40, 80, 1.0f)); // Dark purple-blue sky |
| 53 | + DrawOptions options = {width, height, |
| 54 | + false}; // No anti-aliasing for blocky style |
| 55 | + |
| 56 | + HUB75Display display(width, height); |
| 57 | + if (!display.isInitialized()) { |
| 58 | + ESP_LOGE(TAG, "Display not initialized. Halting."); |
| 59 | + return; |
| 60 | + } |
| 61 | + |
| 62 | + Collection *mainCollection = |
| 63 | + new Collection(ShapeParams{0, 0, Color(0, 0, 0, 0), 0}); |
| 64 | + |
| 65 | + // --- Player Setup --- |
| 66 | + Rectangle *player = new Rectangle( |
| 67 | + RectangleParams{10, 48, Color(255, 80, 80, 1.0f), 5, 8, true}); |
| 68 | + player->addCollider(); |
| 69 | + mainCollection->addShape(player); |
| 70 | + |
| 71 | + // --- Platform Setup --- |
| 72 | + std::vector<Rectangle *> platforms; |
| 73 | + |
| 74 | + auto createPlatform = [&](int x, int y, int w, int h, Color c) { |
| 75 | + Rectangle *p = new Rectangle(RectangleParams{x, y, c, w, h, true}); |
| 76 | + p->addCollider(); |
| 77 | + platforms.push_back(p); |
| 78 | + mainCollection->addShape(p); |
| 79 | + }; |
| 80 | + |
| 81 | + Color platformColor(100, 180, 80, 1.0f); |
| 82 | + createPlatform(0, 56, 64, 8, platformColor); // Ground |
| 83 | + createPlatform(20, 46, 24, 5, platformColor); // Middle platform |
| 84 | + createPlatform(0, 28, 18, 5, platformColor); // Left platform |
| 85 | + createPlatform(46, 28, 18, 5, platformColor); // Right platform |
| 86 | + createPlatform(22, 14, 20, 5, platformColor); // Top-middle platform |
| 87 | + |
| 88 | + // --- Physics and Game State --- |
| 89 | + float velocityX = 0.0f; |
| 90 | + float velocityY = 0.0f; |
| 91 | + const float gravity = 0.25f; |
| 92 | + const float jumpForce = -3.8f; |
| 93 | + const float moveSpeed = 1.8f; |
| 94 | + bool canJump = false; |
| 95 | + |
| 96 | + Pixels pixels; |
| 97 | + |
| 98 | + // --- Game Loop --- |
| 99 | + while (1) { |
| 100 | + uint64_t frameStartTime = esp_timer_get_time(); |
| 101 | + |
| 102 | + // --- Input Handling --- |
| 103 | + bool left_pressed = !gpio_get_level(INPUT_LEFT_GPIO); |
| 104 | + bool right_pressed = !gpio_get_level(INPUT_RIGHT_GPIO); |
| 105 | + bool jump_pressed = !gpio_get_level(INPUT_CENTER_GPIO); |
| 106 | + |
| 107 | + velocityX = 0; |
| 108 | + if (left_pressed) { |
| 109 | + velocityX = -moveSpeed; |
| 110 | + } |
| 111 | + if (right_pressed) { |
| 112 | + velocityX = moveSpeed; |
| 113 | + } |
| 114 | + if (jump_pressed && canJump) { |
| 115 | + velocityY = jumpForce; |
| 116 | + canJump = false; |
| 117 | + } |
| 118 | + |
| 119 | + // --- Physics (Separated Axis) --- |
| 120 | + |
| 121 | + // 1. Horizontal Movement |
| 122 | + player->translate(velocityX, 0); |
| 123 | + |
| 124 | + // 2. Vertical Movement & Collision |
| 125 | + velocityY += gravity; |
| 126 | + float oldPlayerY = |
| 127 | + player->getY(); // Store position before vertical movement |
| 128 | + player->translate(0, velocityY); |
| 129 | + canJump = false; |
| 130 | + for (auto *platform : platforms) { |
| 131 | + if (player->intersects(platform)) { |
| 132 | + // Only land on the platform if the player was above it in the |
| 133 | + // previous frame |
| 134 | + if (velocityY > 0 && |
| 135 | + oldPlayerY + player->getHeight() <= platform->getY() + 1) { |
| 136 | + player->setPosition(player->getX(), |
| 137 | + platform->getY() - player->getHeight()); |
| 138 | + velocityY = 0; |
| 139 | + canJump = true; // Landed on a platform |
| 140 | + } |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + // --- Screen Boundaries & Respawn --- |
| 145 | + if (player->getX() < 0) { |
| 146 | + player->setPosition(0, player->getY()); |
| 147 | + } |
| 148 | + if (player->getX() + player->getWidth() > width) { |
| 149 | + player->setPosition(width - player->getWidth(), player->getY()); |
| 150 | + } |
| 151 | + if (player->getY() > height) { // Fell off the bottom |
| 152 | + player->setPosition(10, 48); |
| 153 | + velocityY = 0; |
| 154 | + } |
| 155 | + |
| 156 | + // --- Rendering --- |
| 157 | + pixels.clear(); |
| 158 | + renderer.render(pixels, std::vector<Collection *>{mainCollection}, |
| 159 | + options); |
| 160 | + display.setBuffer(pixels); |
| 161 | + |
| 162 | + // --- Frame Rate Control --- |
| 163 | + const uint64_t TARGET_FRAME_TIME_US = 1000000 / 30; // 50 FPS |
| 164 | + uint64_t frameEndTime = esp_timer_get_time(); |
| 165 | + uint64_t frameTime = frameEndTime - frameStartTime; |
| 166 | + if (frameTime < TARGET_FRAME_TIME_US) { |
| 167 | + uint64_t remainingTime = TARGET_FRAME_TIME_US - frameTime; |
| 168 | + vTaskDelay(pdMS_TO_TICKS(remainingTime / 1000)); |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + // Cleanup (won't be reached in this infinite loop) |
| 173 | + delete mainCollection; |
| 174 | +} |
0 commit comments