Tutorial: Dynamically-optimized sprites

Share on Facebook0Share on Google+7Tweet about this on TwitterShare on LinkedIn0

Today’s guest tutorial comes to you courtesy of Omid Ahourai, an indie game developer who goes by the alias “ArdentKid.” Please check out his work and blog at www.ardentkid.com.

Basic sprite sheets

If you’re not familiar with image sheets and sprite animation, please read the Image Sheets and Sprite Animation guides first, as this tutorial expands on the basic sprite methods discussed therein.

Image sheets are great because they’re not CPU-intensive, but there are a couple of downsides in comparison to vector animation. If used in excess, they can take up a lot of memory and result in large app file sizes. Also, image sheets are limited in that they usually consist of single-colored un-swappable assets. If we need more flexibility such as changing a character’s clothing or accessories, we would need to duplicate all of our animation frames for that character with those changes, taking up twice as much texture memory.

This tutorial will discuss a possible way to eliminate both of these bottlenecks. The secret is a more intimate relationship between code and animation. Let’s start with a sample image sheet of a kid:

This sheet is designed for basic sprite animation in Corona in which you load the image sheet, set up animation sequences, create a sprite object, and then play the desired sequence on the sprite. If you attempt the more advanced techniques I’m going to discuss below, I recommend that you first become familiar with the basic methods.

Splitting the animation elements

Since we’re going to have multiple kids of the same sprite appear on screen simultaneously, we want to give them varying colored wardrobes, different skin tones, etc. Fortunately, we can mimic some animations by tweening visual properties programmatically — scale, rotation, and translation (for simplicity, we’ll only code the foot animation and leave the arm frames intact).

With that in mind, let’s first “decapitate” the kid, add different colors, and piece him back together in a single display group. This is what the new image sheet would look like:

Notice that the file size of the new sheet is reduced from 225 KB to 109 KB and we gain the ability to use varied wardrobes and skin tones — over 50% reduction in file size and virtually no limit to how we can combine the elements!

Now we’ll declare the split elements as individual sprites in code:

Animating the elements

Once your character shows up with all of the elements in the correct place, we can animate his fancy footwork:

Here, we’re calling our own Runtime animation code instead of a sprite object play function. We’re using recursive transitions in this tutorial, but if you want the arms to sync up with the feet properly, you’ll need to do a bit more work (I’ll leave it out for now). Otherwise, this is a great start!  We now have access to several different colors using the same amount of memory. Could it get any better than that? Well, yes, it can!

Tinting the elements

We can nearly eliminate static colors completely using programatic tinting on each part — that is, using Corona’s object:setFillColor() method. We’ll make all the body parts white and use shades of grey for variance. Here’s what that looks like in image sheet format:

And here’s how we tint the sprite objects in code:

We’re now able to randomize or use any color we want, on the fly! The downsides here are minor, depending on your needs, but there are a few. First, we are compromising a few processor cycles because we have to tint each sprite element every time we play a different animation sequence for the first time. Second, our character movement requires more work from the CPU, since we’re moving each individual body part instead of as one whole. Lastly, we have to restrict a single color to each part. For example, a rainbow-colored hat isn’t possible, but we could get various results by using a color other than white or grey.

In summary

I’ve found that performance is exceptional using this method, especially with modern mobile devices — and the benefits of using much less texture memory can help streamline your app immensely.  I’ll emphasize again that we have reduced the texture memory required for this “beachboy” character from 225 KB (original sheet) to 109 KB (elements separated) all the way down to an ultra-efficient 12 KB. That’s a 95% reduction in texture memory!

Additionally, we have gained the ability to control each element independently. You can animate one arm, both arms, and even run unique animation sequences for the limbs like “punch” and “kick.”

Finally, we can now tint/color each character element individually and change it on the fly.

In conclusion, dynamically-optimized sprite sheets can be a formidable addition to your coding arsenal and should be considered for advanced sprite integration.

Share on Facebook0Share on Google+7Tweet about this on TwitterShare on LinkedIn0

