PixelDust Digital Sand Demos for Arcada
2024-10-09 | By Adafruit Industries
License: See Original Project LCD / TFT Adafruit Feather Grove STEMMA
Courtesy of Adafruit
Guide by Lady Ada
Overview
PyGamer and PyBadge have built in accelerometers - which you can use in your games or demos to make nifty motion-activated effects. In this mini guide we'll show you some examples of PaintYourDragon's PixelDust library but for Arcada boards.
Supported Hardware
You'll need a board with Adafruit Arcada support + an accelerometer such as...
Adafruit PyGamer for MakeCode Arcade, CircuitPython or Arduino
Adafruit PyBadge for MakeCode Arcade, CircuitPython, or Arduino
Text editor powered by tinymce.
Start by following your board's guide on installing Arduino IDE, and support for the board you have. Then install the Adafruit Arcada libraries (there's a lot of em!)
Also install the Adafruit PixelDust library
Compilation Settings
As you get to a few thousand particles, you'll want to speed up your board as much as possible. Compile with ultra-speed settings such as 200MHz overclock, -Ofast optimizations, and Cache enabled.
Runtime Settings
There's not a lot of things you can adjust but here's a few common ones:
#define CHUNKY_SAND
If this is at the top of the code, it will make each particle a 2x2 pixel rather than a single pixel. this makes it look a little better, but you can't fit as many particles on the screen.
#define N_FLAKES 2000
How many particles to simulate. More look cooler but too many and it slows down! 1000-2000 seems to be a good number, especially with CHUNKY_SAND turned on.
On this line in the loop:
pixeldust->iterate(xx 3000.0, yy 3000.0, zz * 3000.0);
The multiplier affects the 'gravity' of the pixels. Larger numbers will drag the pixels down faster, smaller numbers will make the pixels float a little more.
Snow Demo
Start with the pixeldust_demos->pixeldust_snow example, it’s the simplest demo - each pixel is the same white color.
Upload and enjoy!
Sand Demo
This demo builds on the snow version to add speckled yellow colors to each particle, to create a sand-effect!
You can add color to each pixel by creating a new array of 16-bit colors as we do in this demo with the creation of uint16_t *flake_colors; and then later flake_colors = (uint16_t )malloc(N_FLAKES 2).
Then you can assign the colors, we'll use an HSV picker to find a hue we think is sandy...
And randomly assign brightness/saturations so we get a range of sandy colors!
// randomize colors for (int i=0; i< N_FLAKES; i++) { flake_colors[i] = __builtin_bswap16(arcada.ColorHSV565(40, // Hue (sandy) random(50, 100), // saturation random(50, 100))); // brightness }
Note we use __builtin_bswap16 on each color word. That's because we later use DMA to write out all the pixels and we need to have the high/low bytes of color swapped in order for it to run as fast as possible (it’s a weird effect of TFT DMA on Arduino).
Later on, when we draw the pixels, we'll look up the corresponding color before we draw the color to our framebuffer:
for(int i=0; i<N_FLAKES; i++) { pixeldust->getPosition(i, &x, &y); //Serial.printf("(%d, %d) -> %d\n", x, y, x * width + y); uint16_t flakeColor = flake_colors[i]; #ifdef CHUNKY_SAND framebuffer[2*y * width + 2*x] = flakeColor; framebuffer[2*y * width + 2*x+1] = flakeColor; framebuffer[(2*y+1) * width + 2*x] = flakeColor; framebuffer[(2*y+1) * width + 2*x + 1] = flakeColor; #else framebuffer[y * width + x] = flakeColor; #endif }
Logo Demo
Finally, the most advanced of the demos adds a logo 'obstacle' both as an image and a 'mask' that tells PixelDust where not to let pixels go. This makes for lovely effects as particles slide around.
For the logo, which is 8-bit grayscale and stored in the header, you can use a tool like this that will take an image and convert it into a header file.
Like the sand demo we will store a color for each particle. Except this time instead of randomly placing them on the display, they are put into boxes along the bottom of the screen:
// Set up initial sand coordinates, in 8x8 blocks int n = 0; for(int i=0; i<N_COLORS; i++) { int xx = i * play_width / N_COLORS; int yy = play_height - BOX_HEIGHT; for(int y=0; y<BOX_HEIGHT; y++) { for(int x=0; x<play_width / N_COLORS; x++) { //Serial.printf("#%d -> (%d, %d)\n", n, xx + x, yy + y); pixeldust->setPosition(n++, xx + x, yy + y); } } }
Since the chunks of particle divide up into 8 colors, we don’t have to store the color of each one, we know that the index of the particle, divided by 8, gives the color index. Notes we have to bswap16 the color here like we did before.
colors[0] = arcada.color565(40 , 40, 40); // Dark Gray colors[1] = arcada.color565(120, 79, 23); // Brown colors[2] = arcada.color565(228, 3, 3); // Red colors[3] = arcada.color565(255,140, 0); // Orange colors[4] = arcada.color565(255,237, 0); // Yellow colors[5] = arcada.color565( 0,128, 38); // Green colors[6] = arcada.color565( 0, 77,255); // Blue colors[7] = arcada.color565(117, 7,135); // Purple for (int i=0; i<N_COLORS; i++) { colors[i] = __builtin_bswap16(colors[i]); // we swap the colors here to speed up DMA }
Then before we draw all the particles, we also have to draw the logo:
int logo_origin_x = (width - 2*LOGO_WIDTH ) / 2; int logo_origin_y = (height - 2*LOGO_HEIGHT ) / 2; // Draw the logo atop the background... for(int yl=0; yl<LOGO_HEIGHT; yl++) { for(int xl=0; xl<LOGO_WIDTH; xl++) { uint16_t c = __builtin_bswap16(arcada.color565(logo_gray[yl][xl], logo_gray[yl][xl], logo_gray[yl][xl])); x = logo_origin_x + 2*xl; y = logo_origin_y + 2*yl; framebuffer[y * width + x] = c; framebuffer[y * width + x+1] = c; framebuffer[(y+1) * width + x] = c; framebuffer[(y+1) * width + x+1] = c; } }
Text editor powered by tinymce.
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum