1#include <Adafruit_GFX.h> // Core graphics library
2#include <MCUFRIEND_kbv.h>
3#include "TouchScreen.h"
4#include <Fonts/Org_01.h> //Include a different font
5#include <EEPROM.h>
6MCUFRIEND_kbv tft;
7
8/*______Define LCD pins_______*/
9#define YP A1
10#define XM A2
11#define YM 7
12#define XP 6
13#define LCD_CS A3
14#define LCD_CD A2
15#define LCD_WR A1
16#define LCD_RD A0
17#define LCD_RESET A4
18
19#define REDBAR_MINX 50
20#define GREENBAR_MINX 130
21#define BLUEBAR_MINX 180
22#define BAR_MINY 30
23#define BAR_HEIGHT 250
24#define BAR_WIDTH 30
25
26/*______Color Definitions_______*/
27#define BLACK 0x0000
28#define BLUE 0x001F
29#define RED 0xF800
30#define GREEN 0x07E0
31#define CYAN 0x07FF
32#define MAGENTA 0xF81F
33#define YELLOW 0xFFE0
34#define WHITE 0xFFFF
35#define GREY 0x8410
36#define LIME 0x07E0
37
38// --- THE PERFECT FLAPPY BIRD SKY COLOR ---
39#define SKYBLUE 0x4F19
40
41
42
43int currentpcolour;
44bool backsensed = false;
45bool resetsensed = false;
46#define MINPRESSURE 10
47#define MAXPRESSURE 1000
48
49/*____Calibrate TFT LCD_____*/
50#define TS_MINX 910
51#define TS_MINY 760
52#define TS_MAXX 180
53#define TS_MAXY 135
54
55TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
56
57#define DRAW_LOOP_INTERVAL 50
58#define addr 0
59
60int currentpage;
61int currentWing;
62int flX, flY, fallRate;
63int pillarPos, gapPosition;
64int score;
65int highScore = 0;
66bool running = false;
67bool crashed = false;
68bool scrPress = false;
69long nextDrawLoopRunTime;
70
71int redval, greenval, blueval;
72int redpos = BAR_MINY + 12;
73int greenpos = BAR_MINY + 12;
74int bluepos = BAR_MINY + 12;
75
76// --- Helper to fix black screen issue ---
77TSPoint safeGetPoint() {
78 TSPoint p = ts.getPoint();
79 pinMode(XM, OUTPUT);
80 pinMode(YP, OUTPUT);
81 pinMode(XP, OUTPUT);
82 pinMode(YM, OUTPUT);
83 return p;
84}
85
86// Forward Declarations
87void drawHome();
88void startGame();
89void drawLoop();
90void checkCollision();
91void clearPillar(int x, int gap);
92void clearFlappy(int x, int y);
93void drawPillar(int x, int gap);
94void drawFlappy(int x, int y);
95void drawWing1(int x, int y);
96void drawWing2(int x, int y);
97void drawWing3(int x, int y);
98void ResetScore();
99void sensereset();
100void senseBack();
101void drawrgb();
102void drawOSC();
103void awesomeIntro();
104
105void setup() {
106 Serial.begin(9600);
107 tft.reset();
108 uint16_t identifier = tft.readID();
109 if (identifier == 0xEFEF) identifier = 0x9486;
110 tft.begin(identifier);
111
112 tft.setRotation(1);
113 tft.invertDisplay(0);
114
115 currentpage = 0;
116
117 // Awesome Intro
118 awesomeIntro();
119
120 drawHome();
121}
122
123void awesomeIntro() {
124 tft.fillScreen(BLACK);
125
126 tft.setTextColor(LIME);
127 tft.setTextSize(2);
128 tft.setCursor(10, 10);
129 tft.print("SYSTEM BOOTING...");
130 delay(300);
131
132 tft.setTextSize(1);
133 tft.setCursor(10, 40); tft.print("> Loading Kernel..."); delay(150);
134 tft.setCursor(10, 55); tft.print("> Verifying Display..."); delay(150);
135 tft.setCursor(10, 70); tft.print("> Calibrating Touch..."); delay(150);
136 tft.setCursor(10, 85); tft.print("> Tech Vibes Module..."); delay(150);
137 tft.setCursor(10, 100); tft.print("> Loading Flappy Assets..."); delay(150);
138
139 tft.drawRect(10, 150, 300, 20, WHITE);
140 for(int i=0; i<296; i+=5) {
141 tft.fillRect(12, 152, i, 16, LIME);
142 delay(random(5, 20));
143 }
144
145 delay(200);
146 tft.fillScreen(BLACK);
147 tft.setCursor(40, 100);
148 tft.setTextSize(3);
149 tft.setTextColor(GREEN);
150 tft.print("ACCESS GRANTED");
151 delay(1000);
152}
153
154void drawHome()
155{
156 tft.fillScreen(BLACK);
157 tft.drawRoundRect(0, 0, 319, 240, 8, WHITE);
158
159 tft.fillRoundRect(60, 180, 200, 40, 8, RED);
160 tft.drawRoundRect(60, 180, 200, 40, 8, WHITE);
161
162 tft.setTextSize(3);
163 tft.setFont();
164
165 // 3D Shadow Effect
166 tft.setCursor(73, 103);
167 tft.setTextColor(GREY);
168 tft.print("FLAPPY GAME");
169
170 tft.setCursor(70, 100);
171 tft.setTextColor(WHITE);
172 tft.print("FLAPPY GAME");
173
174 tft.setCursor(50, 30);
175 tft.setTextSize(2);
176 tft.setTextColor(LIME);
177 tft.print("Tech Vibes");
178 tft.setTextColor(WHITE);
179 tft.setTextSize(3);
180 tft.setCursor(115, 190);
181 tft.print("START");
182}
183
184void loop() {
185 TSPoint p = safeGetPoint();
186
187 if (currentpage == 0)
188 {
189 if (p.z > MINPRESSURE && p.z < MAXPRESSURE)
190 {
191 if (p.x > 300 && p.x < 750 && p.y > 660 && p.y < 750)
192 {
193 tft.fillRoundRect(60, 180, 200, 40, 8, WHITE);
194 delay(70);
195 tft.fillRoundRect(60, 180, 200, 40, 8, RED);
196 tft.drawRoundRect(60, 180, 200, 40, 8, WHITE);
197 tft.setCursor(115, 190);
198 tft.setTextSize(3);
199 tft.print("START");
200
201 currentpage = 1;
202 nextDrawLoopRunTime = millis() + DRAW_LOOP_INTERVAL;
203 crashed = false;
204 running = false;
205 scrPress = false;
206 startGame();
207 }
208 else if (p.x > 563 && p.x < 683 && p.y > 275 && p.y < 750)
209 {
210 currentpage = 2;
211 drawrgb();
212 }
213 else if (p.x > 403 && p.x < 525 && p.y > 271 && p.y < 725)
214 {
215 currentpage = 3;
216 drawOSC();
217 }
218 }
219 }
220
221 if (currentpage == 1) //Flappy bird
222 {
223 senseBack();
224 if (millis() > nextDrawLoopRunTime && !crashed) {
225 drawLoop();
226 checkCollision();
227 nextDrawLoopRunTime += DRAW_LOOP_INTERVAL;
228 }
229 if (backsensed)
230 {
231 currentpage = 0;
232 drawHome();
233 delay(200);
234 return;
235 }
236
237 sensereset();
238 ResetScore();
239
240 if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
241 if (p.y > 300 && p.x < 600 && !scrPress && !resetsensed && !backsensed) {
242 if (crashed) {
243 startGame();
244 }
245 else if (!running) {
246 tft.fillRect(0, 0, 320, 80, SKYBLUE);
247 running = true;
248 }
249 else
250 {
251 fallRate = -8;
252 scrPress = true;
253 }
254 }
255 }
256 else {
257 scrPress = false;
258 }
259 }
260
261 if (currentpage == 2 || currentpage == 3) {
262 if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
263 if(p.x > 700) {
264 currentpage = 0;
265 drawHome();
266 delay(200);
267 }
268 }
269 }
270}
271
272void drawLoop() {
273 clearPillar(pillarPos, gapPosition);
274 clearFlappy(flX, flY);
275
276 if (running) {
277 flY += fallRate;
278 fallRate++;
279
280 pillarPos -= 5;
281 if (pillarPos == 0) {
282 score++; // Score +1
283 }
284 else if (pillarPos < -50) {
285 pillarPos = 320;
286 gapPosition = random(20, 120);
287 }
288 }
289
290 drawPillar(pillarPos, gapPosition);
291 drawFlappy(flX, flY);
292 switch (currentWing) {
293 case 0: case 1: drawWing1(flX, flY); break;
294 case 2: case 3: drawWing2(flX, flY); break;
295 case 4: case 5: drawWing3(flX, flY); break;
296 }
297 if (score == EEPROM.read(0) )
298 currentpcolour = YELLOW;
299 else
300 currentpcolour = GREEN;
301
302 currentWing++;
303 if (currentWing == 6 ) currentWing = 0;
304}
305
306void checkCollision() {
307 if (flY > 206) crashed = true;
308
309 if (flX + 34 > pillarPos && flX < pillarPos + 50)
310 if (flY < gapPosition || flY + 24 > gapPosition + 90)
311 crashed = true;
312
313 if (score >= 999) { // WINNING CONDITION
314 tft.fillScreen(YELLOW);
315 tft.setTextColor(RED);
316 tft.setTextSize(4);
317 tft.setCursor(40, 100);
318 tft.print("YOU WIN!");
319 delay(3000);
320 currentpage=0;
321 drawHome();
322 return;
323 }
324
325 if (crashed) {
326 tft.setTextColor(RED);
327 tft.setTextSize(5);
328 tft.setCursor(20, 75);
329 tft.print("Game Over!");
330
331 tft.setTextSize(4);
332 tft.setCursor(75, 125);
333 tft.print("Score:");
334
335 tft.setCursor(220, 125);
336 tft.setTextSize(5);
337 tft.setTextColor(WHITE);
338
339 tft.print(score);
340
341 if (score > highScore) {
342 highScore = score;
343 EEPROM.write(addr, highScore);
344 tft.setCursor(75, 175);
345 tft.setTextSize(4);
346 tft.setTextColor(YELLOW);
347 tft.print("NEW HIGH!");
348 }
349 running = false;
350 delay(1000);
351 }
352}
353
354void drawPillar(int x, int gap) {
355 tft.fillRect(x + 2, 2, 46, gap - 4, currentpcolour);
356 tft.fillRect(x + 2, gap + 92, 46, 136 - gap, currentpcolour);
357 tft.drawRect(x, 0, 50, gap, BLACK);
358 tft.drawRect(x + 1, 1, 48, gap - 2, BLACK);
359 tft.drawRect(x, gap + 90, 50, 140 - gap, BLACK);
360 tft.drawRect(x + 1, gap + 91 , 48, 138 - gap, BLACK);
361}
362
363void clearPillar(int x, int gap) {
364 tft.fillRect(x + 45, 0, 5, gap, SKYBLUE);
365 tft.fillRect(x + 45, gap + 90, 5, 140 - gap, SKYBLUE);
366}
367
368void clearFlappy(int x, int y) {
369 tft.fillRect(x, y, 34, 24, SKYBLUE);
370}
371
372void drawFlappy(int x, int y) {
373 tft.fillRect(x + 2, y + 8, 2, 10, BLACK);
374 tft.fillRect(x + 4, y + 6, 2, 2, BLACK);
375 tft.fillRect(x + 6, y + 4, 2, 2, BLACK);
376 tft.fillRect(x + 8, y + 2, 4, 2, BLACK);
377 tft.fillRect(x + 12, y, 12, 2, BLACK);
378 tft.fillRect(x + 24, y + 2, 2, 2, BLACK);
379 tft.fillRect(x + 26, y + 4, 2, 2, BLACK);
380 tft.fillRect(x + 28, y + 6, 2, 6, BLACK);
381 tft.fillRect(x + 10, y + 22, 10, 2, BLACK);
382 tft.fillRect(x + 4, y + 18, 2, 2, BLACK);
383 tft.fillRect(x + 6, y + 20, 4, 2, BLACK);
384 tft.fillRect(x + 12, y + 2, 6, 2, YELLOW);
385 tft.fillRect(x + 8, y + 4, 8, 2, YELLOW);
386 tft.fillRect(x + 6, y + 6, 10, 2, YELLOW);
387 tft.fillRect(x + 4, y + 8, 12, 2, YELLOW);
388 tft.fillRect(x + 4, y + 10, 14, 2, YELLOW);
389 tft.fillRect(x + 4, y + 12, 16, 2, YELLOW);
390 tft.fillRect(x + 4, y + 14, 14, 2, YELLOW);
391 tft.fillRect(x + 4, y + 16, 12, 2, YELLOW);
392 tft.fillRect(x + 6, y + 18, 12, 2, YELLOW);
393 tft.fillRect(x + 10, y + 20, 10, 2, YELLOW);
394 tft.fillRect(x + 18, y + 2, 2, 2, BLACK);
395 tft.fillRect(x + 16, y + 4, 2, 6, BLACK);
396 tft.fillRect(x + 18, y + 10, 2, 2, BLACK);
397 tft.fillRect(x + 18, y + 4, 2, 6, WHITE);
398 tft.fillRect(x + 20, y + 2, 4, 10, WHITE);
399 tft.fillRect(x + 24, y + 4, 2, 8, WHITE);
400 tft.fillRect(x + 26, y + 6, 2, 6, WHITE);
401 tft.fillRect(x + 24, y + 6, 2, 4, BLACK);
402 tft.fillRect(x + 20, y + 12, 12, 2, BLACK);
403 tft.fillRect(x + 18, y + 14, 2, 2, BLACK);
404 tft.fillRect(x + 20, y + 14, 12, 2, RED);
405 tft.fillRect(x + 32, y + 14, 2, 2, BLACK);
406 tft.fillRect(x + 16, y + 16, 2, 2, BLACK);
407 tft.fillRect(x + 18, y + 16, 2, 2, RED);
408 tft.fillRect(x + 20, y + 16, 12, 2, BLACK);
409 tft.fillRect(x + 18, y + 18, 2, 2, BLACK);
410 tft.fillRect(x + 20, y + 18, 10, 2, RED);
411 tft.fillRect(x + 30, y + 18, 2, 2, BLACK);
412 tft.fillRect(x + 20, y + 20, 10, 2, BLACK);
413}
414
415void drawWing1(int x, int y) {
416 tft.fillRect(x, y + 14, 2, 6, BLACK);
417 tft.fillRect(x + 2, y + 20, 8, 2, BLACK);
418 tft.fillRect(x + 2, y + 12, 10, 2, BLACK);
419 tft.fillRect(x + 12, y + 14, 2, 2, BLACK);
420 tft.fillRect(x + 10, y + 16, 2, 2, BLACK);
421 tft.fillRect(x + 2, y + 14, 8, 6, WHITE);
422 tft.fillRect(x + 8, y + 18, 2, 2, BLACK);
423 tft.fillRect(x + 10, y + 14, 2, 2, WHITE);
424}
425
426void drawWing2(int x, int y) {
427 tft.fillRect(x + 2, y + 10, 10, 2, BLACK);
428 tft.fillRect(x + 2, y + 16, 10, 2, BLACK);
429 tft.fillRect(x, y + 12, 2, 4, BLACK);
430 tft.fillRect(x + 12, y + 12, 2, 4, BLACK);
431 tft.fillRect(x + 2, y + 12, 10, 4, WHITE);
432}
433
434void drawWing3(int x, int y) {
435 tft.fillRect(x + 2, y + 6, 8, 2, BLACK);
436 tft.fillRect(x, y + 8, 2, 6, BLACK);
437 tft.fillRect(x + 10, y + 8, 2, 2, BLACK);
438 tft.fillRect(x + 12, y + 10, 2, 4, BLACK);
439 tft.fillRect(x + 10, y + 14, 2, 2, BLACK);
440 tft.fillRect(x + 2, y + 14, 2, 2, BLACK);
441 tft.fillRect(x + 4, y + 16, 6, 2, BLACK);
442 tft.fillRect(x + 2, y + 8, 8, 6, WHITE);
443 tft.fillRect(x + 4, y + 14, 6, 2, WHITE);
444 tft.fillRect(x + 10, y + 10, 2, 4, WHITE);
445}
446
447void ResetScore() {
448 TSPoint p = safeGetPoint();
449 if (p.x < 199 && p.y < 293 && running == false && p.z > MINPRESSURE) {
450 EEPROM.write(addr, 0);
451 startGame();
452 }
453}
454
455void sensereset() {
456 TSPoint p = safeGetPoint();
457 if (p.x < 199 && p.y < 293 && !running && p.z > MINPRESSURE)
458 resetsensed = true;
459 else
460 resetsensed = false;
461}
462
463void startGame() {
464 flX = 50;
465 flY = 25;
466 fallRate = 0;
467 pillarPos = 320;
468 gapPosition = 60;
469 crashed = false;
470 score = 0;
471 highScore = EEPROM.read(addr);
472 tft.setFont(&Org_01);
473 tft.fillScreen(SKYBLUE);
474 tft.setTextColor(YELLOW);
475 tft.setTextSize(3);
476 tft.setCursor(5, 20);
477 tft.println("Flappy Bird");
478 tft.setTextColor(WHITE);
479 tft.setTextSize(2);
480 tft.println(" Tap to begin!!");
481 tft.setTextColor(GREEN);
482 tft.setCursor(60, 60);
483 tft.print("Top Score : ");
484 tft.setTextColor(RED);
485 tft.setTextSize(3);
486 tft.print(highScore);
487 tft.setTextSize(2);
488 tft.fillRoundRect(240, 5, 250, 15, 8, WHITE);
489 tft.drawRoundRect(240, 5, 250, 15, 8, RED);
490 tft.setCursor(250, 15);
491 tft.println("RESET");
492
493 tft.fillRoundRect(240, 30, 250, 15, 8, WHITE);
494 tft.drawRoundRect(240, 30, 250, 15, 8, RED);
495 tft.setCursor(250, 40);
496 tft.print("BACK");
497
498 int ty = 230; int tx = 0;
499 for ( tx = 0; tx <= 300; tx += 20) {
500 tft.fillTriangle(tx, ty, tx + 10, ty, tx, ty + 10, GREEN);
501 tft.fillTriangle(tx + 10, ty + 10, tx + 10, ty, tx, ty + 10, YELLOW);
502 tft.fillTriangle(tx + 10, ty, tx + 20, ty, tx + 10, ty + 10, YELLOW);
503 tft.fillTriangle(tx + 20, ty + 10, tx + 20, ty, tx + 10, ty + 10, GREEN);
504 }
505 delay(1000);
506 nextDrawLoopRunTime = millis() + DRAW_LOOP_INTERVAL;
507}
508
509void senseBack() {
510 TSPoint p = safeGetPoint();
511 if (p.y < 305 && p.x < 285 && p.x > 239 && !running && p.z > MINPRESSURE) {
512 backsensed = true;
513 }
514 else
515 backsensed = false;
516}
517
518void drawrgb() {
519 tft.fillScreen(BLACK);
520 tft.setCursor(90, 20);
521 tft.setTextColor(WHITE);
522 tft.setTextSize(2);
523 tft.print("COLOUR : ");
524 tft.drawRect(BAR_MINY, BLUEBAR_MINX, BAR_HEIGHT, BAR_WIDTH, WHITE);
525 tft.drawRect(BAR_MINY, GREENBAR_MINX, BAR_HEIGHT, BAR_WIDTH, WHITE);
526 tft.drawRect(BAR_MINY, REDBAR_MINX, BAR_HEIGHT, BAR_WIDTH, WHITE);
527 tft.fillRect(BAR_MINY + 12, REDBAR_MINX + 3, BAR_WIDTH - 10, BAR_WIDTH - 5, RED);
528 tft.fillRect(BAR_MINY + 12, GREENBAR_MINX + 3, BAR_WIDTH - 10, BAR_WIDTH - 5, GREEN);
529 tft.fillRect(BAR_MINY + 12, BLUEBAR_MINX + 3, BAR_WIDTH - 10, BAR_WIDTH - 5, BLUE);
530 tft.fillRoundRect(5, 5, 50, 30, 8, BLUE);
531 tft.drawRoundRect(5, 5, 50, 30, 8, WHITE);
532 tft.setCursor(15, 15);
533 tft.setTextColor(BLACK);
534 tft.print("<-");
535 delay(300);
536}
537
538void drawOSC() {
539 tft.fillScreen(BLACK);
540 tft.fillRoundRect(5, 5, 50, 30, 8, BLUE);
541 tft.drawRoundRect(5, 5, 50, 30, 8, WHITE);
542 tft.setCursor(15, 15);
543 tft.print("<-");
544}