Brent Sorrentino serves as a full-time Developer Evangelist for Corona Labs, assisting developers in the forums, maintaining documentation/guides, and creating samples which highlight core features of Corona SDK.

This entry has 17 replies

  1. buder says:

    Very nice tutorial. Good job, Omid!

  2. Very nice tutorial! Thanks!

  3. Great article, thanks.

    Just a side-note: since iPad 3 was released (the one with Retina display), the non-WiFi download size for an app was raised to 50MB. Gives a bit more flexibility.

  4. Brian Burton says:

    Great timing! I was just introducing sprite-sheets to my class!

  5. Chris Leyton (SegaBoy) says:

    Thank you Corona and thanks ArdentKid – I follow your tutorials on your blog, and like I said on the forums you really know your stuff.

    This is exactly the type of a slightly more advanced tutorials we need to see on here.

    Great stuff.

  6. Haakon says:

    The file size itself doesn’t really matter – it does not affect the texture memory used. The sprite sheet’s dimension is what matters, and it is scaled up to the closest powers of 4. Or has something changed that I’m not aware of?

  7. Arturitu says:

    Brent, are you working on implementing “Spriter” for Corona to make this kind of animations? Because Spriter tool looks amazing for Dynamically-Optimized aminations

  8. Harry Kim says:

    Great help for me…!
    Thank you.

  9. Brent Sorrentino says:

    Hi Arturitu,
    Personally, I don’t use a 3rd-party sprite packing/optimization tool, of which Spriter is just one of them. As long as a sprite optimization tool supports output to Corona in the current sprite API methods, it should be fine to use it for this tutorial’s advanced method(s).

  10. Brent Sorrentino says:

    Hi Haakon,
    Sorry about the slight confusion (Omid wrote this tutorial, but I helped edit it and I should have clarified that point). Anyway, I believe you’re correct. The actual “file size” isn’t directly indicative of the texture memory used by OpenGL. While I’m not an authority on OpenGL and low-level engineering issues, there seems to be a fair amount of debate about whether developers should use “Power-of-2” image(sheet) sizes, or if “NPoT” (Non-Power-of-2) sheet sizes are acceptable. The consensus that I’ve found is that it’s “better” to use “PoT” sheet sizes, but NPoT is fine too (it has been supported since OpenGL 2.0). Corona allows for both… personally, I use PoT image sheet sizes, but it’s not required in Corona.

  11. dellos says:

    how can i remove not necessary color in a frame ????

    • Omid Ahourai says:

      dellos- I’m not sure what you mean exactly. I think you’re asking if you can set (or remove) the color during specific frames of a sprite animation. To do that, you could define an enterFrame function like this:

      function changeColorOnFrame(event)
      if (sprite.frame == whateverFrameNumber) then
      sprite:setFillColor(255) –remove color tint
      Runtime:addEventListener(‘enterFrame’, changeColorOnFrame)

  12. Nick says:

    Really great tutorial! I just realized something though, in the second and final spritesheet there are only frames for moving downwards which means that final file size would probably more like 12*4kb.

    • Juan Pulido says:

      No Nick, Ardenkit says: Fortunately, we can mimic some animations by tweening visual properties programmatically — scale, rotation, and translation (for simplicity, we’ll only code the foot animation and leave the arm frames intact).
      This means that, with the sprite of 12k, we can get movement in four directions.


      artjc 🙂

  13. ArdentKid says:

    Thanks Juan
    Yes, the sprites parts can be rotated to make him appear sideways as well in some cases. I had a bit of difficulty finding the exact first spritesheet that correlated most closely, at the time of the article’s writing. But the file sizes would be close, if not the same.

  14. Timea says:

    Finally a great tutorial on animating elements using sprite sheets! I was looking for such for quite some time, happy to find it!

  15. romin says:

    A very great and useful tutorial but unfortunately very complicated for newbies.
    Is there any kind soul who can make a video tutorial on this important subject???

    Thanks in advance.