19 Aug

A weekend boxed inside a dylib

I’ve dedicated the entire weekend to heterogeneous yet related tasks. First, I started off by trying to build NagiQ, our first published game, on OS X Mavericks. By “build” I mean to produce a binary complying with the requirements of the Mac App Store. You know what I mean: well-formed directory hierarchies inside the .app, proper icons and .plists, code signing, etc. For the record, NagiQ is a word game created with Ren’Py, a visual novel engine which is, in turn, built with Python. At the time of release a lot of folks shared their thought regarding our election of Ren’Py to create our word game: it was, to say the least, an unorthodox choice. However, almost 3 years after its initial release, NagiQ is still running fairly well on Windows, Mac and Linux, thanks to the wonderful capabilities of Ren’Py for multi-platform deployment.

Building NagiQ on Mavericks is easy, it amounts to just a single click on a Ren’Py option[1]. However, turning the generated .app into a binary suitable for the Mac App Store has proven to be a daunting task. You have to organize the directory structure of the Python Framework distributed with the game. You have to circumvent the writing of Python .o files in the .app directory, a big no-no for sandboxed apps. You have to retrieve the proper directory to save user and game data (~/Library/Application Support/NagiQ is not allowed). Etcetera. Right on the middle of such etcetera lies the requirement of communication with a few dynamic libraries needed by NagiQ to satisfy several functional demands.

As you surely know, dynamic libraries = dylibs on Mac. Taking into account that Ren’Py is a citizen of the Python world, we use the ctypes library to communicate with our dylibs from inside the game. An important lesson I learned during this weekend is that you have to be very careful when dealing with dylibs and ctypes. First, you have to verify that your dylib and the Python version you’re running are compatible. Is your dylib a 32 or a 64-bits binary? Your Python instance must be apt to properly load and call functions of your dylib, or you will spend a lot of time trying to sort out segmentation faults.

Typing python on a terminal of my Mac launches the 64-bit version of the interpreter by default. If you want to execute the 32-bit version, run this in your shell before launching python:

export VERSIONER_PYTHON_PREFER_32_BIT=Yes

After you have launched the proper version of Python, you can import ctypes to load and communicate with your dylib. However, don’t take this communication lightly. Pay special attention to the type of the arguments and return values of your functions. For instance, if you’re invoking a function of your dylib which requires a char* value, then you have to wrap your argument in the type c_char_p. Let’s see another example. Suppose you want to get the proper location to save the data of your game. Of course, as a good programmer, you don’t want to hardcode such path. Instead, you’ll be asking the operating system for it, the right way. Let’s create a tiny, demonstrative Objective-C library (demolib.m) for this:

#import <Foundation/Foundation.h>
const char* findAppDir()
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(
        NSApplicationSupportDirectory,
        NSUserDomainMask, YES);
    NSString *basePath = [paths objectAtIndex:0];
    const char *convertedPath = [basePath UTF8String];

    return convertedPath;
}

Compile with:

clang -dynamiclib -framework Foundation demolib.m -o demolib.dylib

Then you can call your function from Python:

Python 2.7.5 (default, Mar  9 2014, 22:15:05)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> lib = ctypes.cdll.LoadLibrary("demolib.dylib")
>>> findAppDir = lib.findAppDir
>>> findAppDir.restype = ctypes.c_char_p
>>> findAppDir()
'/Users/yourusername/Library/Application Support'
>>>

This little function will prove useful later, when I resume the building of NagiQ. However, the weekend is already over and I have yet to implement several adjustments for DragonScales, our next game soon to be released. The delight of working with dylibs, sandboxes, etc., will surely be the subject of future happy weekends.

  1. [1]This single-step build has grown to be the Ren’Py feature I love the most.
25 Feb

RenPy: Games, Rationale and The Power of a Well-Crafted Engine

More than 1 year after releasing NagiQ, I’d like to share a few bits of our experience with NagiQ, and of course, with the engine we used to build it: RenPy. First, we want to express our gratitude to RenPy’s author, PyTom, and to all of the RenPy contributors, including the kind people of the RenPy forums, who are always ready to provide useful feedback and answer our questions.

RenPy is a wonderful engine. What I like the most about RenPy is its robustness and cross-platform wonders. It feels like it might run anywhere and without ever crumbling. In fact, we released NagiQ for Windows, Mac and Linux, and deployment was incredibly easy: RenPy smoothly abstracted the differences among operating systems, and the game runs and behaves exactly the same way on all these 3 platforms. Besides, I think that several games built with RenPy have found their way into Steam and Google Play. I especially recommend RenPy for people creating its first game (NagiQ, developed in 2011, was our first title), because RenPy allows you to focus on what really matters for your game (gameplay, spaces, visuals and story.) As suggested, abstraction is a key concept for RenPy: abstraction is everywhere, it’s high-level and indeed powerful. For instance, right now I’m thinking of RenPy’s ATL, which is a very powerful way for showing displayables and applying several visual transformations (rotation, zoom, etc.) to them. And that’s sweet, really sweet. More so if you’re submitting games to publishers for the first time.

I don’t know of an engine better than RenPy for creating visual novels, and generally speaking, games in which pairs (image, text) are first-class citizens for scene design. NagiQ isn’t a visual novel, but a word game. As a word game, NagiQ is mostly based on simple images and strings. Handling images and strings with RenPy is a breeze. And not just strings. In RenPy you can show images and play music with a single line of code. A single line. You work fast, and you work safe. Further on, you might apply a tiny bit of ATL for achieving a richer presentation.

As RenPy is open-source, you can browse and study its code. You’ll end up learning quite a lot. Furthermore, knowing how things are done internally will allow you to find pathways for implementing any specific feature you might need. For example, several publishers require special handling of the quit message (Alt+F4 on Windows, CMD+Q on Mac OS X.) By knowing RenPy internals you’ll be able to handle this message as needed.

Overall, organizing your game’s scenes in RenPy is pretty straightforward. And you can even use neat transitions between scenes with a single line of code too. Normally, you’ll want a splash scene, a main menu scene, a level selection scene, and of course, the scenes implementing your main gameplay. Several of these scenes might require a lot of UI components whose creation is a piece of cake with RenPy. You’ll have the valuable help of Screens and Screen Language, which will simplify implementation and handling of your game’s screens.

RenPy is extensible. For the levels of NagiQ we required boards made of clickable cells. For doing that, we created a custom Board class in Python. And, again, RenPy proved golden: you can extend RenPy as you wish. We coded our Python class and integrated it to a RenPy scene pretty easily.

Summarizing, RenPy promotes creativity. It’s a very flexible and dynamic system, which doesn’t impose tight restrictions on the way you show and structure your content. The powerful ATL and the Screen Language are dreamy features, an outstanding support for unbounded creativity.

In retrospective, RenPy was indeed the best choice for building NagiQ: it was such a fun experience. And that’s what really matters. Long life to RenPy.