starfields
parent
2241a36aed
commit
6b57938794
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* 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.h>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* good old starfield - PASTIS 51 EDITION + CAPTAIN MOD
|
||||||
|
*
|
||||||
|
* Same as starfield51 but this time we can control the origin with POT 1 and
|
||||||
|
* POT2, which affects all the newly created stars, allowing for deep space
|
||||||
|
* travel.
|
||||||
|
*
|
||||||
|
* Really not optimised in current state, even though I tried to simplify things.
|
||||||
|
* But it starts to push the chip beyond its capacity, so it feels a bit sluggish now.
|
||||||
|
* More changes would start to make the thing even less readable so...
|
||||||
|
*/
|
||||||
|
#include <TVout.h>
|
||||||
|
|
||||||
|
TVout TV;
|
||||||
|
|
||||||
|
#define STARS 51
|
||||||
|
#define POT_1 0
|
||||||
|
#define POT_2 1
|
||||||
|
#define POT_1_MOD 11 // 120/1024 ish int(0.1171875 * 100)
|
||||||
|
#define POT_2_MOD 9 // 96/1024 ish
|
||||||
|
#define RND_1_MOD 5 // 60/1024 ish
|
||||||
|
#define RND_2_MOD 4 // 48/1024 ish
|
||||||
|
|
||||||
|
int x[STARS];
|
||||||
|
int y[STARS];
|
||||||
|
int vx[STARS];
|
||||||
|
int vy[STARS];
|
||||||
|
uint8_t ox;
|
||||||
|
uint8_t oy;
|
||||||
|
|
||||||
|
void
|
||||||
|
setup()
|
||||||
|
{
|
||||||
|
randomSeed(analogRead(6)); // effective?
|
||||||
|
TV.begin(NTSC,120,96);
|
||||||
|
new_stars();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
new_star(uint8_t i)
|
||||||
|
{
|
||||||
|
uint8_t x_tmp, y_tmp;
|
||||||
|
int dx, dy, pot1, pot2;
|
||||||
|
float vl;
|
||||||
|
|
||||||
|
pot1 = analogRead(POT_1);
|
||||||
|
pot2 = analogRead(POT_2);
|
||||||
|
|
||||||
|
ox = pot1 * POT_1_MOD / 100 ;
|
||||||
|
oy = pot2 * POT_2_MOD / 100 ;
|
||||||
|
|
||||||
|
x_tmp = pot1 * RND_1_MOD / 100 + random(60);
|
||||||
|
y_tmp = pot2 * RND_2_MOD / 100 + random(48);
|
||||||
|
|
||||||
|
dx = x_tmp - ox;
|
||||||
|
dy = y_tmp - oy;
|
||||||
|
|
||||||
|
switch(dx)
|
||||||
|
case 0: dx = 1; // CHEAT
|
||||||
|
|
||||||
|
vl = 1 / sqrt(dx*dx + dy*dy);
|
||||||
|
vx[i] = int(dx * vl * 100);
|
||||||
|
vy[i] = int(dy * vl * 100);
|
||||||
|
|
||||||
|
x[i] = x_tmp * 100;
|
||||||
|
y[i] = y_tmp * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
new_stars()
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < STARS; i++)
|
||||||
|
new_star(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
update_stars()
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < 40; i++) {
|
||||||
|
x[i] = x[i] + vx[i];
|
||||||
|
y[i] = y[i] + vy[i];
|
||||||
|
if (x[i] > 12000 || x[i] < 0 || y[i] < 0 || y[i] > 9600)
|
||||||
|
new_star(i);
|
||||||
|
}
|
||||||
|
for (uint8_t i = 40; i < STARS; i++) {
|
||||||
|
x[i] = x[i] + (vx[i] << 1);
|
||||||
|
y[i] = y[i] + (vy[i] << 1);
|
||||||
|
if (x[i] > 12000 || x[i] < 0 || y[i] < 0 || y[i] > 9600)
|
||||||
|
new_star(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
draw_stars()
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < STARS; i++)
|
||||||
|
TV.set_pixel(x[i]/100, y[i]/100, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
loop()
|
||||||
|
{
|
||||||
|
TV.delay_frame(1);
|
||||||
|
update_stars();
|
||||||
|
TV.clear_screen();
|
||||||
|
draw_stars();
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* good old starfield - PASTIS 51 EDITION
|
||||||
|
*
|
||||||
|
* See starfield25 for context and more comments, otherwise it may not make much
|
||||||
|
* sense...
|
||||||
|
*
|
||||||
|
* So basically the trick used to get more stars here, is not to free memory,
|
||||||
|
* because that's not possible, but instead it's to make the stars use less memory.
|
||||||
|
* To do that we change the type from float (4 bytes) to int (2 bytes).
|
||||||
|
* The tradeoff is of course that now we have integers and not floating point numbers,
|
||||||
|
* and that prevents us to have any meaningful results when we calculate vector related
|
||||||
|
* stuff. But there's a workaround to this!
|
||||||
|
* The workaround is very simple, we use int as floats without floating points, which
|
||||||
|
* means that everytime we read from the array we convert the int to float and divide
|
||||||
|
* it by an arbitrary precision value (here we use 100), and when we want to store a
|
||||||
|
* float we multiply it by the same precision value and convert to int.
|
||||||
|
* We loose in accuray, but that does not matter here at all.
|
||||||
|
*
|
||||||
|
* ex: we want to store 12,345678
|
||||||
|
* 12,3456 * 100 = 1234,56
|
||||||
|
* int(1234,56) = 1234
|
||||||
|
* and converted back
|
||||||
|
* float(1234 / 100) = 12,34
|
||||||
|
* in the process with this trick we lost 0,005678 (who cares?)
|
||||||
|
*
|
||||||
|
* Note: most of the changes are in new_star(). There are some minor changes elsewhere,
|
||||||
|
* mostly to try optimise things cheaply, ignore.
|
||||||
|
*/
|
||||||
|
#include <TVout.h>
|
||||||
|
|
||||||
|
TVout TV;
|
||||||
|
|
||||||
|
#define STARS 51
|
||||||
|
|
||||||
|
int x[STARS];
|
||||||
|
int y[STARS];
|
||||||
|
int vx[STARS];
|
||||||
|
int vy[STARS];
|
||||||
|
|
||||||
|
void
|
||||||
|
setup()
|
||||||
|
{
|
||||||
|
randomSeed(analogRead(6)); // effective?
|
||||||
|
TV.begin(NTSC,120,96);
|
||||||
|
new_stars();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
new_star(uint8_t i)
|
||||||
|
{
|
||||||
|
uint8_t x_tmp, y_tmp;
|
||||||
|
uint8_t ox = 60;
|
||||||
|
uint8_t oy = 48;
|
||||||
|
int dx, dy;
|
||||||
|
float vl;
|
||||||
|
|
||||||
|
x_tmp = 30 + random(60);
|
||||||
|
y_tmp = 24 + random(48);
|
||||||
|
|
||||||
|
dx = x_tmp - ox;
|
||||||
|
dy = y_tmp - oy;
|
||||||
|
|
||||||
|
switch(dx)
|
||||||
|
case 0: dx = 1; // CHEAT
|
||||||
|
|
||||||
|
vl = 1 / sqrt(dx*dx + dy*dy);
|
||||||
|
vx[i] = int(dx * vl * 100);
|
||||||
|
vy[i] = int(dy * vl * 100);
|
||||||
|
|
||||||
|
x[i] = x_tmp * 100;
|
||||||
|
y[i] = y_tmp * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
new_stars()
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < STARS; i++)
|
||||||
|
new_star(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
update_stars()
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < 40; i++) {
|
||||||
|
x[i] = x[i] + vx[i];
|
||||||
|
y[i] = y[i] + vy[i];
|
||||||
|
if (x[i] > 12000 || x[i] < 0 || y[i] < 0 || y[i] > 9600)
|
||||||
|
new_star(i);
|
||||||
|
}
|
||||||
|
for (uint8_t i = 40; i < STARS; i++) {
|
||||||
|
x[i] = x[i] + (vx[i] << 1);
|
||||||
|
y[i] = y[i] + (vy[i] << 1);
|
||||||
|
if (x[i] > 12000 || x[i] < 0 || y[i] < 0 || y[i] > 9600)
|
||||||
|
new_star(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
draw_stars()
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < STARS; i++)
|
||||||
|
TV.set_pixel(x[i]/100, y[i]/100, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
loop()
|
||||||
|
{
|
||||||
|
TV.delay_frame(1);
|
||||||
|
update_stars();
|
||||||
|
TV.clear_screen();
|
||||||
|
draw_stars();
|
||||||
|
}
|
Loading…
Reference in New Issue