The Spout on porting Android OS: first experience with NDK, JNI, and OpenGL ES
Once upon a time, I ported to the platform MotoMAGX unpretentious, but very addictive game Spout. The purpose of this toy is very simple: you need to climb on the boat higher and higher on the screen, gaining points and fighting gravity. The ship is controlled by a jet, the particles of which can serve not only as a means of transportation, but also a formidable weapon. The jet stream can destroy the walls and obstacles blocking the way up. If you control the ship will face an obstacle or fall to the bottom of the screen, you lose. With increasing height, the density of the obstacles gradually increases, which greatly affects the complexity of the game. Space on which the ship rises is divided into equal parts, the boundaries of which are marked by a horizontal strip of black, which, incidentally, also needs to be destroyed. To overcome the first part of the space, you are given exactly one minute, and after you cross the black line, to the remaining time again added 60 seconds. Thus, quickly flying the first part of the space, you can save valuable time for more complex areas above. It sounds very simple, but in practice, the constant struggle with gravity and gaining speed — a very difficult test.
The Spout port of the game running on an Android device, the Motorola Photon Q
The game captured me so much by the simplicity of its execution and at the same time by the complexity that I decided to port Spout to modern devices running Android OS. In the original version of the engine Spout, ported to MotoMAGX, worked through the SDL library. Thanks to this, the game could run on the entire line of MotoMAGX-devices for which the SDL library was ported. In the case of porting the game to Android OS, I decided to use only the libraries and tools available in the standard package, so the dependence on SDL is no longer necessary. It was decided not to rewrite the Spout engine from the C programming language to Java, but to use the Android NDK and the standard JNI (Java Native Interface) mechanism for interaction between the Java layer and native code in the C language. OpenGL ES Was taken as a library for displaying the game context on the screen; this library is available in almost every Android device that has the ability to hardware graphics acceleration through the GPU. Most of these devices, Android devices, which can only do software rendering, have long since disappeared from the Windows of mobile shops. The game context will be rendered to the texture, which I will display on the screen of the device. To my great regret, the owners of Android-devices with an ergonomic physical keyboard remained units, so for easy control of the ship in Spout, I implemented a simple touch control.
At first, I decided to completely abandon the use of the SDL library, but after looking at the code, I saw that the engine very actively uses it for streams, sound and the graphics. I had to keep it, but with a few caveats. LAB3D/SDL uses the old SDL version 1.2, which officially does not support Android OS, and its implementation from enthusiasts do not differ acceptable quality of work. The new SDL version 2.0 branch has official support for both Android OS and iOS, but alas, the library API is incompatible with the versions of the previous branch, so the code will require changes, although very minor. I decided to first port LAB3D/SDL to SDL 2.0, and then check the possibility of the game under Android OS.
The developers of SDL 2.0 was written in a special document: SDL 1.2 to 2.0 Migration Guide, following the prescriptions of which you can without difficulty to migrate to SDL 2.0 any SDL application. In short, the essence boils down to the following:
- Instead of SDL_SetVideoMode (), use SDL_CreateWindow() and SDL_CreateRenderer();
- Instead SDL_ListModes() should be used SDL_GetDisplayMode() and SDL_GetNumDisplayModes();
- Instead of SDL_UpdateRect() and SDL_Flip (), use SDL_RenderPresent();
- In addition to SDL_Surface, you can use SDL_Texture;
- Instead SDL_VideoInfo() should be used SDL_GetRendererInfo() and SDL_GetRenderDriverInfo();
- Instead SDL_GetCurrentVideoDisplay() should be used SDL_GetWindowDisplayIndex();
- Equivalent to the SDL_VIDEORESIZE event now SDL_WINDOWEVENT_RESIZED;
- Instead of SDL_GL_SwapBuffers (), use SDL_GL_SwapWindow().
Also, you should check how keyboard keystrokes are handled, because in SDL 2.0 Unicode support was implemented without crutches, such as the SDL_EnableUNICODE () function, which had to be called when it was necessary to obtain a non-ASCII character.
Difficulties of transfer LAB3D/SDL on SDL 2.0 I had only the handler for keyboard events because there were used different methods of interaction with her. I had to write small auxiliary functions-wrappers, which I isolated into separate sdl2keyhelper files.h and sdl2keyhelper.c: