Code img help

Hello I was just asking about how to display a sprite on the Ringo and move it around

Here’s a simple example of a 20x20 sprite containing a fixed centered 10x10 multicolored square moving around on the Ringo screen:

#include <MAKERphone.h>

MAKERphone mp;
TFT_eSprite *mysprite;

//Initialization
void setup()
{
  //Initial startup
  Serial.begin(115200);
  mp.begin(1);    
  mp.inCall=1; //Keep mp awake
  mp.display.fillScreen(TFT_BLACK);
  mp.display.pushSprite(0,0); // Clear entire screen

  //Create sprite with multicolored centered square
  mysprite = new TFT_eSprite(&mp.tft);
  mysprite->setColorDepth(8);
  mysprite->createSprite(20,20);
  mysprite->setRotation(1);
  mysprite->fillScreen(TFT_BLACK);
  mysprite->drawLine(5,5,5,15,TFT_GREEN);
  mysprite->drawLine(5,15,15,15,TFT_YELLOW);
  mysprite->drawLine(15,15,15,5,TFT_RED);
  mysprite->drawLine(15,5,5,5,TFT_BLUE);
}

//Display sprite moving around.
void loop()
{
  static int x = 0, dx = 1;
  static int y = 0, dy = 1;
  mysprite->pushSprite(x,y);
  x += dx; if(x >= 140 || x == 0) dx=-dx;
  y += dy; if(y >= 108 || y == 0) dy=-dy;
  delay(10);
}
2 Likes

Thanks for the help.

Aidan

I do have another question how do I display a sprite that is a .png or other images formats

Brace yourself…this is a long one. Sorry in advance. I really should teach classes again and get this long-windedness out of my system somewhere else. :shushing_face:

For most image formats contained in file, such as on the SD Card, you’ll need to call upon a library to load and convert them to a format acceptable on the Ringo. For example, a JPEGDecoder library is already loaded in the standard MAKERphone (extern’d as mp) library and there are function calls already available in MAKERphone for drawing a JPEG file loaded from the SD mp.drawJpeg(String filename, int xpos, int ypos); So, if you are including the mp library in your app, you only need that one-line command to load the JPEG, decompress/decode it, and display it. You don’t even have to fuss with SD.open()/SD.close() calls…the function does it all for you. String filename will take an SD card path formatted like "/folder/file.jpg" or "/file.jpg"

You’ll want to avoid most other compressed formats like PNG/GIF/etc, as there just isn’t enough RAM on the RINGO to decompress any but the smallest of images. Don’t just take my word for it…search the internet; you won’t find any truly workable Arduino example code using anything beyond BMP and JPEG directly. It’s just unreasonable on such limited hardware; you either don’t have the CPU to render quickly, or the RAM to store it, or both.

If you are using JPEG images appropriately sized for the display, you should be ok…unnecessarily large JPEG files will almost certainly cause crashes on decode (no scaling support/not enough RAM). BMP files could be read and fed to to the display directly if you can’t afford the overhead of JPEG decompression. Since BMP is uncompressed color byte data with a header, it’s very light on RAM usage to work with…you don’t even have to read the whole file to start writing it to the display, eliminating most filesize issues. That said, you should always optimize your sprites/images for their target display resolution and color depth. The Ringo uses a 160x128px 16bit RGB display that can operate between 25 and 50fps…but rendering/transferring images, especially compressed, will be your biggest bottleneck to keep in mind from a performance and stability perspective.

There are plenty of ready made functions and code examples out there for extracting the image data from a BMP to pass to a display, but keep in mind that you should be adding #include <MAKERphone.h> (without or without a #define MPMINIMAL) to load the ready-made Ringo libraries for working with the Display, buttons, and such. If not, you’ll have to load the Arduino.h, TFT.h, SPI.h, etc libraries and adding all those pin definitions and such on your own. You should only go this route if you are building an app that needs a lot of “magic” to fit it into the 2MB app space in Flash and loading even #include <MAKERphone.h> with #define MPMINIMAL would be too much.

