Spritesheets Revisited – Game sprites with Inkscape + Gimp

I’ve lately been doing some work with the (awesome) Unity game engine, prototyping a 2D platformer using version 4.3’s new 2D mode, and as part of that I needed to create some sprite-sheets for game-related animations and the like.

Inkscape Screen Shot
Spritesheet fodder!

As I’ve mentioned before, my tools of choice for graphical work are Inkscape and The GIMP, so I needed to  put together a workflow that would allow me to draw vectors in Inkscape and then export pixels to Gimp, where I would create suitable spritesheets for import into Unity.

I don’t want delve too deeply into the Inkscape/Gimp interaction in this post (I’m still stuck on Windows at the moment, so I just followed this post to get Inkscape’s “Save as XCF with layers” feature working – thanks to Chris Hillbig for the instructions). So, skipping over that part, what I ended up with was a GIMP image, made up of layers, each layer being an animation frame. I now needed to make this into a flat spritesheet, suitable for PNG export to Unity.

If all this sounds a bit familiar, that’s because it’s very reminiscent of this post from September 2012 (if you’ve read that post, then thanks for sticking around for this long!), when I was busy putting retroify.me together and needed to easily create spritesheets, in GIMP, from layered images. Of course, I initially just used the GIMP script I developed then to create my sheets, and while it did work, it wasn’t perfect. Why? Well, to explain that, I think a little background is in order.

The way retroify.me works, it’s easy (and efficient) for us to use very wide spritesheets with a single row of sprites, and so the script I wrote way back in 2012 does just that – it creates a sheet that’s arbitrarily wide, with the overall height being the height of a single sprite. For Unity, however, this is non-optimal. Like most game-engines, and anything else that works directly with video hardware to provide high-performance graphics, Unity prefers its assets to be sized by powers of two. While it will happily chomp away on an image that’s 5376 pixels by 256, and even handle the slicing for me, this can result in some odd visual artifacts, and will prevent proper optimisation of the graphics in the game by the engine. I really needed to revisit my old script, and add in the ability to create images with multiple rows, to allow my spritesheets to fit neatly into power-of-two-sized images.

And so, it was back to Scheme. As I mentioned in the old post, I’m not a Scheme programmer, but it’s always fun to play around in an unfamiliar language. I spent a little while hacking around, re-reading the GIMP script API, and generally having fun, and the end result is a new script that does everything I need. In the spirit of the previous post, here is the updated code:

; Script to convert image layers to a sprite sheet.
; Copyright (c) 2012-2014 Ross Bamford.
; http://www.apache.org/licenses/LICENSE-2.0.html
(define (script-fu-unityspritesheet img sheet-size-opt)
  (let* (
      (layer-count          (car    (gimp-image-get-layers img)))
      (layer-ids            (cadr   (gimp-image-get-layers img)))
      (sprite-width         (car    (gimp-image-width img)))
      (sprite-height        (car    (gimp-image-height img)))
      (spritesheet-width    (expt 2 (- 12 sheet-size-opt)))
      (sprites-per-row      (/ spritesheet-width sprite-width))   
      (rows-needed          (ceiling(/ layer-count sprites-per-row)))

      ; Create new image with appropriate size
      (spritesheet-image (car (gimp-image-new 
                                      spritesheet-width
                                      spritesheet-width 
                                      RGB)
                          )
      )
    )

    ; Copy and move each source layer appropriately
    (let (
        (i 0)
        (y 0)
        (x 0)
      )

      (while (< i layer-count)
        (let (
            ; Copy the layer...
            (new-layer (car(gimp-layer-new-from-drawable 
                                      (aref layer-ids (- layer-count (+ i 1))) 
                                      spritesheet-image)
                        )
            )                     
          )

          ; Insert, position and make it visible
          (gimp-image-insert-layer spritesheet-image new-layer 0 -1)        
          (gimp-layer-translate new-layer x y)
          (gimp-item-set-visible new-layer TRUE)
        )

        ; Update i/x/y for next iteration...
        (set! i (+ i 1))
        (set! x (+ x sprite-width))
        
        (if (>= x spritesheet-width)
            (begin
                (set! x 0)
                (set! y (+ y sprite-height))
            )
        )
      )
    )

    ; Merge layers and rename
    (gimp-image-merge-visible-layers spritesheet-image CLIP-TO-IMAGE)
    (gimp-item-set-name (aref (cadr (gimp-image-get-layers spritesheet-image)) 0) "spritesheet")

    ; Create view for new image
    (gimp-display-new spritesheet-image)
  )
)

(script-fu-register "script-fu-unityspritesheet"
                    "Create Unity spritesheet"
                    "Create a Unity-compatible spritesheet from current image layers"
                    "Ross Bamford"
                    "Copyright (c)2012-2014 Ross Bamford"
                    "2012-2014"
                    "*"
                    SF-IMAGE    "Image"         0
                    SF-OPTION   "Size"          '("4096" "2048" "1024" "512")
                )

(script-fu-menu-register "script-fu-unityspritesheet"
                         "<Image>/Sprites")

(Once again, thanks for the folks at ToHTML.com for the syntax highlighting 🙂 )

Advertisements

Spritesheets with Gimp – An adventure in Scheme

One of the coolest Android projects I’m involved with at the moment is a new strategy game, and one of the coolest things about it is it’s retro-look 8-bit style graphics. Growing up in the 80’s I obviously got a lot of exposure to the unique look of games in the 8-bit era, and I have fond memories of hacking away on the C64 and Spectrum, trying to represent real objects in a tiny number of pixels. So I’m really loving the resurgence of these techniques on current mobile devices.

