Sign In Start Free Trial
Account

Add to playlist

Create a Playlist

Modal Close icon
You need to login to use this feature.
  • Book Overview & Buying Android NDK Game Development Cookbook
  • Table Of Contents Toc
Android NDK Game Development Cookbook

Android NDK Game Development Cookbook

By : Sergey Kosarevsky, Viktor Latypov
4 (5)
close
close
Android NDK Game Development Cookbook

Android NDK Game Development Cookbook

4 (5)
By: Sergey Kosarevsky, Viktor Latypov

Overview of this book

Android NDK is used for multimedia applications which require direct access to a system's resources. Android NDK is also the key for portability, which in turn provides a reasonably comfortable development and debugging process using familiar tools such as GCC and Clang toolchains. If your wish to build Android games using this amazing framework, then this book is a must-have.This book provides you with a number of clear step-by-step recipes which will help you to start developing mobile games with Android NDK and boost your productivity debugging them on your computer. This book will also provide you with new ways of working as well as some useful tips and tricks that will demonstrably increase your development speed and efficiency.This book will take you through a number of easy-to-follow recipes that will help you to take advantage of the Android NDK as well as some popular C++ libraries. It presents Android application development in C++ and shows you how to create a complete gaming application. You will learn how to write portable multithreaded C++ code, use HTTP networking, play audio files, use OpenGL ES, to render high-quality text, and how to recognize user gestures on multi-touch devices. If you want to leverage your C++ skills in mobile development and add performance to your Android applications, then this is the book for you.
Table of Contents (11 chapters)
close
close
10
Index

Going cross platform

The main idea is the possibility of cross-platform development in What You See (on a PC) is What You Get (on a device), when most of the application logic can be developed in a familiar desktop environment like Windows, and it can be built for Android using the NDK whenever necessary.

Getting ready

To perform what we just discussed, we have to implement some sort of abstraction on top of the NDK, POSIX, and Windows API. Such an abstraction should feature at least the following:

  • Ability to render buffer contents on the screen: Our framework should provide the functions to build the contents of an off-screen framebuffer (a 2D array of pixels) to the screen (for Windows we refer to the window as "the screen").
  • Event handling: The framework must be able to process the multi-touch input and virtual/physical key presses (some Android devices, such as the Toshiba AC 100, or the Ouya console, and other gaming devices, have physical buttons), timing events, and asynchronous operation completions.
  • Filesystem, networking, and audio playback: The abstraction layers for these entities need a ton of work to be done by you, so the implementations are presented in Chapter 3, Networking, Chapter 4, Organizing a Virtual Filesystem, and Chapter 5, Cross-platform Audio Streaming.

How to do it...

  1. Let us proceed to write a minimal application for the Windows environment, since we already have the application for Android (for example, App1). A minimalistic Windows GUI application is the one that creates a single window and starts the event loop (see the following example in Win_Min1/main.c):
    #include <windows.h>
    
    LRESULT CALLBACK MyFunc(HWND h, UINT msg, WPARAM w, LPARAM p)
    {
      if(msg == WM_DESTROY) { PostQuitMessage(0); }
      return DefWindowProc(h, msg, w, p);
    }
    
    char WinName[] = "MyWin";
  2. The entry point is different from Android. However, its purpose remains the same— to initialize surface rendering and invoke callbacks:
    int main()
    {
      OnStart();
    
      const char WinName[] = "MyWin";
    
      WNDCLASS wcl;
      memset( &wcl, 0, sizeof( WNDCLASS ) );
      wcl.lpszClassName = WinName;
      wcl.lpfnWndProc = MyFunc;
      wcl.hCursor = LoadCursor( NULL, IDC_ARROW );
    
      if ( !RegisterClass( &wcl ) ) { return 0; }
    
      RECT Rect;
    
      Rect.left = 0;
      Rect.top = 0;
  3. The size of the window client area is predefined as ImageWidth and ImageHeight constants. However, the WinAPI function CreateWindowA() accepts not the size of the client area, but the size of the window, which includes caption, borders, and other decorations. We need to adjust the window rectangle to set the client area to the desired size through the following code:
      Rect.right  = ImageWidth;
      Rect.bottom = ImageHeight;
    
      DWORD dwStyle = WS_OVERLAPPEDWINDOW;
    
      AdjustWindowRect( &Rect, dwStyle, false );
    
      int WinWidth  = Rect.right  - Rect.left;
      int WinHeight = Rect.bottom - Rect.top;
    
      HWND hWnd = CreateWindowA( WinName, "App3", dwStyle,100, 100, WinWidth, WinHeight,0, NULL, NULL, NULL );
      ShowWindow( hWnd, SW_SHOW );
    
      HDC dc = GetDC( hWnd );
  4. Create the offscreen device context and the bitmap, which holds our offscreen framebuffer through the following code:
      hMemDC = CreateCompatibleDC( dc );
      hTmpBmp = CreateCompatibleBitmap( dc,ImageWidth, ImageHeight );
      memset( &BitmapInfo.bmiHeader, 0,sizeof( BITMAPINFOHEADER ) );
      BitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
      BitmapInfo.bmiHeader.biWidth = ImageWidth;
      BitmapInfo.bmiHeader.biHeight = ImageHeight;
      BitmapInfo.bmiHeader.biPlanes = 1;
      BitmapInfo.bmiHeader.biBitCount = 32;
      BitmapInfo.bmiHeader.biSizeImage = ImageWidth*ImageHeight*4;
      UpdateWindow( hWnd );
  5. After the application's window is created, we have to run a typical message loop:
      MSG msg;
      while ( GetMessage( &msg, NULL, 0, 0 ) )
      {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
      }
      …
    }
  6. This program only handles the window destruction event and does not render anything. Compilation of this program is done with a single command as follows:
    >gcc -o main.exe main.c -lgdi32

