Porting and Maintaining your C++ Game on Android without losing your mind

28
Porting and Maintaining Your C++ Game on Android (without losing your mind)

description

Presentation from David Wingrove & Katie Merrill from Golden Hammer Software http://www.goldenhammersoftware.com/ From the Barcelona Android User Group meetup: http://www.meetup.com/Barcelona-Android-User-Group/events/166734982/

Transcript of Porting and Maintaining your C++ Game on Android without losing your mind

Page 1: Porting and Maintaining your C++ Game on Android without losing your mind

Porting and Maintaining Your C++ Game

on Android(without losing your mind)

Page 2: Porting and Maintaining your C++ Game on Android without losing your mind
Page 3: Porting and Maintaining your C++ Game on Android without losing your mind

Why C++ Cross-Platform support

(Some lesser platforms don’t have a JVM)

Don’t want to use Unity, etc Existing C++ Codebase

Page 4: Porting and Maintaining your C++ Game on Android without losing your mind

Overview NDK recap What goes where? C++ vs Java Streamlining packaged app data

Eliminating Data Duplication Compiling multiple architectures Other quirks we’ve run into Some downloads info about our apps

Page 5: Porting and Maintaining your C++ Game on Android without losing your mind

NDK Application.mk

APP_PROJECT_PATH := $(call my-dir)APP_BUILD_SCRIPT := $(call my-dir)/Android.mkAPP_MODULES := GHEngineAPP_OPTIM := releaseAPP_STL := stlport_staticAPP_PLATFORM := android-8

Page 6: Porting and Maintaining your C++ Game on Android without losing your mind

NDK Android.mk (static lib)LOCAL_PATH := $(call my-dir)

LOCAL_CFLAGS := -Wno-psabi

LOCAL_CFLAGS += -D ANDROID

LOCAL_MODULE := GHEngine

LOCAL_MODULE_FILENAME := libGHEngine

LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../../../Base

LOCAL_SRC_FILES += ../../../../Base/GHAppRunner.cpp

include $(BUILD_STATIC_LIBRARY)

Page 7: Porting and Maintaining your C++ Game on Android without losing your mind

NDK Android.mk (shared lib – loaded by java)

LOCAL_MODULE := libGHEngine

LOCAL_SRC_FILES := ../../GHEngine/obj/local/armeabi/libGHEngine.a

include $(PREBUILT_STATIC_LIBRARY)

LOCAL_STATIC_LIBRARIES += libGHEngine

include $(BUILD_SHARED_LIBRARY)

$(shell cp libs/armeabi/libGHBowling.so ../../../GHBowlingBase/libs/armeabi)

Page 8: Porting and Maintaining your C++ Game on Android without losing your mind

NDK JNILoading the C++ Library

public class GHBowlingBaseActivity

extends Activity {

static {

System.loadLibrary("GHBowling");

}

}

Loads the file named libGHBowling.so

Page 9: Porting and Maintaining your C++ Game on Android without losing your mind

NDK JNIJava (calling C++)

public class GHEngineInterface {public native void runNativeFrame();public native void launchNativeApp(int windowWidth,

int windowHeight,String externalStoragePath,

int isTablet,int iapIsOn);

//resizeWindow, handleTouchStart, handleTouchEnd, handleTouchPos//handleAcceleration, handleJavaPause, handleJavaResume,//handleJavaShutdown,handleBackPressed, calculatePublicKey,//onInterstitialRewardGranted,onRewardInterstitialAvailabilityChange,//onInterstitialDismiss, loadFile,handleTextureLoadConfirmed,//onAdActivation, onAdDeactivation, onIAPPurchase}

Page 10: Porting and Maintaining your C++ Game on Android without losing your mind

NDK JNIC++ (called by Java)

static jobject globalEngineInterface = 0;extern "C“ __attribute__((visibility("default"))) voidJava_goldenhammer_ghbowlingbase_GHEngineInterface_launchNativeApp (JNIEnv* env,

jobject engineInterface, jint windowWidth, jint windowHeight, jstring jExternalStoragePath, jint isTablet, jint useIAP)

{globalEngineInterface = env->NewGlobalRef(engineInterface);//Engine/Game Initialization goes here.

}

When you shut down:

env->DeleteGlobalRef(globalEngineInterface);

Page 11: Porting and Maintaining your C++ Game on Android without losing your mind

NDK JNIJava (called by C++)public class GHEngineInterface

{

public void showInterstitialAd() {

if (mInterstitialHandler != null) {

mInterstitialHandler.showInterstitial();

} else {

onInterstitialDismiss();

}

}

};

Page 12: Porting and Maintaining your C++ Game on Android without losing your mind

NDK JNIC++ (calling Java)

GHAndroidInterstitialAd class declaration:

JNIEnv& mJNIEnv;

jobject mEngineInterface;

jmethodID mShowAdMethod;

GHAndroidInterstitialAd ctor:

jclass cls = mJNIEnv.GetObjectClass(mEngineInterface);

mShowAdMethod = mJNIEnv.GetMethodID(cls, "showInterstitialAd", "()V");

GHAndroidInterstitialAd::activate:

mJNIEnv.CallVoidMethod(mJavaObj, mShowAdMethod);

Page 13: Porting and Maintaining your C++ Game on Android without losing your mind

What goes Where?C++ or Java C++