Although there are a few “pixel art” graphics tools out there, we’re actually using the gimp to create our art (it’s what we know and love) and the game itself is using a layered sprite system to allow us to keep raw data to a minimum and also to make it easy for us to use different permutations of layers to create different sprites. For example, all the characters in the game are built from a small set of sprite layers which we just overlay in different combinations.

Perhaps an example is in order. Consider the rather snazzy (and somewhat surprised) looking individual on the left. He’s actually composed of a number of different layers in gimp, which are grouped and arranged in such an order that when one layer from each group is visible, they combine to create a character (in this case a mildly-shocked purple-haired gentleman in a blue sweater).

There are a number of these files, some covering people (as in this example), some with vehicles, some with background objects, and so on. And in each one, there are many layers each with a different sprite image. Not all are overlaid in this way, but they all use gimp’s layer features. Continuing the example, the image on the right shows you the gimp layers palette with the various layers that comprise the purple-haired dude.

If you look carefully you’ll see that our chap comprises a number of layers, with helpful names such as “skin_med” and “hair_02_purple”. All this works great while we’re actually drawing stuff, but once we come to actually put it into the game, we’re obviously not importing layered XCF  files – instead we break up the layers into a sprite sheet which flattens all the layers out and arranges them side-by-side in a single image (traditional sprite-sheets actually use multiple rows and columns, usually to give a fixed width and height as required by the hardware – we’re not doing this at the moment, although we do support it in the graphics engine). Turns out that this is not especially well supported in gimp.

Originally, we were using the built-in filmstrip plugin to accomplish this, but this was always a stop-gap solution – filmstrip is designed for use with photos, as a visual effect. As such, it actually adds virtual sprocket holes, frame numbers, and other assorted things. All nice if you’re making a montage of your week in Barbados, but no so great if you’re trying to get an exact-sized image you can then index into by pixel from your code. It is possible to stop these effects by tweaking the settings, but they don’t get saved across sessions, and in any event it doesn’t support transparent backgrounds so there’s always an extra select by colour/delete step once the sheet is made.

Luckily, Gimp supports scripting (via it’s Script-Fu interface to the Scheme language), so it was a fairly simple matter to knock up a script to take care of the conversion from layered image to flattened spritesheet. Gimp also supports Python scripts, but given the choice between a whitespace-sensitive language and a Lisp-like dialect, I’ll take the latter every time. It’s been a while since I last wrote Scheme, making it an ideal time to brush up some skills. A definite win-win situation!

After a little hacking around the API (Script-Fu has changed a bit since I last used it, but not so much that it’s unrecognisable) I had a basic working script, which I could call at any time through a menu item (on a new ‘Sprite’ menu I added, in anticipation of further scripts and plug-ins the guys have already asked me for). Going back to our example, the script creates a spritesheet in a new image, which for the guy above looks like:

Obviously, this example is so contrived as to be pointless. The real files have many more sprite tiles than this – I don’t think I’d be too popular if I started dumping all our graphic artifacts on here – but you get the idea.

And just in case you’re doing something similar and might find this useful, here’s the code. Call it a Saturday present from me, to you.

; Script to convert image layers to a sprite sheet.
; Copyright (c) 2012 Ross Bamford.
; http://www.apache.org/licenses/LICENSE-2.0.html
(define (script-fu-spritesheet img)
  (let* (
      (layer-count (car  (gimp-image-get-layers img)))
      (layer-ids   (cadr (gimp-image-get-layers img)))
      (sprite-width(car  (gimp-image-width img)))
      (spritesheet-width (* sprite-width layer-count))

      ; Create new image with appropriate size
      (spritesheet-image (car (gimp-image-new 
                                      spritesheet-width
                                      (car (gimp-image-height img)) 
                                      RGB)
                          )
      )
    )

    ; Copy and move each source layer appropriately
    (let (
        (i (- layer-count 1))
      )

      (while (> i -1)
        (let (
            (new-layer (car(gimp-layer-new-from-drawable 
                                      (aref layer-ids i) 
                                      spritesheet-image)
                        )
            )                     
          )

          (gimp-image-insert-layer spritesheet-image new-layer 0 -1)        
          (gimp-layer-translate new-layer (- spritesheet-width (* sprite-width (+ i 1))) 0)
          (gimp-item-set-visible new-layer TRUE)
        )

        (set! i (- i 1))
      )
    )

    ; Merge layers and rename
    (gimp-image-merge-visible-layers spritesheet-image CLIP-TO-IMAGE)
    (gimp-item-set-name (aref (cadr (gimp-image-get-layers spritesheet-image)) 0) "spritesheet")

    ; Create view for new image
    (gimp-display-new spritesheet-image)
  )
)

(script-fu-register "script-fu-spritesheet"
                    "Create spritesheet"
                    "Create a spritesheet from current image layers"
                    "Ross Bamford"
                    "Copyright (c)2012 Ross Bamford"
                    "2012"
                    "*"
                    SF-IMAGE    "Image"         0)
(script-fu-menu-register "script-fu-spritesheet"
                         "<Image>/Sprites")

(Caveat: I’m not primarily a scheme programmer. If you are, and you find my style abhorrent, well, tough :D)

(Postscript: Turns out that WordPress.com doesn’t support Scheme in it’s sourcecode tags! Thanks to the guys over at tohtml.com for their highlighter, even though I had to pretend this was lisp to get it highlighted anything like properly.)