Creating 9-Patch Textures in SDL - Game DevLog Series #2

Last week I wrote a bit about everything and my progress so far. I just want to continue this series and write loosely about my experience in writing my own little game engine.

What is working right now:

  • Game-Loop
  • Command-Pattern  (described here: Link)
  • Random Map Generation with Noise (FastNoise)
  • "Infinite"-Map with Chunks which get rendered when you're near.
  • Fast text rendering with sprite sheet
  • *.tmx/xml importer for maps made with Tiled
  • Camera movement on the map (of course)

This week I worked a bit on the GUI. I really was not looking forward to this, but there comes a time you just need some buttons, health-bars etc.

At first I was thinking of just taking a "ready" to use GUI-Library. But I was thinking I don't need everything they provide.

Here is a little list of SDL Gui Libraries so you don't have to google them... (write in the comments if you've found some more)

So I went with my own implementation. Currently I have some basics functionality, creating layout containers (which can rearrange their child elements), buttons and also an text input field.

Right now I was working on an 9-Patch texture for my UI-Elements. Basically you have a texture like:

9-Patch Texure
You get the corners and edges.
Testing the UI in my game.

And then you patch them all together to an button with your desired width and height.

Button with different width and height.

For the corners and edges the coordinates are like this:

//CORNERS
    int x_np = ninePatch->x;
    int y_np = ninePatch->y;

    int x_r = ninePatch->w - nine_patch_tile_s + x_np;
    int y_bot = ninePatch->h - nine_patch_tile_s + y_np;

    int xw_tile = x_np + ((ninePatch->w/2) - (tile_size / 2));
    int yh_tile = y_np + ((ninePatch->h/2) - (tile_size / 2));

    src_rects[NW].x = x_np;
    src_rects[NW].y = y_np;

    src_rects[N].x = xw_tile;
    src_rects[N].y = y_np;

    src_rects[NE].x = x_r;
    src_rects[NE].y = y_np;

    src_rects[E].x = x_r;
    src_rects[E].y = yh_tile;

    src_rects[SE].x = x_r;
    src_rects[SE].y = y_bot;

    src_rects[S].x = xw_tile;
    src_rects[S].y = y_bot;

    src_rects[SW].x = x_np;
    src_rects[SW].y = y_bot;

    src_rects[W].x = x_np;
    src_rects[W].y = yh_tile;

    src_rects[CENTER] = {xw_tile, yh_tile, tile_size, tile_size};

with:

SDL_Rect src_rects[9]{};

enum orientation { ///< clockwise then center
    NW,
    N,
    NE,
    E,
    SE,
    S,
    SW,
    W,
    CENTER
};

I put this all in a class called NinePatchDrawable. Do this at your liking, my NinePatchDrawable has a Texture and what I do is I check if I need a resize or if the texture has never been created, I then render the different little edges on a texture. I do it like this:


    target_texture->setBlendMode(SDL_BLENDMODE_BLEND);

    auto *sdl_renderer = renderer->getSDLRenderer();
    auto *sdl_target = target_texture->getSDLTexture();
    auto *sdl_src = src_texture->getSDLTexture();

    //Setting Render Target
    if(SDL_SetRenderTarget(sdl_renderer, sdl_target) < 0) {
        spdlog::error("[NinePatchDrawable] could not set texture as rendering target");
    }
    SDL_RenderClear(sdl_renderer);

    calc_size();

    SDL_Rect dest;
    dest.w = step_x;
    dest.h = step_y;

    for (int y_l = 0; y_l < y_max_step ; ++y_l) {
        for(int x_l = 0; x_l < x_max_step; ++x_l) {
            dest.x = step_x * x_l;
            dest.y = step_y * y_l;
            orientation o = get_orientation(x_l, y_l);
            SDL_RenderCopyEx(sdl_renderer, sdl_src,
                             &src_rects[o], &dest,
                             0.0, nullptr, SDL_FLIP_NONE);

        }
    }

    SDL_SetRenderTarget(sdl_renderer, nullptr); // reset Render Target
    spdlog::info("[NinePatchDrawable] rendering ninePatch");

There are different, and I bet better methods of doing this. If you do this in any other way just comment this blog post :) I'm really happy for your feedback.

Future Ideas are that you also define a tile with a width=1 for the edges so you can size the textures with pixel resolution. I have to define a button with a multiple of the tile size of my edges/corners. But for me that's enough :) I don't want to over engineer this, If I really don't need it.

So if you like this series to continue please write a comment below :)


The Texture I used can be found on https://www.kenney.nl/  he does a great job and I use the textures from him for prototyping. Really awesome work there.

9-Patch Android: https://developer.android.com/studio/write/draw9patch

More on 9-Patches for Android: https://developer.android.com/guide/topics/graphics/drawables#nine-patch