All your platform-independent/pre-existing code Bare minimum wrapper for Android implementation of

platform services Java

Typical Android platform code Middleware integration

Can swap middleware vendors of the same service without touching C++

Exceptions: OpenGL (initialization in Java, most code in C++ or GLSL) File I/O (mix of Java, C++)

Page 14: Porting and Maintaining your C++ Game on Android without losing your mind

What Goes WhereJava OpenGL initialization Sound through SoundPool File handle loading through AssetManager Image loading through BitmapFactory Google Play, In-App Billing, etc Ads and other middleware integration

AdMob, AdColony, Chartboost, PlayHaven, Facebook, etc

Input Handling (touches, accelerometer, buttons/gamepad)

Page 15: Porting and Maintaining your C++ Game on Android without losing your mind

What Goes WhereC++ All your game code

Ideally 90% of your app OpenGL rendering code

Need to handle reinit after lost device fopen using file handle from Java Thin wrapper over JNI calls for

everything else

Page 16: Porting and Maintaining your C++ Game on Android without losing your mind

What Goes WhereOur code distribution.

2800 lines Java 1600 in base project 1200 in master project

Includes middleware integration

50k-150k lines C++ Varies depending on game 6400 Android-specific C++

Page 17: Porting and Maintaining your C++ Game on Android without losing your mind

Eliminating Data DuplicationProblem Eclipse wants all data underneath

project We want to reuse data (between

projects) in our own directory structure

We hate maintaining multiple copies of the same data files.

Page 18: Porting and Maintaining your C++ Game on Android without losing your mind

Eliminating Data DuplicationSolution

Batch file/shell script to copy data On Maccp ../../../../../data/GHBowling/ballrollloop.wav

../../../GHBowling/assets/ballrollloop.wav

cp ../../../../../data/Bowling/GHBowlingAndroid/backwall.jpg ../../../GHBowling/assets/backwall.jpg

On Windowscopy ..\..\..\..\..\data\GHBowling\ballrollloop.wav ..\..\..\

GHBowling\assets\ballrollloop.wav

copy ..\..\..\..\..\data\Bowling\GHBowlingAndroid\backwall.jpg ..\..\..\GHBowling\assets\backwall.jpg

Page 19: Porting and Maintaining your C++ Game on Android without losing your mind

Eliminating Data DuplicationBatch File Generation Tool for initial generation

Looks through set of directories with a specified order of preference

Some files are different per-platform We may have Android-specific files We may not, but we prefer iOS-specific to generic

Downside: some unnecessary files get copied

Maintenance usually done by hand

Page 20: Porting and Maintaining your C++ Game on Android without losing your mind

Packaged App DataProblem Data is packaged through Android

build process All files except those with certain

excluded file extensions (pre-compressed file types) are automatically compressed.

Platform-agnostic file reading code doesn’t know to uncompress: sees garbage

Page 21: Porting and Maintaining your C++ Game on Android without losing your mind

Excluded file extensions Source: Android Asset Packaging

Tool/* these formats are already compressed, or don't

compress well */

static const char* kNoCompressExt[] = { ".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg", ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".amr", ".awb", ".wma", ".wmv" };

Page 22: Porting and Maintaining your C++ Game on Android without losing your mind

App Data CompressionSolution

One option: forgo Eclipse and pass –0 to the AAPT via command line (universally or for certain extensions)

What we do

cp ../../../../../data/GHBowlingiOS/arrowpixel.glsl ../../../GHBowling/assets/arrowpixel.glsl.mp3

Page 23: Porting and Maintaining your C++ Game on Android without losing your mind

Compiling for x86 (or other architectures) In Application.mk:

APP_ABI := x86 armeabi

Supported values: armeabi armeabi-v7a x86 mips all

Page 24: Porting and Maintaining your C++ Game on Android without losing your mind

Compiling for x86Problem Shared library Android.mk needs to

include the correct static library for each architecture For arm: /armeabi/libGHEngine.a For x86: /x86/libGHEngine.a

Page 25: Porting and Maintaining your C++ Game on Android without losing your mind

Compiling for x86Solution

include $(CLEAR_VARS)

LOCAL_MODULE := libGHEngine

LOCAL_SRC_FILES :=

../../GHEngine/obj/local/$(TARGET_ARCH_ABI)/libGHEngine.a

include $(PREBUILT_STATIC_LIBRARY)

Page 26: Porting and Maintaining your C++ Game on Android without losing your mind

Building on WindowsProblem We really like verbose filenames

GHBowlingYellowBallThrowWith190DegreeSpinTransitionXMLLoaderTransition.cpp

Our GHEngine project has lots of files Linker includes all of those filenames

in one shell command Exceeds maximum line length on

Windows cmd (8191 characters)

Page 27: Porting and Maintaining your C++ Game on Android without losing your mind

Building on WindowsProblem We really like verbose filenames

Ok, more like GHGUIPopTransitionXMLLoader.cpp

Our GHEngine project has lots of files

Linker includes all of those filenames in one shell command

Exceeds maximum line length on Windows cmd (8191 characters)

Page 28: Porting and Maintaining your C++ Game on Android without losing your mind

Building on WindowsSolution In Android.mk:LOCAL_SHORT_COMMANDS := true

Build system generates intermediate list file and then invokes it with a much shorter command line

Downside: slower compiles Can use only in projects that need it.