I have built a 20 X 20 neopixel display using 400 WS2812B LEDs. This will be controlled by an Arduino Mega using their version of C++. I have also created a numbered map of the LEDs . I use this map to make sprites using arrays. Let me give you an example. Consider a diamond shaped sprite using eight neopixels. I use my map to determine location of the neopixels I want for the starting position and build an array:
int diamond1[]={301,297,263,295,305,335,343,337};
Now I write the code that draws the sprite.
int count=0;
while(count<8){"here goes the code that cites the pixels of the diamond1 array one at a time " count++;} "here goes the code that draws the cited pixels, does a time delay, then erases the pixels.
Other than the generic C++ code, the rest of the code is specific to the Arduino, or the FastLED library used in conjunction with the Arduino.
The next position is represented by the array:
int diamond2[]={298,262,256,264,294,304,336,302};
which is one position to the right. To draw this position, I simply clone off the above code, substituting diamond2[] for diamond1[]. This is a very easy and simple process. But the problem becomes this: I want to move these sprites to perhaps hundreds of different position on the display , and I want to build dozens of sprites. This is going to require several thousand lines of code, which while easy to produce, takes up a huge amount of space and memory. Because the only thing that changes in all these lines of code is the name of each array: diamond1[], diamond2[], etc. I was thinking there must be a way to convert an integer to a string, and then combine it with the string "diamond" to produce the name of the array. That way I could reduce a few thousand lines of code to perhaps a hundred or so using for or while loops. I have been experimenting with the function String(), but have had no success. Is there a way this can be done?
I recommend some sort of drawing function that can draw any sprite at any location. The parameters for the function are the sprite (and, if there are any variations, its dimensions or if you have OOP, the sprite could be a small struct/class) and the desired location (could be an array or whatever).
I don't know what a neopixel is but you may be able to make them use less memory when you store them and pass them around. Or not. That is a small performance hit, but the hardware isnt exactly screaming fast.
The concepts in this are fundamental to you constructing sprites, managing/positioning them on the display. It also gives you enough information and key words to do further searches on the mountains of information available online.
You only need a single map for the diamond sprite. And if you have 100 diamonds, D1, D2 or D[0], D[1] etc they still only require the single map. Except for being on turned on or off, dead or alive, etc their x,y positions are the only relevant state variables to keep track of.
embedded c++ isnt OOP friendly, more often than not. One of the reasons my OOP advanced concepts is a bit behind at times is too much time in that world.
Thanks everyone for the replies. The example of the diamond is an over simplification. Think more of an amoeba, something that changes size and shape. Creating this type of "sprite" is just as easy as a diamond. You have to have as the first value in the array as the number of following elements. Then that value becomes part of the for loop, telling it how many times to cycle through the loop to pick up the remaining values. Again, very simple to generate code, but uses up gobs of memory. The Mega has 256K of flash memory. I have a Teensy 3.6 with 1M, but requires boosting its signal to 5v i.e. extra hardware that I want to avoid if possible. Again, thanks for the replies.
the first atari had 4kb and they did ok :)
I looked up the neopixel thing and its a form of RBG. If your memory gets too tight, you can define a smaller number of colors you want to use and reduce the memory by orders of magnitude. IF you defined 256 neopixel colors, for example, each sprite would only use one byte instead of 3 or 4 (seem to be a 4 byte version of it too) and a simple lookup table would rebuild the compressed data into memory for the full version just long enough to draw it. If you took it down to 16 colors you could get 2 pixels per byte, adding a little bit-logic to the extracting loop.
These are the things that had to be done in the late 80s early 90s when the display card only supported 256 colors at once, or 16 if you go back a little more...
256 colors looks really good in games, considering its limitations, but it fails on natural images where gradients tend to draw edges.
20*20*3 is 1200. 20*20 is 400 and 20*20*.5 is 200 for 3 byte, 1 byte and 1/2 byte color schemes respectively.
getting fancy: if these sprites are animated, eg like an animated gif with 10 or 15 copies of the same data with tiny changes, you can come up with a way to only store the changes to cut more size.
Thanks for the suggestion, Jonnin. Unfortunately, the FastLED library I am using doesn't have the ability to limit the color range. If I am using RGB, I can blend 255 different intensities of red, blue and green. If I am using HSV , (hue, saturation, volume), I can blend 255 different hues with 255 levels of saturation (how much color added to white) and 255 levels of volume (intensity of the hue). I see no way to limit these choices, thereby saving memory, unless I go in and tamper with the library itself. Although I have been programming since I first bought an MC-10, I just don't have that level of programming expertise.
You need to distinguish between texture coordinates and screen coordinates.
That is to say, a 4-by-4-pixel sprite should be encoded using only 16 samples, treating the top-left corner of the sprite as (0, 0). I.e., the texture is specified using this encoding
When you want to draw such a sprite at a position on the screen, you do so procedurally, not by manually translating the texture in screen space & hard-coding the result:
1 2
// put the w-by-h pixel texture tex to the buffer dst at position x, y:
void blit(rgb* dst, int x, int y, rgb const* tex, int w, int h);
Put another way, you should never have two textures which differ only in their position.
This is fairly basic -- if you're already doing this and still running out of memory, there is more that can be done. For example, you can use fewer bits to store color data on your end of the software, as @jonnin suggests. You would need to procedurally convert between color representations (to satisfy the library) when it comes time to actually draw them.