📜 ⬆️ ⬇️

Porting your favorite Android game

Making the game process is exciting and informative. This is especially noticeable when you make a remake of the “classics” yourself, guided by the ideas of the original and dozens of hours spent on the campaign. I didn’t have any significant development experience for Android, so the creation of a working “as it should be” application for a tablet at first looked rather vague, but it’s no less attractive. Given the time and opportunity, you can shake off the dust from old games, grease and glue, adding support for “large” resolutions and it turns out that they look no worse than modern products laid out on the market, even with an RGB565 palette without an alpha channel. I assumed that there would be pitfalls and a carefully hidden rake, which lie quietly during development, but are painfully thrashed on the head, it is worth running the game on real hardware. What was badly missing was a debugger, and the problems that arose only reinforced the desire to achieve the goal. Under the cut there will be a story about how it all worked.

image

It is necessary to immediately warn that it will probably be a story about bicycles, I have not invented anything that does not google on the open spaces of the “Internet”. Also, the Reader is unlikely to see new solutions or mega technologies, but will find tested instructions for building an application using SDL1 / 2 for Android.

Hello!
The Caesar III © remake did not begin as a separate project, but rather a set of fixes for the number of residents, support for “large” resolutions and research of the decompiled code of the original game in search of Easter eggs and undocumented modes of operation. And when the amount of the restored code exceeded half of the total, it became clear that you can try to restore the game. SDL1.2 was chosen as the rendering library, which has proven itself in other projects, and is still easy to learn and use. At first, the remake 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 from time to time my head had thoughts “it works on one Linux, it should work on another”.
')
Attempt number once, 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 your environment and scripts, to collect code that uses this library in an android application. The collected 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 SDL_image, SDL_mixer, ttf, and other environments. If the game does not have platform-specific code, then porting takes several steps:
0. installation and configuration 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 into the libsdl-android project folder
in my case, this is source code cloning via git
cd commandergenius / project / jni / application
git clone bitbucket.org/dalerank/caesaria

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

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

4. setting the path for compiling the desired application

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

5. assembly apk

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

6. Signing and installing the application on Android

If everything compiles successfully, 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


Underwater rocks
0. Definition of the environment: first you need to decide in what environment Windows, Linux or Linux Android will work.
Solution: Check the presence of defines ANDROID / __ ANDROID__.

1. Logs: you can watch 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 the file and watch it already, but I wanted some more familiar debugging.
Solution: connect the header file of the android logs #include <android / log.h> , and use the function to display the message
__android_log_print(ANDROID_LOG_DEBUG, CAESARIA_PLATFORM_NAME, "%s", str.c_str() );

with familiar printf syntax.