Homework for you:

  • Read up on the MAKERphone library for Ringo…saves a lot of re-invention and figuring out the internal hardware wiring, pinouts, etc. If you’ve got the app space for it, you should be including it in all of your Ringo apps…by the time you wrote all your own functions for the hardware interaction, you probably wouldn’t save too much space anyway, depending on the app and its usage.

  • Check out this example of loading a BMP and displaying it (without using the MAKERphone ready-made functions in your app)

  • Consider the ramifications of different image formats AND ways of making them accessible in your app:

    • Formats:

      • BMP & Raw Sprites (which is just byte representation of BMP pixel data without headers stored in FLASH or RAM) are larger but faster/more efficient because they are uncompressed and super easy to work with in most any situation. Besides, these are rather low-res screens…not too many uncompressed pixels to work with. You’d use PROGMEM/Flash for sprites like your character in a game, which you want to be able to load and render to the display quickly as they move around (simulating animation with multiple small sprites like for legs walking) without having to eat up RAM to hold onto those constantly used, but otherwise static, sprites.

      • JPEG is small/compressed…BUT that is half its problem, as decompression takes up precious RAM and limits file size and quality. However, JPEG is “simple” enough that you can decompress and decode reasonably sized files. Use when latency of initial load/decompress/decode isn’t an issue…like the “map” or “background” image for a game level; something that will change infrequently, so a little delay on load is no problem.

      • GIF is, again, a compressed format…and forget about dealing with animated raster images on such limited hardware on top of that. SKIP

      • PNG is just a lossless compression successor to GIF…same decompression vs RAM issues, but actually probably way worse on performance due to how lossless decompression is handled. Fun fact, the rendering time of a modern website with all PNG images is nearly 4 times that of a site employing all JPEG images, even on modern PC hardware. Sure, this only equates to seconds on a modern PC with a few dozen images…but imagine how this would work out on an Arduino with even one PNG.

    • Storage Locations:

      • PROGMEM/FLASH - This is your best bet if you don’t have a ton of images, as the FLASH partition for your app is only 2MB. It’s best because it doesn’t require resources and time/delay reading files from SD card and works great when converting from BMP images. A 16-bit RGB565 formatted full screen BMP will take up about 18-23kb each of your total 2MB app space.

      • SD Card - You can store as many images as will fit on the SD Card, but loading times will be much slower than PROGMEM/FLASH, as you will have to code routines to load the file from the SD Card into RAM, convert or decompress as needed ON THE FLY which will take up some more RAM even if only temporarily, before you can push it to the display.

For example: My WiFiTest app’s splash screen started out as a standard 160x128 BMP.
(NOTE: The only image file format I can’t upload here appears to be BMP, so this here example pic is a “representation” of the original BMP.)
WiFiTestSplash

Anywho, the final PROGMEM stored const looks like this (truncated due to forum post limitations):

const unsigned short WiFiTestSplash[0x5000] PROGMEM ={
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0010 (16)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0020 (32)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0030 (48)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0040 (64)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0050 (80)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0060 (96)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0070 (112)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0080 (128)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x0090 (144)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,   // 0x00A0 (160)
//truncated for forum posting
};

Now, I know I’m getting a little deep with this, but there are ways to further optimize images within your app, especially if you go the uncompressed/BMP route with PROGMEM and you are pressed for app space. The biggest one is gonna sound pretty obvious once I tell you, but a lot of folks don’t think of it…even I need to address this in a future revision of my app (just been lazy and not out of space…yet haha): See all those 0x0000 in the array? Just black pixels…in fact, ~90% of the full 160x128 image is just black pixels. Such a waste of space! Same if most were white or any other solid color. Why not just mp.display.fillScreen(TFT_BLACK); (I do this anyway to clear the display) and then load a cropped version of my image in PROGMEM…just the portion that isn’t “background”…instead? Then one just needs to mp.display.pushSprite(x,y); onto the display starting at the appropriate (x,y) position.


