Arduino sketch on a TFT display
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

344 lines
6.3KB

  1. #include <TouchScreen.h>
  2. #include <MCUFRIEND_kbv.h>
  3. MCUFRIEND_kbv tft;
  4. #include "space_schnabli_100.c"
  5. #define BLACK 0x0000
  6. #define WHITE 0xffff
  7. #define GRAY 0xce59
  8. #define RED 0xe000
  9. #define ORANGE 0xfc60
  10. #define YELLOW 0xff60
  11. #define GREEN 0x0404
  12. #define BLUE 0x027f
  13. #define VIOLET 0x7030
  14. #define WIDTH 480
  15. #define HEIGHT 320
  16. #define MINPRESSURE 200
  17. #define MAXPRESSURE 1000
  18. // Number of frames a spark is alive.
  19. #define SPARK_AGE_MIN 25
  20. #define SPARK_AGE_MAX 35
  21. #define SPARKS_PER_TAP 10
  22. // Frames between spawning fireworks.
  23. #define FIREWORK_CD 5
  24. // Chance that a random firework explodes.
  25. #define FIREWORK_CHANCE 15
  26. struct entity_t
  27. {
  28. float x, y;
  29. float dx, dy;
  30. int width, height;
  31. uint8_t *sprite;
  32. };
  33. struct star_t
  34. {
  35. float x, y;
  36. float dx;
  37. uint16_t color;
  38. };
  39. struct spark_t
  40. {
  41. float x, y;
  42. float dx, dy;
  43. int8_t age;
  44. int16_t color;
  45. };
  46. const int XP = 8, XM = A2, YP = A3, YM = 9;
  47. const int TS_LEFT = 97, TS_RT = 949, TS_BOT = 128, TS_TOP = 905;
  48. TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
  49. entity_t schnablis[2];
  50. uint8_t schnablis_num = sizeof(schnablis) / sizeof(*schnablis);
  51. star_t stars[25];
  52. uint8_t stars_num = sizeof(stars) / sizeof(*stars);
  53. spark_t sparks[64];
  54. uint8_t sparks_num = sizeof(sparks) / sizeof(*sparks);
  55. uint16_t spark_colors[] = {RED, ORANGE, YELLOW, GREEN, BLUE, VIOLET};
  56. uint8_t spark_colors_num = sizeof(spark_colors) / sizeof(uint16_t);
  57. /* entity_t */
  58. void entity_t_randomize_angles(entity_t *e)
  59. {
  60. e->dx = random(300, 600) / 100.0;
  61. e->dy = random(15, 150) / 100.0;
  62. }
  63. void entity_t_update(entity_t *e)
  64. {
  65. entity_t_move(e);
  66. entity_t_draw(e);
  67. entity_t_clear(e);
  68. }
  69. // Clear leftover pixels. The platypus is always flying towards the upper left
  70. // corner, so only the bottom and right side needs to be cleaned.
  71. void entity_t_clear(entity_t *e)
  72. {
  73. tft.fillRect(e->x + e->width, e->y, ceil(e->dx), e->height, BLACK);
  74. tft.fillRect(e->x, e->y + e->height, e->width, ceil(e->dy), BLACK);
  75. }
  76. void entity_t_move(entity_t *e)
  77. {
  78. e->x -= e->dx;
  79. e->y -= e->dy;
  80. if (e->x < -e->width)
  81. {
  82. e->x = WIDTH;
  83. entity_t_randomize_angles(e);
  84. }
  85. if (e->y < -e->height)
  86. {
  87. e->y = HEIGHT;
  88. entity_t_randomize_angles(e);
  89. }
  90. }
  91. void entity_t_draw(entity_t *e)
  92. {
  93. // Do not draw pixels that would be outside the displays left side.
  94. if (e->x < 0)
  95. {
  96. for (int y = max(-e->y, 0); y < min(e->height, HEIGHT - e->y); y++)
  97. {
  98. uint16_t i = y * e->width - e->x;
  99. tft.setAddrWindow(0, e->y + y, e->width + e->x, 1);
  100. tft.pushColors(&e->sprite[i * 2], e->width + e->x, 1, false);
  101. }
  102. return;
  103. }
  104. // Do not draw pixels that would be outside the displays right side.
  105. if (e->x + e->width > WIDTH)
  106. {
  107. for (int y = max(-e->y, 0); y < min(e->height, HEIGHT - e->y); y++)
  108. {
  109. uint16_t i = y * e->width;
  110. tft.setAddrWindow(e->x, e->y + y, WIDTH - e->x, 1);
  111. tft.pushColors(&e->sprite[i * 2], WIDTH - e->x, 1, false);
  112. }
  113. return;
  114. }
  115. // The display is able to push colors outside its top but not bottom. So we
  116. // make sure to only draw as much pixels as necessary if we're on the
  117. // displays bottom.
  118. uint16_t height = min(e->height, HEIGHT - e->y);
  119. tft.setAddrWindow(e->x, e->y, e->x + e->width - 1, e->y + e->height - 1);
  120. tft.pushColors(e->sprite, e->width * height, 1, false);
  121. }
  122. /* entity_t schnabli */
  123. // Generate a new schnabli. It starts outside the displa and moves towards the
  124. // upper left corner where it wraps around.
  125. entity_t entity_t_new_schnabli(int y)
  126. {
  127. entity_t s = entity_t{
  128. x : float(WIDTH),
  129. y : float(y),
  130. dx : 0.0,
  131. dy : 0.0,
  132. width : 100,
  133. height : 41,
  134. sprite : (uint8_t *)space_schnabli_100,
  135. };
  136. entity_t_randomize_angles(&s);
  137. return s;
  138. }
  139. /* stars */
  140. star_t star_t_new()
  141. {
  142. return star_t{
  143. x : (float)random(0, WIDTH - 1),
  144. y : (float)random(0, HEIGHT - 1),
  145. dx : random(10, 100) / 100.0,
  146. color : GRAY,
  147. };
  148. }
  149. void star_t_update(star_t *s)
  150. {
  151. star_t_clear(s);
  152. star_t_move(s);
  153. star_t_draw(s);
  154. }
  155. void star_t_move(star_t *s)
  156. {
  157. s->x += s->dx;
  158. if (s->x > WIDTH)
  159. {
  160. s->x = 0;
  161. }
  162. }
  163. void star_t_clear(star_t *s)
  164. {
  165. tft.drawPixel(s->x, s->y, BLACK);
  166. }
  167. void star_t_draw(star_t *s)
  168. {
  169. tft.drawPixel(s->x, s->y, s->color);
  170. }
  171. /* sparks */
  172. void spark_t_update(spark_t *s)
  173. {
  174. spark_t_clear(s);
  175. if (s->age <= 0)
  176. {
  177. return;
  178. }
  179. spark_t_move(s);
  180. spark_t_draw(s);
  181. }
  182. void spark_t_move(spark_t *s)
  183. {
  184. s->x += s->dx;
  185. s->y += s->dy;
  186. s->age--;
  187. if (WIDTH < s->x || s->x < 0)
  188. {
  189. s->age = 0;
  190. }
  191. if (HEIGHT < s->y || s->y < 0)
  192. {
  193. s->age = 0;
  194. }
  195. }
  196. void spark_t_clear(spark_t *s)
  197. {
  198. tft.fillCircle(s->x, s->y, 1, BLACK);
  199. }
  200. void spark_t_draw(spark_t *s)
  201. {
  202. tft.fillCircle(s->x, s->y, 1, s->color);
  203. }
  204. /* firework */
  205. int firework_cd = 0;
  206. // Spawn same colored sparks. If force is true, a soarks are created regardless
  207. // of the fireworks cooldown.
  208. void firework_new(int x, int y, bool force)
  209. {
  210. if (!force)
  211. {
  212. if (firework_cd > 0)
  213. {
  214. return;
  215. }
  216. firework_cd = FIREWORK_CD;
  217. }
  218. uint8_t sparks_added = 0;
  219. uint16_t color = spark_colors[random(0, spark_colors_num - 1)];
  220. for (int i = 0; i < sparks_num; i++)
  221. {
  222. if (sparks[i].age > 0)
  223. {
  224. continue;
  225. }
  226. if (sparks_added >= SPARKS_PER_TAP)
  227. {
  228. break;
  229. }
  230. sparks_added++;
  231. sparks[i].x = x;
  232. sparks[i].y = y;
  233. sparks[i].dx = random(-200, 200) / 100.0;
  234. sparks[i].dy = random(-200, 200) / 100.0;
  235. sparks[i].age = random(SPARK_AGE_MIN, SPARK_AGE_MAX);
  236. sparks[i].color = color;
  237. }
  238. }
  239. void firework_update()
  240. {
  241. firework_cd--;
  242. if (random(0, 100) < FIREWORK_CHANCE)
  243. {
  244. firework_new(random(0, WIDTH), random(0, HEIGHT), true);
  245. }
  246. }
  247. /* main */
  248. void setup()
  249. {
  250. randomSeed(analogRead(0));
  251. tft.begin(tft.readID());
  252. tft.setRotation(3);
  253. tft.fillScreen(BLACK);
  254. for (int i = 0; i < stars_num; i++)
  255. {
  256. stars[i] = star_t_new();
  257. }
  258. for (int i = 0; i < schnablis_num; i++)
  259. {
  260. uint16_t y = HEIGHT / schnablis_num * i;
  261. y += random(0, HEIGHT / schnablis_num / 2);
  262. schnablis[i] = entity_t_new_schnabli(y);
  263. }
  264. }
  265. void loop()
  266. {
  267. touch();
  268. for (int i = 0; i < stars_num; i++)
  269. {
  270. star_t_update(&stars[i]);
  271. }
  272. for (int i = 0; i < schnablis_num; i++)
  273. {
  274. entity_t_update(&schnablis[i]);
  275. }
  276. for (int i = 0; i < sparks_num; i++)
  277. {
  278. spark_t_update(&sparks[i]);
  279. }
  280. firework_update();
  281. }
  282. void touch()
  283. {
  284. TSPoint p = ts.getPoint();
  285. pinMode(YP, OUTPUT);
  286. pinMode(XM, OUTPUT);
  287. digitalWrite(YP, HIGH);
  288. digitalWrite(XM, HIGH);
  289. if (MINPRESSURE < p.z && p.z < MAXPRESSURE)
  290. {
  291. int x = map(p.y, TS_LEFT, TS_RT, 0, WIDTH);
  292. int y = map(p.x, TS_BOT, TS_TOP, 0, HEIGHT);
  293. firework_new(x, y, false);
  294. }
  295. }