2. Using OpenGL: if anyone needs OpenGL, then its close relative GLES lives on mobile platforms.
Solution: connect instead of the standard header files <GLES / gl.h> and <GLES / glext.h>, there are slight differences in the use of textures and rendering, but basically the code (the simple code 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. It sometimes manifested itself in the absence of reaction of interface elements to user actions, for example, after moving, they stopped at a button, which, in theory, should go to the state if the mouse cursor is above it.
Solution: Specifically for my application - when building for android, a forced update of the state of the elements under the cursor was added while the latter was moving.

4. Small interface: the screen resolution of modern mobile devices is comparable to or exceeds the resolution of the monitor used 10-15 years ago, but the physical dimensions are noticeably smaller, which is why the user interface elements themselves look fine and it will not always be convenient to use them.
Solution: Alteration of the interface, which is quite troublesome and it is not always possible to retain the original look.


One move is equal to two fires (popular wisdom)
It all started with the fact that one of the committers sent a link to the development branch, where they successfully launched the game using the relatively recent SDL2 library, and before that they used version SDL1.2 - 2008 of the release. I must say that I myself considered the possibility of switching to a new version, especially after viewing the list of changes , which promised normal support for Mac and Android, which is called out of the box. And then there was a mini-vacation at work, having taken a sledgehammer with a thicker guide and a large cup of coffee, I began to translate the remake to a new “engine”.
I don’t want to bore the reader with the technical details of the move, it’s just that the library itself changed the ideology of the work with the arrival of hardware support, which at first posed certain difficulties while I was not used to it. The move stretched for a week of evenings and at the end was a correction of the remaining shortcomings and graphic artifacts. The modifications were completed and the builds for the “big” OS were prepared, and again there was a need to re-read the instructions for building the application for Android, because libsdl-android is normally adapted to work with SDL1.2, and SDL2 support seems to be abandoned (what the authors themselves and write in readme)
Hidden text
SDL 1.3 Android port now, which is more recent and
this port is focused mainly
on SDL 1.2 and compiling existing applications.
It is a port that has been developed very slowly.


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

Attempt number two, not entirely successful
SDL2 already contains all the necessary configs to build an application for android, after reading the article recommended on the official website, you can try to build something. Again, there will be several steps, except for installing and configuring adt.
0. copying an example from the delivery of SDL2
$ 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

What are all these copies for ??? to make it easier to count relative paths for libraries. The SDL and company will be in the android / libs folder, and the application icon will be in the android / data folder.

1. creating a folder structure for a 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 video in the smack format, in this format the videos of the original game are made.
Bzip, lzma and aes are needed to work with zip archives.
libpng is required to download textures for the game.
SDL, SDL_mixer, SDL_net are respectively responsible for drawing, working with sound and the network.
application contains the source of the game itself, which will be compiled into the library libapplication.so
The source code of the library libmain.so is located in the src folder, but for it the lace of java-calls is already written over the c-code, 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 to build game components
In order for the build system to see which libraries we need to work with and compile them, you need to write configs for them, like Macfile. With 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 config files for the game and the libsmk library.

Android.mk for libsmk is very simple and will be understandable to people not related to programming for android
# 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 instructions to compile all files with the .c extension found in the current folder (for libsmk this will be jni / smk)

Similarly, the config is written to build the library that 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 bzip2 aes smk
LOCAL_CPP_FEATURES + = exceptions
LOCAL_CPP_FEATURES + = rtti
LOCAL_LDLIBS: = -lGLESv1_CM -llog

include $ (BUILD_SHARED_LIBRARY)

It should also be clear, in the LOCAL_C_INCLUDES add the path where you need to look for the header files, in the LOCAL_SRC_FILES add the files with the source code,
in LOCAL_SHARED_LIBRARIES we prescribe the dependencies of the application.

The rtti, exceptions flags are responsible for using RTTI and exceptions.


3. build
$ 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 a connected device or emulator, you will see the installed application.


Rake
1. Where to look for resources ???
The location of the 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 , using the path itself may be an access error or a file search error.
You can get the full path to the application directory via 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 does not work on all devices, remove SDL_WINDOW_OPENGL or SDL_WINDOW_BORDERLESS and see which of the flags the program crashes. I can not explain what this behavior is connected with. With the SDL_WINDOW_SHOWN flag, one-on-one logs over the logs, as with all flags, but the probability of departure is much less.
3. Too many audio channels.
Departures are observed when calling the function SDL_mixer :: Mix_AllocateChannels (N> 16) with the error that it is impossible to initialize the sound. It costs me to reduce the number of channels requested, how correctly to solve this problem in this way I do not know.
4. stlport vs gnustl
Departure when using stlport, ran into this bug when crawling a vector using iterators on the Nexus 7 emulator (Android 4.0.3). Again, I can not explain the fact of this error, it was 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 one already in the system leads to the loading of someone else's library, which may not have the necessary functions. The error appeared due to the fact that I am collecting my version of libpng.so, the solution was found on stackoverflow , corrected by replacing the library name libpng.so with libpnggo.so

In custody...
Works! Almost no different from BB! Am I pleased? Not really!

The fact is that I tinker Krivorukov, skis do not go skiing, but on the tablet the application turned out to be extremely slow (10-12 fps for a very simple picture, the result is dull), I think the fault is in the hands and ignorance of the materiel. SDL is an excellent library in both reincarnations, and a lot of really good games use it, as well as ported to android .

The time spent on the creation of the port is not a pity, a certain experience and a lot of positive emotions were gained when the game took off. Those who are still trying to try or not, definitely try, do not wait for later!

ZY Behind development of the project it is always possible to look here .

Source: https://habr.com/ru/post/233737/


All Articles