Skip to content

Commit 9dd0964

Browse files
Refactored examples, added 2 example games
1 parent c7d483e commit 9dd0964

13 files changed

Lines changed: 870 additions & 0 deletions

main/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,16 @@ idf_component_register(SRCS
1313
"src/Shapes/Point.cpp"
1414
"src/Shapes/RegularPolygon.cpp"
1515
"src/Shapes/Collection.cpp"
16+
"examples/SolarSystem.cpp"
17+
"examples/TestShapes.cpp"
18+
"examples/CollisionTest.cpp"
19+
"examples/Fire.cpp"
20+
"examples/Platformer.cpp"
21+
"examples/SpaceShooter.cpp"
1622
INCLUDE_DIRS
1723
"include"
1824
"include/Shapes"
25+
"include/examples"
1926
REQUIRES ESP32-HUB75-MatrixPanel-I2S-DMA
2027
PRIV_REQUIRES esp_timer perfmon)
2128

main/examples/CollisionTest.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include "examples/CollisionTest.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+
void runCollisionTest() {
27+
const int width = 64;
28+
const int height = 64;
29+
Renderer renderer(width, height, Color(0, 0, 0, 1.0f));
30+
Collection *mainCollection =
31+
new Collection(ShapeParams{32, 32, Color(0, 0, 0, 1.0f), 0});
32+
33+
RegularPolygon *triangle = new RegularPolygon(
34+
RegularPolygonRadiusParams{32, 60, Color(0, 255, 0, 1.0f), 3, 4, true});
35+
Rectangle *enemy = new Rectangle(
36+
RectangleParams{20, 0, Color(255, 0, 0, 1.0f), 10, 10, true});
37+
triangle->addCollider();
38+
enemy->addCollider();
39+
40+
mainCollection->addShape(triangle);
41+
mainCollection->addShape(enemy);
42+
43+
DrawOptions options = {width, height, true}; // Anti-aliased
44+
HUB75Display display(width, height);
45+
if (!display.isInitialized()) {
46+
printf("Display not initialized. Skipping display output.\n");
47+
return;
48+
}
49+
50+
printf("Triangle Collider position: (%d, %d)\n", triangle->getCollider()->x,
51+
triangle->getCollider()->y);
52+
Pixels pixels;
53+
while (true) {
54+
pixels.clear();
55+
56+
enemy->translate(0, 1);
57+
58+
if (triangle->intersects(enemy)) {
59+
triangle->setColor(Color(255, 0, 0, 1.0f)); // Red on collision
60+
printf("Collision detected at enemy position (%d, %d)\n",
61+
enemy->getCollider()->x, enemy->getCollider()->y);
62+
} else {
63+
triangle->setColor(Color(0, 255, 0, 1.0f)); // Green otherwise
64+
}
65+
66+
renderer.render(pixels, std::vector<Collection *>{mainCollection},
67+
options);
68+
display.setBuffer(pixels);
69+
vTaskDelay(pdMS_TO_TICKS(100));
70+
}
71+
}

main/examples/Fire.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#include "examples/Fire.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+
void runFire() {
27+
28+
const int width = 64;
29+
30+
const int height = 64;
31+
32+
const int COOLING =
33+
20; // Value from 0-100. Higher value means faster cooling.
34+
35+
const int SPARKING =
36+
255; // Value from 0-255. Higher value means more sparks.
37+
38+
const int FPS = 30;
39+
40+
HUB75Display display(width, height);
41+
42+
if (!display.isInitialized()) {
43+
44+
printf("Display not initialized. Skipping display output.\n");
45+
46+
return;
47+
}
48+
49+
// 2D buffer for fire intensity values, allocated on the heap
50+
std::vector<uint8_t> fire(width * height, 0);
51+
52+
// Fire color palette (Black -> Red -> Yellow -> White)
53+
std::vector<Color> palette(256);
54+
for (int i = 0; i < 256; i++) {
55+
if (i < 32) { // Black to Red
56+
palette[i] = Color(i * 8, 0, 0);
57+
} else if (i < 96) { // Red to Yellow
58+
palette[i] = Color(255, (i - 32) * 4, 0);
59+
} else if (i < 160) { // Yellow to White
60+
palette[i] = Color(255, 255, (i - 96) * 4);
61+
} else { // White
62+
palette[i] = Color(255, 255, 255);
63+
}
64+
}
65+
66+
Pixels pixels;
67+
while (true) {
68+
// 1. Seed the fire source at the bottom
69+
for (int x = 0; x < width; x++) {
70+
if (rand() % 255 < SPARKING) {
71+
fire[(height - 1) * width + x] = rand() % 256;
72+
}
73+
}
74+
75+
// 2. Propagate fire upwards and cool it down
76+
for (int y = 0; y < height - 1; y++) {
77+
for (int x = 0; x < width; x++) {
78+
// Average surrounding pixels from below
79+
uint8_t new_val =
80+
(fire[(y + 1) * width + ((x - 1 + width) % width)] +
81+
fire[(y + 1) * width + x] +
82+
fire[(y + 1) * width + ((x + 1) % width)] +
83+
fire[((y + 2) % height) * width + x]) /
84+
4;
85+
86+
// Cooling
87+
int random_cool = rand() % (COOLING / 4 + 1);
88+
89+
fire[y * width + x] =
90+
(new_val > random_cool) ? new_val - random_cool : 0;
91+
}
92+
}
93+
94+
// Blur the bottom row a bit to make it less "column-y"
95+
for (int x = 0; x < width; x++) {
96+
int y = height - 1;
97+
fire[y * width + x] = (fire[y * width + x] +
98+
fire[y * width + ((x - 1 + width) % width)] +
99+
fire[y * width + ((x + 1) % width)]) /
100+
3;
101+
}
102+
103+
// 3. Render fire buffer to the display buffer
104+
pixels.clear();
105+
for (int y = 0; y < height; y++) {
106+
107+
for (int x = 0; x < width; x++) {
108+
109+
uint8_t temp = fire[y * width + x];
110+
111+
if (temp > 0) {
112+
113+
pixels.push_back(Pixel(x, y, palette[temp]));
114+
}
115+
}
116+
}
117+
118+
display.setBuffer(pixels);
119+
vTaskDelay(pdMS_TO_TICKS(1000 / FPS));
120+
}
121+
}

main/examples/Platformer.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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

Comments
 (0)