Porting your favorite game to Android

    Creating a game is an exciting and informative process. This is especially noticeable when you do a remake of the "classics" yourself, guided by the ideas of the original and the dozens of hours spent on the campaign. I did not have any significant development experience for Android, so the creation of a working “as it should” application for the tablet at first looked pretty foggy, but it was no less attractive. If you have the time and opportunity, you can brush off the dust from old games, grease and glue it, adding support for "large" resolutions and it turns out that they look no worse than modern products laid out on the market, even with the RGB565 palette without an alpha channel. I assumed that there would be pitfalls and carefully hidden rakes that lay quietly during development, but they hit on the head painfully, it’s worth starting the game on real hardware. What was badly lacking was a debugger, and the emerging problems only strengthened the desire to achieve the goal. Under the cut will be a story about how it all worked.

    image

    It is worth immediately warning that it will probably be a story about bicycles, I haven’t come up with anything that doesn’t google on the Internet. Also, the Reader is unlikely to see new solutions or mega technologies, but will find tried and tested instructions for building an application using SDL1 / 2 for Android.

    Hello!
    The remake of the Caesar III © game did not begin at all as a separate project, but rather a set of fixes for the number of inhabitants, support for “large” permissions and research of the decompiled code of the original gamein search of easter eggs and undocumented modes of operation. And when the amount of recovered code exceeded half of the total, it became clear that you can try to restore the game. SDL1.2 was chosen as a rendering library, which has proven itself in other projects, and is also easy to learn and use. The remake at first was Linux-only, at the beginning of this year I moved to other platforms (Mac, Windows and Haiku), and then I got such a tablet , and my head occasionally thought: “it works on one Linux, it should work on another”.

    The attempt is a number of times, successful
    SDL version 1.2 “out of the box” does not have the ability to work under android, but there is a wonderful project libsdl-android, which allows using its environment and scripts to collect code that uses this library in an android application. The assembled application can download resources both from the Internet and unpack from the installer. Libsdl-android itself contains a large number of libraries that you may need, ranging from bzip2 and various codecs to the SDL itself and its environment SDL_image, SDL_mixer, ttf and others. If the game does not have platform-specific code, then porting takes several steps:
    0. install and configure as adt
    # / bin / bash
    ARCH = x86_64;
    NDK_VERSION = r9;
    SDK_VERSION = 20130729;
    [$ (TARGET_ARCH) = "i386"] && ARCH = x86;
    echo "Downloading the ndk ...";
    wget --quiet --continue dl.google.com/android/ndk/android-ndk- $$ NDK_VERSION-linux - $$ ARCH.tar.bz2;
    echo "Extracting the ndk ...";
    tar -xjf android-ndk - $$ NDK_VERSION-linux - $$ ARCH.tar.bz2 -C ~ /;
    echo "Downloading the sdk ...";
    wget --quiet --continue dl.google.com/android/adt/adt-bundle-linux- $$ ARCH - $$ SDK_VERSION.zip;
    echo "Extracting the sdk ...";
    ARCHIVE = `readlink -f adt-bundle-linux - $$ ARCH - $$ SDK_VERSION.zip`;
    cd ~;
    unzip -o -qq $$ ARCHIVE;
    echo "Configure paths ...";
    echo "export ANDROID_SDK = ~ / adt-bundle-linux - $$ ARCH - $$ SDK_VERSION / sdk" >> ~ / .bashrc;
    echo "export ANDROID_NDK = ~ / android-ndk - $$ NDK_VERSION" >> ~ / .bashrc;
    echo "export NDK_ROOT = \ $$ ANDROID_NDK" >> ~ / .bashrc;
    echo "export PATH = \ $$ PATH: \ $$ ANDROID_NDK: \ $$ ANDROID_SDK / tools: \ $$ ANDROID_SDK / platform-tools" >> ~ / .bashrc;

    1. cloning the libsdl-android repository

    2. copying the application sources to the libsdl-android project folder
    in my case, this is cloning the source through git
    cd commandergenius / project / jni / application
    git clone bitbucket.org/dalerank/caesaria

    3. creating a configuration file for building the game through libsdl-andlroid

    In the source folder, you need to create or copy the AndroidAppSettings.cfg file from another project, below I brought its contents to my config, the
    comments must be deleted, I also omitted the default settings
    # The application settings for Android libSDL port
    # Name that will be shown to the user
    AppName = “CaesarIA”
    # package name
    AppFullName = net.dalerank.caesaria
    # internal version of the application
    AppVersionCode = 1740
    # this version will be shown to the user
    AppVersionName = “0.3.1740”
    # here you can specify the local or remote archive that will be unpacked after installing
    AppDataDownloadUrl = " !! Game data is 100 Mb | cache.zip "
    # version of the library the application is built with (version 2.0 does not work)
    LibSdlVersion = 1.2
    # screen orientation
    ScreenOrientation = h
    # color depth, supported 16/24/32 - 16 is the fastest, the differences are not noticeable by
    eye VideoDepthBpp = 16
    # this and two The following flags are responsible for connecting OpenGL to applications,
    # since I do not use GL, there’s no
    reason to connect them either NeedDepthBuffer = n
    NeedStencilBuffer = n
    NeedGles2 = n
    # the flag is responsible for storing textures in RAM, if this did not cause problems on the PC, then
    # on android without this flag texture may not display
    SwVideoMode = y
    # mouse emulation, the flag is needed for the next two flags to work
    AppUsesMouse = y
    # handling several simultaneous clicks
    AppUsesMultitouch = y
    # emulating the right mouse button with the second finger
    tap AppNeedsTwoButtonMouse = y
    # cursor display
    ShowMouseCursor = n
    # should actually be yes here, but when this flag is turned on, the input field was not removed
    AppNeedsTextInput = n
    # permission to read from the drive
    AccessSdCard = y
    # if the cache is pulled from the Internet, then you need to set yes
    AccessInternet = n
    # the number of built-in virtual SDL buttons, I use my GUI, so there will be no buttons
    AppTouchscreenKeyboardKeysAmount = 0
    # delay SDL screensaver before starting the application
    StartupMenuButtonTimeout = 3000
    # under which abi the application will be
    built MultiABI = armeabi-v7a
    # here you need to specify the library. in addition to sdl, which are needed for the application to work
    CompiledLibraries = "sdl_mixer sdl_ttf lzma ogg"
    # additional compilation flags, I have RTTI enabled and exceptions
    AppCflags = '- O2 -finline-functions -frtti -fexceptions'
    # here are the folders where you need to look for the sources for assembly, in addition to the current
    AppSubdirsBuild = 'dep dep / smk dep / aes dep / lzma dep / bzip2 dep / libpng source source / vfs source / core source / gfx source / game source / gui source / sound source / scene source / pathway source / walker source / objects source / good source / city source / events source / world source / religion '

    4. setting the path to compile the desired application

    $ rm project / jni / application / src
    $ ln -s caeasaria project / jni / application / src

    5. apk assembly

    $. / changeAppSettings.sh -a
    $ android update project -p project
    $. / build.sh

    6. signing and installing the application on android

    If everything compiles successfully, then the MainActivity- [release | debug] -unsigned.apk file will appear in the commandergenius / project / bin folder, which you need to sign and install on the device.

    $ keytool -genkey -v -keystore rs.keystore -alias caesaria -keyalg RSA -keysize 2048 -validity 10000
    $ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore rs.keystore ~ ​​/ projects / commandergenius / project / bin / MainActivity- release-unsigned.apk caesaria
    $ mv ~ / projects / commandergenius / project / bin / MainActivity-release-unsigned.apk ~ / projects / caesaria.apk
    $ adb uninstall net.dalerank.caesaria
    $ adb install ~ / projects / caesaria.apk


    Pitfalls
    0. Definition of the environment: first you need to decide in which environment Windows, Linux or Linux Android will work .
    Solution: Check for ANDROID / __ ANDROID__ defines.

    1. Logs: you can view error messages and other output through abd logcat , but as it turned out standard tools like stdout / printf do not work, you can of course use the output of the log to a file and watch it already, but I wanted some more familiar debugging.
    Solution: connect the header file of android logs #include, and to display the message we use a function with the usual printf syntax. 2. Using OpenGL: if anyone needs OpenGL, then his close relative GLES lives on mobile platforms. Solution: connect instead of standard header files
    __android_log_print(ANDROID_LOG_DEBUG, CAESARIA_PLATFORM_NAME, "%s", str.c_str() );




    and , there are slight differences in the use of textures and rendering, but basically the code (the simple code that I used) works almost unchanged.

    3. Event handling: the SDL_MOUSEBUTTONUP event disappears when you move your finger across the screen, it could be a flaw in the libsdl-android library itself or I lost it somewhere. Sometimes it manifested itself in the absence of reaction of the interface elements to the actions you use, for example, after the movement, we stopped at the button, which in theory should go into the state if the mouse cursor is above it.
    Solution: Specifically for my application - when assembling for android, a forced update of the state of the elements under the cursor was added when the latter was moving.

    4. Small interface: the screen resolution of modern mobile devices is comparable to or higher than the resolution of a monitor used 10-15 years ago, but the physical dimensions are noticeably smaller, and therefore the user interface elements themselves look small and it will not always be convenient to use them.
    Solution: Altering the interface is a rather troublesome task and it is not always possible to maintain the original appearance.


    One move is equal to two fires (folk wisdom)
    It all started with the fact that one of the committers sent a link to the development branch, where he successfully launched the game using the relatively fresh SDL2 library, and before that, the version SDL1.2 - 2008 release was used. I must say that I myself was considering the possibility of switching to a new version, especially after watchinga list of changes that promised normal support for Mac and Android, which is called "out of the box." And then there was a mini-vacation at work, taking a bigger sledgehammer with a thicker guide and a big cup of coffee, I began to transfer the remake to a new “engine”.
    I don’t want to bore the reader with the technical details of the move, just at the very library with the advent of hardware support, the ideology of work changed, which at first caused certain difficulties until I got used to it. The move lasted for a week evenings and in the end was a correction of the remaining defects and graphic artifacts. The alterations were completed and the assemblies were prepared for the "large" OS, and again there was a need to re-read the manuals for building the application for Android, because libsdl-android is normally adapted to work with SDL1.2, and SDL2 support seems to be abandoned (as the authors themselves and write in readme)
    Hidden text
    The libsdl.org now has an official SDL 1.3 Android port, which is more recent and
    better suited for creating new applications from scratch, this port is focused mainly
    on SDL 1.2 and compiling existing applications, it's up to you to decide which port is better .
    Also this port is developed very slowly, although the same is true for an official port.


    I realized the veracity of this text when several hours were spent trying to start the port in the old configuration via libsdl-android. Well, negative experience is also experience: I will use the available tools.

    Attempt number two, not entirely successful
    SDL2 already contains all the necessary configs for building the application for android, after reading the article recommended on the official website, you can try to collect something. Again there will be a few steps, except for installing and configuring adt.
    0. copying the example from the SDL2 delivery
    $ git clone bitbucket.org/dalerank/caesaria
    $ hg clone hg.libsdl.org/SDL
    $ mkdir caesaria / android
    $ cp SDL / android-project caesaria / android
    $ mkdir caesaria / android / libs
    $ mkdir caesaria / android / data
    $ cp SDL caesaria / android / libs


    Why are all these copies made ??? to make it easier to read relative paths for libraries. In the android / libs folder there will be SDL and the company, in the android / data folder there will be an application icon.

    1. creating a folder structure for the project
    In the android / android-project / jni folder, create symbolic links to application components

    $ ln -s ../../libs/SDL SDL
    $ ln -s ../../libs/SDL_mixer SDL_mixer
    $ ln -s ../ ../libs/SDL_net SDL_net
    $ ln -s ../../src/dep/aes aes
    $ ln -s ../../src/source application
    $ ln -s ../../src/dep / bzip2 bzip2
    $ ln -s ../../src/dep/freetype freetype
    $ ln -s ../../src/dep/libpng libpng
    $ ln -s ../../src/dep/lzma lzma
    $ ln -s ../../src/dep/smk smk
    $ ln -s ../../src/dep/src src
    $ ln -s ../../src/dep/ttf ttf
    $ ln -s ../../src/dep/zlib zlib

    A little bit about what I wrote here:
    zlib is needed to build freetype, which in turn is needed for SDL_ttf and will be responsible for rendering fonts.
    The smk library is needed to play videos in the smack format, the videos of the original game are made in this format.
    Bzip, lzma and aes are needed to work with zip archives.
    libpng is required to load textures for the game.
    SDL, SDL_mixer, SDL_net are responsible for drawing, working with sound and network, respectively.
    application contains the source code of the game itself, which will be compiled into the libapplication.so library. The
    source files of the libmain.so library are located in the src folder, but for it the lace of java-calls above the c-code is already written, which will allow us to successfully start and please the user with a bright picture.
    Project settings and configs for ndk are already kindly provided by the authors of SDL2

    2. writing configs for assembling game components
    In order for the build system to see which libraries we need to work and assemble them, we need to write configs for them, like Makefile. With a high probability Android.mk will already be present in the library repository, or they can be found on the Internet. I had to add build configs for the game and the libsmk library.

    Android.mk for libsmk is very simple and will be understandable to people who are not related to android programming
    # smk / Android.mk
    LOCAL_PATH: = $ (call my-dir)

    include $ (CLEAR_VARS)

    LOCAL_MODULE: = smk
    LOCAL_SRC_FILES: = $ (subst $ (LOCAL_PATH) / ,, \
    $ (wildcard $ (LOCAL_PATH) / *. C))

    include $ (BUILD_SHARED_LIBRARY) The config


    contains an instruction to compile all files with the extension .с,found in the current folder (for libsmk it will be jni / smk)

    Similarly, the config is written to build the library, which will represent the game itself.

    # application / Android.mk
    LOCAL_PATH: = $ (call my-dir)
    include $ (CLEAR_VARS)

    LOCAL_MODULE: = application

    SDL_PATH: = ../../libs/SDL
    SDL_MIXER_PATH: = ../../libs/SDL_mixer
    SDL_NET_PATH : = ../../libs/SDL_net
    GAME_PATH: = $ (LOCAL_PATH)
    DEP_PATH: = ../dep

    LOCAL_C_INCLUDES: = \
    $ (LOCAL_PATH) / $ (SDL_PATH) / include \
    $ (LOCAL_PATH) / $ (SDL_MIXER_PATH) \
    $ (LOCAL_PATH) / $ (SDL_NET_PATH) / include \
    $ (LOCAL_PATH) / $ (FREETYPE_PATH) / include \
    $ (LOCAL_PATH) / $ (GAME_PATH) \
    $ (LOCAL_PATH) / $ (DEP_PATH) \
    $ (LOCAL_PATH) / $ (DEP_PATH) / libpng

    # Add your application source files here ...
    LOCAL_SRC_FILES: = $ (subst $ (LOCAL_PATH) / ,, \
    $ (wildcard $ (GAME_PATH) /*.cpp) \
    $ (wildcard $ (GAME_PATH) / core / *. cpp) \
    $ (wildcard $ (GAME_PATH) / vfs / *. cpp) \
    $ (wildcard $ (GAME_PATH) / objects / *. cpp) \
    $ (wildcard $ (GAME_PATH) / gui / *. cpp) \
    $ (wildcard $ (GAME_PATH) / city / *. cpp) \
    $ (wildcard $ (GAME_PATH) / gfx / *. cpp) \
    $ (wildcard $ (GAME_PATH) / events / *. Cpp) \
    $ (wildcard $ (GAME_PATH) / world / *. Cpp) \
    $ (wildcard $ (GAME_PATH) / pathway / *. Cpp) \
    $ (wildcard $ (GAME_PATH) / walker /*.cpp) \
    $ (wildcard $ (GAME_PATH) / good / *. cpp) \
    $ (wildcard $ (GAME_PATH) / religion / *. cpp) \
    $ (wildcard $ (GAME_PATH) / scene / *. cpp) \
    $ (wildcard $ (GAME_PATH) / sound / *. cpp) \
    $ (wildcard $ ( GAME_PATH) / game / * cpp)).

    LOCAL_SHARED_LIBRARIES: = SDL2 SDL2_mixer SDL2_net SDL_ttf pnggo lzma the bzip2 aes smk
    LOCAL_CPP_FEATURES + = exceptions
    LOCAL_CPP_FEATURES + = rtti
    LOCAL_LDLIBS: = -lGLESv1_CM -llog

    the include $ (BUILD_SHARED_LIBRARY)


    must also be understood in LOCAL_C_INCLUDES adds the paths where you need to search for header files, in LOCAL_SRC_FILES we add files with the source code,
    in LOCAL_SHARED_LIBRARIES we write application dependencies.

    flags rtti, exceptions are responsible for the use of RTTI and exceptions.


    3. assembly
    $ cd android-project
    $ android update project -p. -t android-15
    $ ndk-build V = 1
    $ ant [release | debug]
    $ ant install [r | d]


    Theoretically, after performing the described steps on the connected device or emulator, you will see the installed application.


    Rake
    1. Where to look for resources ???
    The location of resources depends on the specific OS implementation, but in most cases the application will have access to the folder / sdcard / Android / data / package_name / files , when using the path itself, there may be an access error or file search error.
    The full path to the application directory can be obtained through the SDL_AndroidGetExternalStoragePath () function defined in the SDL_system.h file
    2. Using the window creation flags.
    Combination SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS doesn’t work on all devices, remove SDL_WINDOW_OPENGL or SDL_WINDOW_BORDERLESS and see which flag crashes the program. I can’t explain what this behavior is connected with. With the flag SDL_WINDOW_SHOWN, it logs one by one on logs, as with all flags, but the probability of a crash is much less.
    3. Too many audio channels.
    There are crashes when calling the function SDL_mixer :: Mix_AllocateChannels (N> 16) with the error that it is impossible to initialize the sound. It is by reducing the requested number of channels, how correctly to solve this problem in this way I do not know.
    4. stlport vs gnustl
    Crash when using stlport, ran into this bug when traversing a vector using iterators on the Nexus 7 emulator (Android 4.0.3). Again, I can not explain the fact of this error, I decided to use gnustl when building the application.
    5. My kungfu is stronger than yours.
    Using a library with a name similar to the name of the one already in the system leads to the loading of someone else's library, which may not have the necessary functions. The error appeared because I was building my version of libpng.so, the solution was found on stackoverflow , it was fixed by replacing the library name libpng.so with libpnggo.so

    In conclusion ...
    It works! Almost no different from BB! Am i satisfied Not really!

    The thing is, I’m krivoruky, I don’t have skiing, but on the tablet the application turned out to be extremely slow (10-12 fps for an extremely simple picture, the result is dull), I think the fault here is in the hands and ignorance of equipment. SDL is an excellent library in both reincarnations, and many really good games use it, and it is also ported to android .

    The time spent on creating the port is not a pity for sure; some experience was gained and a lot of positive emotions when the game took off. For those who are still considering trying or not, definitely try it, do not delay for later!

    Z.Y. You can always see the development of the project here .


    Also popular now: