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

Post on 22-Nov-2014

522 views 0 download

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

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

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

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

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)

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)

NDK JNILoading the C++ Library

public class GHBowlingBaseActivity

extends Activity {

static {

System.loadLibrary("GHBowling");

}

}

Loads the file named libGHBowling.so

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}

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);

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

{

public void showInterstitialAd() {

if (mInterstitialHandler != null) {

mInterstitialHandler.showInterstitial();

} else {

onInterstitialDismiss();

}

}

};

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);

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++)

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)

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

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++

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.

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

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

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

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" };

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

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

APP_ABI := x86 armeabi

Supported values: armeabi armeabi-v7a x86 mips all

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

Compiling for x86Solution

include $(CLEAR_VARS)

LOCAL_MODULE := libGHEngine

LOCAL_SRC_FILES :=

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

include $(PREBUILT_STATIC_LIBRARY)

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)

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)

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.