How it works…

To render a framebuffer on the screen, we need to create a so-called device context with an associated bitmap, and add the WM_PAINT event handler to the window function.

To handle the keyboard and mouse events, we add the WM_KEYUP and WM_MOUSEMOVE cases to the switch statement in the previous program. Actual event handling is performed in the externally provided routines OnKeyUp() and OnMouseMove(), which contain our game logic.

The following is the complete source code of the program (some omitted parts, similar to the previous example, are omitted). The functions OnMouseMove(), OnMouseDown(), and OnMouseUp() accept two integer arguments that store the current coordinates of the mouse pointer. The functions OnKeyUp() and OnKeyDown() accept a single argument—the pressed (or released) key code:

#include <windows.h>

HDC hMemDC;
HBITMAP hTmpBmp;
BITMAPINFO BmpInfo;

In the following code, we store our global RGBA framebuffer:

unsigned char* g_FrameBuffer;

We do all OS-independent frame rendering in this callback. We draw a simple XOR pattern (http://lodev.org/cgtutor/xortexture.html) into the framebuffer as follows:

void DrawFrame()
{
  int x, y;
  for (y = 0 ; y < ImageHeight ; y++)
  {
    for (x = 0 ; x < ImageWidth ; x++)
    {
      int Ofs = y * ImageWidth + x;
      int c = (x ^ y) & 0xFF;
      int RGB = (c<<16) | (c<<8) | (c<<0) | 0xFF000000;
      ( ( unsigned int* )g_FrameBuffer )[ Ofs ] =	RGB;
    }
  }
}

The following code shows the WinAPI window function:

LRESULT CALLBACK MyFunc(HWND h, UINT msg, WPARAM w, LPARAM p)
{
  PAINTSTRUCT ps;
  switch(msg)
  {
  case WM_DESTROY:
    PostQuitMessage(0);
break;
  case WM_KEYUP:
    OnKeyUp(w);
break;
  case WM_KEYDOWN:
    OnKeyDown(w);
break;
  case WM_LBUTTONDOWN:
    SetCapture(h);
    OnMouseDown(x, y);
break;
  case WM_MOUSEMOVE:
    OnMouseMove(x, y);
break;
  case WM_LBUTTONUP:
    OnMouseUp(x, y);
    ReleaseCapture();
break;
  case WM_PAINT:
    dc = BeginPaint(h, &ps);
    DrawFrame();         

Transfer the g_FrameBuffer to the bitmap through the following code:

    SetDIBits(hMemDC, hTmpBmp, 0, Height,g_FrameBuffer, &BmpInfo, DIB_RGB_COLORS);
    SelectObject(hMemDC, hTmpBmp);

And copy it to the window surface through the following code:

    BitBlt(dc, 0, 0, Width, Height, hMemDC, 0, 0, SRCCOPY);
    EndPaint(h, &ps);
break;
  }
  return DefWindowProc(h, msg, w, p);
}

Since our project contains a make file the compilation can be done via a single command:

>make all

Running this program should produce the result as shown in the following screenshot, which shows the Win_Min2 example running on Windows:

How it works…

There's more…

The main difference between the Android and Windows implementation of a main loop can be summarized in the following way. In Windows, we are in control of the main loop. We literally declare a loop, which pulls messages from the system, handles input, updates the game state, and render s the frame (marked green in the following figure). Each stage invokes an appropriate callback from our portable game (denoted with blue color in the following figure). On the contrary, the Android part works entirely differently. The main loop is moved away from the native code and lives inside the Java Activity and GLSurfaceView classes. It invokes the JNI callbacks that we implement in our wrapper native library (shown in red). The native wrapper invokes our portable game callbacks. Let's summarize it in the following way:

There's more…

The rest of the book is centered on this kind of architecture and the game functionality will be implemented inside these portable On...() callbacks.

There is yet another important note. Responding to timer events to create animation can be done on Windows with the SetTimer() call and the WM_TIMER message handler. We get to that in Chapter 2, Porting Common Libraries, when we speak about rigid body physics simulations. However, it is much better to organize a fixed time-step main loop, which is explained later in the book.

See also

  • Chapter 6, Unifying OpenGL ES 3 and OpenGL 3
  • The recipe Implementing the main loop in Chapter 8, Writing a Match-3 Game
CONTINUE READING
83
Tech Concepts
36
Programming languages
73
Tech Tools
Icon Unlimited access to the largest independent learning library in tech of over 8,000 expert-authored tech books and videos.
Icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Icon 50+ new titles added per month and exclusive early access to books as they are being written.
Android NDK Game Development Cookbook
notes
bookmark Notes and Bookmarks search Search in title playlist Add to playlist font-size Font size

Change the font size

margin-width Margin width

Change margin width

day-mode Day/Sepia/Night Modes

Change background colour

Close icon Search
Country selected

Close icon Your notes and bookmarks

Confirmation

Modal Close icon
claim successful

Buy this book with your credits?

Modal Close icon
Are you sure you want to buy this book with one of your credits?
Close
YES, BUY

Submit Your Feedback

Modal Close icon
Modal Close icon
Modal Close icon