Conclusion/Summary: There are a LOT of ways to do this, generally speaking. How, exactly, you would ultimately go about it depends on your goals, both narrow and short. For example, you could go all JPEG from SD and find your app/game runs SUPER slow. Or you could go all in-app/FLASH stored sprites to start and find halfway through development that you’ve run out of app space in flash and have to rework everything from the start!

If you could give a little more context as to what you are wanting to do and/or the type of app/game/expectations you have in mind, I’m sure we can write up a few more specific examples in code for you. BMP/Sprites are easy…JPEGs with the MAKERphone library (or JPEGDecoder library directly) loaded are easy, with a couple limitations…other formats are out of reach. Will some elements be static and some not? Will you be accounting for the layering of sprites or just overwriting as you go? Planning to have a lot of images or just a few?

Sorry, I suppose I’m attempting to teach instead of just show…it sounds like you might be writing your very first game on Ringo, possibly even Arduino/Cpp(C++) in general. I went through similar growing pains in the basics when I first started working with Arduino devices a handful of years ago, then again when I started working with Ringo, which is a custom Arduino with a LOT of attachments and related libraries to understand. Reading through the source code of the Ringo (along with a whole ton of other Arduino examples based on the libraries the Ringo code calls upon) was what allowed me to learn how to write my first apps on Ringo with the greatest level of ease and success.

Of course, the folks here in the forum are always here to help when you get stuck on something…especially @frankprindle and I, who give the general appearance that we are actually sleeping in makeshift tents on the CircuitMess forum’s front lawn or something. :rofl: We don’t know everything…but we try our best.

2 Likes

Thanks. By the way the wifi tester were really well. Great job.

1 Like

Thanks! I still have plans to add some more to it. I’ve gotta reoptimize a few things first - that splash being one of the things - because I’m already at about 1.5Mb of the 2Mb max. Tho, I think this is in part due to me using the “full fat” mp library instead of defining MPMINIMAL…I’d end up loading back half the libraries myself, but I’d drop a lot of bulk like their extra sprites too. “Six in one; half a dozen in the other.” :man_shrugging:

Yeah, Aidan, what TheWebMachine said.
But if you just want to put a .bmp into a sprite, there’s a member function for that: drawBmp().

Here’s the previous example modified to fill a sprite with the Snake game icon and push it around the screen:

#include <MAKERphone.h>

MAKERphone mp;
TFT_eSprite *mysprite;
TFT_eSprite *mysprite2;

//Initialization
void setup()
{
  //Initial startup
  Serial.begin(115200);
  mp.begin(1);    
  mp.inCall=1; //Keep mp awake
  mp.display.fillScreen(TFT_BLACK);
  mp.display.pushSprite(0,0); // Clear entire screen

  //Create sprite with multicolored centered square
  mysprite = new TFT_eSprite(&mp.tft);
  mysprite->setColorDepth(8);
  mysprite->createSprite(26,26);
  mysprite->setRotation(1);
  mysprite->fillScreen(TFT_BLACK);
  mysprite->drawBmp("/Snake/icon.bmp", 1, 0); //This BMP is 24x26 so first and last columns stay black
  mysprite->pushSprite(0,0); //Initial position
}

//Display sprite moving around.
void loop()
{
  static int x = 0, dx = 1;
  static int y = 0, dy = 1;

  //Clean up left-over top or bottom row before 1 pixel move
  mp.tft.fillRect(x,y+(dy>0?0:25),26,1,TFT_BLACK);
  
  x += dx; if(x >= 134 || x == 0) dx=-dx;
  y += dy; if(y >= 102 || y == 0) dy=-dy;
  mysprite->pushSprite(x,y); //Move sprite to new x,y
  delay(10);
}

Wow,

Even I learned some things while reading this! :sweat_smile:

You should really be a teacher! :smiley:

Robert

2 Likes

Thanks,

You guys are the best I have learned a lot.

2 Likes