#ifndef _LCD_Graphics_H #define _LCD_Graphics_H #include // Include required library #include // For `micros()` function #define HX8357_ORANGE 0xFD20 // RGB565 format // Declare the display object externally (to be initialized in main code) extern Adafruit_HX8357 tft; // Ball animation state extern int ballX, ballY; extern int ballDX, ballDY; extern uint8_t ballHue; extern unsigned long lastBallUpdate; extern int ballRadius; void initPinballScene() { tft.fillScreen(HX8357_BLACK); // Static background (only drawn once) tft.setTextColor(HX8357_YELLOW); tft.setTextSize(3); tft.setCursor(40, 20); tft.print("PINBALL"); tft.fillCircle(60, 100, 15, HX8357_RED); tft.fillCircle(160, 100, 15, HX8357_BLUE); tft.fillCircle(110, 160, 15, HX8357_GREEN); tft.fillRect(45, 200, 40, 10, HX8357_WHITE); tft.fillRect(135, 200, 40, 10, HX8357_WHITE); tft.fillTriangle(85, 200, 110, 220, 135, 200, HX8357_CYAN); tft.drawRect(20, 250, 180, 30, HX8357_RED); tft.setCursor(30, 260); tft.setTextColor(HX8357_WHITE); tft.setTextSize(2); tft.print("Score: 000000"); tft.setTextColor(HX8357_MAGENTA); tft.setTextSize(1); tft.setCursor(65, 290); tft.print("Press Any Button to Start"); } void updateBallPosition() { unsigned long now = millis(); if (now - lastBallUpdate < 30) return; // 30ms throttle for ~33 FPS lastBallUpdate = now; // Erase previous ball tft.fillCircle(ballX, ballY, ballRadius, HX8357_BLACK); // Update ball position ballX += ballDX; ballY += ballDY; // Bounce off edges if (ballX - ballRadius < 0 || ballX + ballRadius > tft.width()) ballDX = -ballDX; if (ballY - ballRadius < 0 || ballY + ballRadius > tft.height()) ballDY = -ballDY; // Redraw ball at new position tft.fillCircle(ballX, ballY, ballRadius, HX8357_WHITE); } String lcdMessage = ""; unsigned long lcdMessageTime = 0; const unsigned long lcdMessageDuration = 1000; // 1 second unsigned long testLines(uint16_t color) { unsigned long start = micros(); int x1 = 0, y1 = 0, x2, y2; int w = tft.width(); int h = tft.height(); tft.fillScreen(HX8357_BLACK); y2 = h - 1; for (x2 = 0; x2 < w; x2 += 6) tft.drawLine(x1, y1, x2, y2, color); x2 = w - 1; for (y2 = 0; y2 < h; y2 += 6) tft.drawLine(x1, y1, x2, y2, color); return micros() - start; } unsigned long testFastLines(uint16_t color1, uint16_t color2) { unsigned long start = micros(); int x, y, w = tft.width(), h = tft.height(); tft.fillScreen(HX8357_BLACK); for (y = 0; y < h; y += 5) tft.drawFastHLine(0, y, w, color1); for (x = 0; x < w; x += 5) tft.drawFastVLine(x, 0, h, color2); return micros() - start; } unsigned long testRects(uint16_t color) { unsigned long start = micros(); int n, i, i2, cx = tft.width() / 2, cy = tft.height() / 2; tft.fillScreen(HX8357_BLACK); n = min(tft.width(), tft.height()); for (i = 2; i < n; i += 6) { i2 = i / 2; tft.drawRect(cx - i2, cy - i2, i, i, color); } return micros() - start; } unsigned long testFilledRects(uint16_t color1, uint16_t color2) { unsigned long start, t = 0; int n, i, i2, cx = tft.width() / 2 - 1, cy = tft.height() / 2 - 1; tft.fillScreen(HX8357_BLACK); n = min(tft.width(), tft.height()); for (i = n; i > 0; i -= 6) { i2 = i / 2; start = micros(); tft.fillRect(cx - i2, cy - i2, i, i, color1); t += micros() - start; tft.drawRect(cx - i2, cy - i2, i, i, color2); } return t; } unsigned long testFilledCircles(uint8_t radius, uint16_t color) { unsigned long start = micros(); int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2; tft.fillScreen(HX8357_BLACK); for (x = radius; x < w; x += r2) { for (y = radius; y < h; y += r2) { tft.fillCircle(x, y, radius, color); } } return micros() - start; } unsigned long testCircles(uint8_t radius, uint16_t color) { unsigned long start = micros(); int x, y, r2 = radius * 2, w = tft.width() + radius, h = tft.height() + radius; for (x = 0; x < w; x += r2) { for (y = 0; y < h; y += r2) { tft.drawCircle(x, y, radius, color); } } return micros() - start; } unsigned long testTriangles() { unsigned long start = micros(); int n, i, cx = tft.width() / 2 - 1, cy = tft.height() / 2 - 1; tft.fillScreen(HX8357_BLACK); n = min(cx, cy); for (i = 0; i < n; i += 5) { tft.drawTriangle( cx, cy - i, // peak cx - i, cy + i, // bottom left cx + i, cy + i, // bottom right tft.color565(200, 20, i) ); } return micros() - start; } unsigned long testFilledTriangles() { unsigned long start, t = 0; int i, cx = tft.width() / 2 - 1, cy = tft.height() / 2 - 1; tft.fillScreen(HX8357_BLACK); for (i = min(cx, cy); i > 10; i -= 5) { start = micros(); tft.fillTriangle( cx, cy - i, cx - i, cy + i, cx + i, cy + i, tft.color565(0, i, i) ); t += micros() - start; tft.drawTriangle( cx, cy - i, cx - i, cy + i, cx + i, cy + i, tft.color565(i, i, 0) ); } return t; } unsigned long testRoundRects() { unsigned long start = micros(); int w, i, i2, cx = tft.width() / 2, cy = tft.height() / 2; tft.fillScreen(HX8357_BLACK); w = min(tft.width(), tft.height()); for (i = 0; i < w; i += 8) { i2 = i / 2 - 2; tft.drawRoundRect(cx - i2, cy - i2, i, i, i / 8, tft.color565(i, 100, 100)); } return micros() - start; } unsigned long testFilledRoundRects() { unsigned long start; int i, i2, cx = tft.width() / 2 + 10, cy = tft.height() / 2 + 10; tft.fillScreen(HX8357_BLACK); start = micros(); for(i=min(tft.width(), tft.height()) - 20; i>25; i-=6) { i2 = i / 2; tft.fillRoundRect(cx-i2, cy-i2, i-20, i-20, i/8, tft.color565(100, i/2, 100)); } return micros() - start; } void flashPoints(const char* message, uint16_t color) { tft.fillScreen(HX8357_BLACK); tft.setTextSize(5); tft.setTextColor(color); int y = 80; for (int i = 0; i < 5; i++) { // Reduced from 10 to 5 steps tft.fillScreen(HX8357_BLACK); // Clear for clean animation tft.setCursor(60, y + i * 6); // Move down slightly per frame tft.print(message); delay(60); // Faster transitions } tft.fillScreen(HX8357_BLACK); // Final clear after animation } void drawPinballSplashHX8357() { static int lastBallX = ballX; static int lastBallY = ballY; // Clear the previous ball (but not full screen to avoid flicker) tft.fillCircle(lastBallX, lastBallY, 6, HX8357_BLACK); // Draw static parts once (optional optimization) static bool initialized = false; if (!initialized) { tft.fillScreen(HX8357_BLACK); tft.setTextColor(HX8357_YELLOW); tft.setTextSize(3); tft.setCursor(60, 10); tft.print("PINBALL"); tft.fillCircle(60, 80, 15, HX8357_RED); tft.fillCircle(160, 80, 15, HX8357_BLUE); tft.fillCircle(110, 130, 15, HX8357_GREEN); tft.fillRect(45, 200, 40, 10, HX8357_WHITE); tft.fillRect(135, 200, 40, 10, HX8357_WHITE); tft.fillTriangle(85, 200, 110, 220, 135, 200, HX8357_CYAN); tft.drawRect(20, 250, 180, 30, HX8357_RED); tft.setCursor(30, 260); tft.setTextColor(HX8357_WHITE); tft.setTextSize(2); tft.print("Score: 000000"); initialized = true; } // Update ball hue and position ballHue += 5; lastBallX = ballX; lastBallY = ballY; ballX += ballDX; ballY += ballDY; // Bounce within screen bounds (adjust to your playfield size) if (ballX <= 20 || ballX >= 200) ballDX = -ballDX; if (ballY <= 30 || ballY >= 240) ballDY = -ballDY; // Convert HSV to RGB565 color uint16_t ballColor = tft.color565( (uint8_t)(sin(ballHue * 0.1) * 127 + 128), (uint8_t)(cos(ballHue * 0.1) * 127 + 128), (uint8_t)(sin(ballHue * 0.15 + 1) * 127 + 128) ); // Draw new ball tft.fillCircle(ballX, ballY, 6, ballColor); } void showGameOverScreen() { tft.fillScreen(HX8357_BLACK); tft.setTextSize(6); tft.setTextColor(HX8357_RED); String msg = "GAME OVER"; int16_t x1, y1; uint16_t w, h; tft.getTextBounds(msg, 0, 0, &x1, &y1, &w, &h); int x = (tft.width() - w) / 2; int y = (tft.height() - h) / 2; tft.setCursor(x, y); tft.print(msg); delay(2000); // Display for 2 seconds tft.fillScreen(HX8357_BLACK); // Clear before showing high scores } #endif // _LCD_Graphics_H