/* * good old starfield * * The way this one works is by first filling the screen with pixels at * random coordinates. Then at each frame we calculate the new position of * each pixel as if it was going away from the center of the screen. When a * pixel is going off screen, it is reassigned at a new random position. * To simulate a bit of depth, some pixels move twice as fast. * * Limitations: all the pixels/stars coordinates are stored in float arrays. * Each float is 4 bytes in size (32 bits) and the Nano has 2 KB of SRAM. * For each pixel/star we need to store 4 floats: x, y, vx, vy. That's * 16 bytes per pixel/star, so we should be able to store quite few. * However, the SRAM is already used by other parts of the code, and TVout. * In practice with the current implementation, there's just enough free * memory left for 25 pixels/stars :( * * See starfield51 for a modification that allows to have 51 stars :) */ #include TVout TV; #define STARS 25 float x[STARS]; // x coordinates of all the stars float y[STARS]; // y coordinates of all the stars float vx[STARS]; // vx and vy are the increments that we add to x float vy[STARS]; // and y at each frame to get the x,y position /* * new_star(i) generates the x, y, vx, and vy values for star i * x is generated at random x-axis position around the centre * y is generated at random y-axis position around the centre * we calculate the distances dx and dy from the screen center ox,oy * (we cheat a bit to make sure no 0 are left in a division later on) * we use dx and dy to calculte the distance vl between the star * and the center, ie the magnitude of the vector (center, star) * we use the magnitude to normalize the vector, namely calculating * the normalized components vx and vy */ void new_star(uint8_t i) { uint8_t ox = 60; uint8_t oy = 48; float dx, dy, vl; x[i] = 30 + random(60); y[i] = 24 + random(48); dx = x[i] - ox; dy = y[i] - oy; switch(int(dx)) case 0: dx = 1; // CHEAT vl = sqrt(dx*dx + dy*dy); vx[i] = dx / vl; vy[i] = dy / vl; } /* * new_stars() creates as many stars as set by the STARS constant */ void new_stars() { for (uint8_t i = 0; i < STARS; i++) new_star(i); } /* * Nothing exciting here, we initialize the PRNG with some noise, * configure TV out, and generate coordinates for all stars. */ void setup() { randomSeed(analogRead(6)); // effective? TV.begin(NTSC,120,96); new_stars(); } /* * update_stars() goes through all the x and y values and update * them in such a way that: * new x = current x + vx * new y = current y + vy * we also test if the new coordinates of the star are still in the * visible part of the screen, and if not, we call new_star() to * generate new x, y, vx, vy all over again * Note: fancy effect, the first 20 stars are updated with regular * vx and vy values, while the last 5 are a updated with 2 times vx and vy, * which means that these 5 last stars will move twice the speed. */ void update_stars() { for (uint8_t i = 0; i < 20; i++) { x[i] = x[i] + vx[i]; y[i] = y[i] + vy[i]; if (x[i] > 120 || x[i] < 0 || y[i] < 0 || y[i] > 96) new_star(i); } for (uint8_t i = 20; i < STARS; i++) { x[i] = x[i] + vx[i] * 2; y[i] = y[i] + vy[i] * 2; if (x[i] > 120 || x[i] < 0 || y[i] < 0 || y[i] > 96) new_star(i); } } /* * draw_stars() walks through the x and y coordinates arrays and * uses the value to draw a pixel for each star */ void draw_stars() { for (uint8_t i = 0; i < STARS; i++) TV.set_pixel(x[i], y[i], 1); } /* * loop() is stuck doing the same stuff forever because it dared to trick * the gods... */ void loop() { TV.delay_frame(1); update_stars(); TV.clear_screen(); draw_stars(); }