Programing

Cocos2d-x(3.13),Gpg 2.1 GooglePlayGameService C++ SDK 붙이기

Medeev 2016. 12. 21. 20:20

Cocos2d-x 에 구글플레이게임 라이브러리를 붙이는 과정에 대한 내용을 적어봅니다.


이전에는 이클립스에서 작업했었는데 안드로이드 스튜디오에서 적용하려니 처음이라 사소한 부분에서 많이 막히더군요.


우선 작업환경은 Cocos2d-x 3.13.1 버젼  : http://www.cocos2d-x.org/download 이고 

Play Games C++ SDK Version 2.1 버젼 : https://developers.google.com/games/services/downloads/sdks 입니다.



기본적으로 https://developers.google.com/games/services/cpp/GettingStartedNativeClient

여기를 참고하지만.. Cocos2d-x에서는 조금 다르게 추가 설정해야 하는 부분이 있습니다.



1. AndroidManifest.xml 에서


 <!-- Tell Cocos2dxActivity the name of our .so -->

<meta-data android:name="android.app.lib_name"

  android:value="cocos2dcpp" />


이부분 밑에 


<meta-data

            android:name="com.google.android.gms.games.APP_ID"

            android:value="@string/app_id" />

        <meta-data android:name="com.google.android.gms.version"

            android:value="@integer/google_play_services_version"/>



를 추가합니다.


2. /proj.android-studio/app/build.gradle 에서 굵은 글씨부분을 추가합니다.



dependencies {

    compile 'com.google.android.gms:play-services:10.0.1'

    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile project(':libcocos2dx')

}



안드로이드 Sync Now로 맞춰주고.


3. cocos2d/external/gpg 폴더를 생성 

include폴더와, prebuilt폴더를 생성합니다.


위처럼 되게 하고 

위에서 다운 받은 Play Games C++ SDK Version 2.1  버젼을 압축을 풀어 라이브러리를 알맞은 위치에 넣습니다.

c++용 말고 gnustl의 라이브러리를 포함합니다.


/gpg/prebuilt/android/ 경로에 Android.mk를 생성하여 


LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)

LOCAL_MODULE := cocos_gpg_static

LOCAL_MODULE_FILENAME := gpg

LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libgpg.a

LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../include/android/gpg

include $(PREBUILT_STATIC_LIBRARY)


를 작성합니다.


4. /proj.android-studio/app/jni/Android.mk  수정


굵은 글씨 부분 수정합니다.



LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../Classes

# LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../../cocos2d/external/lua/lua

LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../../cocos2d/external/gpg/include/android


LOCAL_STATIC_LIBRARIES := cocos2dx_static

LOCAL_WHOLE_STATIC_LIBRARIES += cocos_gpg_static


$(call import-module,.)

$(call import-module,gpg/prebuilt/android)


추가로

/cocos2d/cocos/platform/android/Android.mk 를 굵은글씨부분 추가합니다.

LOCAL_C_INCLUDES := $(LOCAL_PATH) \

                    $(LOCAL_PATH)/.. \

                    $(LOCAL_PATH)/../.. \

                    $(LOCAL_PATH)/../../../external/gpg/include/android


5. 프로젝트 Class폴더에 GPG관련 NativeC++코드를 추가합니다. 


아래코드는 cocos2d-x Warrior 예제의 샘플을 최신sdk에 맞게 약간 수정한 버젼입니다.

(헤더는 생략)


#include "GPGSManager.h"

#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) 


#ifdef __APPLE__

//

//Logging for CoreFoundation

//

#include <CoreFoundation/CoreFoundation.h>

extern "C" void NSLog(CFStringRef format, ...);

const int32_t BUFFER_SIZE = 256;

#define LOGI(...) {char c[BUFFER_SIZE];\

snprintf(c,BUFFER_SIZE,__VA_ARGS__);\

CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, c, kCFStringEncodingMacRoman);\

NSLog(str);\

CFRelease(str);\

}

#else

#include "android/Log.h"

#define DEBUG_TAG "main"

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, DEBUG_TAG, __VA_ARGS__))


#endif


#include "cocos2d.h"

#include "SfClientSelect.h"

#include "SfNetworkManager.h"



bool GPGSManager::isSignedIn = false;

std::unique_ptr<gpg::GameServices> GPGSManager::gameServices;

gpg::TurnBasedMatch GPGSManager::current_match_;



void OnAuthActionStarted(gpg::AuthOperation op) {

LOGI("OnAuthActionStarted");

switch ( op ) {

case gpg::AuthOperation::SIGN_IN:

LOGI("Signing In...");

break;

case gpg::AuthOperation::SIGN_OUT:

LOGI("Signing Out...");

break;

}

}


void OnAuthActionFinished(gpg::AuthOperation op, gpg::AuthStatus status) {

LOGI("OnAuthActionFinished");

if (op == gpg::AuthOperation::SIGN_IN){

LOGI("Signing In.");


if( GPGSManager::IsSignedIn() )

{

gpg::PlayerManager::FetchSelfResponse localPlayer = 

GPGSManager::GetGameServices()->Players().FetchSelfBlocking();

std::string str = localPlayer.data.Id();

std::string avatarUrl = localPlayer.data.AvatarUrl(gpg::ImageResolution::ICON);

LOGI("gpgid=%s, avatarurl:%s", str.c_str(), avatarUrl.c_str() );

}

}

else{

LOGI("Signing Out.");

}

}


gpg::GameServices *GPGSManager::GetGameServices() {

return gameServices.get();

}


void GPGSManager::BeginUserInitiatedSignIn() {

if (!gameServices->IsAuthorized()) {

LOGI("StartAuthorizationUI");

gameServices->StartAuthorizationUI();

}

}


void GPGSManager::SignOut() {

if (gameServices->IsAuthorized()) {

LOGI("SignOut");

gameServices->SignOut();

}

}


void GPGSManager::UnlockAchievement(const char *achievementId) {

if (gameServices->IsAuthorized()) {

LOGI("Achievement unlocked");

gameServices->Achievements().Unlock(achievementId);

}

}


void GPGSManager::IncrementAchievement(const char *achievementId, uint32_t steps)

{

if (gameServices->IsAuthorized()) {

LOGI("Achievement Increase");

gameServices->Achievements().Increment(achievementId, steps);

}

}


void GPGSManager::SubmitHighScore(const char *leaderboardId, uint64_t score) {

if (gameServices->IsAuthorized()) {

LOGI("High score submitted");

gameServices->Leaderboards().SubmitScore(leaderboardId, score);

}

}


void GPGSManager::ShowAchievements()

{

if (gameServices->IsAuthorized()) {

LOGI("Show achievement");

gameServices->Achievements().ShowAllUI([](gpg::UIStatus const &) {


});

}

}


void GPGSManager::ShowLeaderboard(const char *leaderboardId)

{

if (gameServices->IsAuthorized()) {

LOGI("Show achievement");

gameServices->Leaderboards().ShowUI(leaderboardId, [](gpg::UIStatus const &) {


});

}    

}



void GPGSManager::InitServices(gpg::PlatformConfiguration &pc)

{

LOGI("Initializing Services");

if (!gameServices) {

LOGI("Uninitialized services, so creating");

gameServices = gpg::GameServices::Builder()

.SetOnLog([](gpg::LogLevel, std::string const & strlog) {

LOGI("strlog=%s", strlog.c_str());

}, gpg::LogLevel::VERBOSE)

//      .SetOnAuthActionFinished(OnAuthActionFinished)

//      .SetOnAuthActionStarted(OnAuthActionStarted)

// Add a test scope (we don't actually use this).

//    .AddOauthScope("https://www.googleapis.com/auth/appstate")

//    .InternalSetRootURL("https://www-googleapis-staging.sandbox.google.com/")

.SetOnAuthActionStarted([](gpg::AuthOperation op){

OnAuthActionStarted(op);

})

.SetOnAuthActionFinished([](gpg::AuthOperation op, gpg::AuthStatus status){

LOGI("Sign in finished with a result of %d", (int)status);

if( status == gpg::AuthStatus::VALID )

isSignedIn = true;

else

isSignedIn = false;

OnAuthActionFinished( op, status);

}).Create(pc);

}

LOGI("Created");

}




#endif // #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) 



4. /cocos2d/cocos/platform/android/javaactivity-android.cpp 파일수정합니다.


굵은글씨 추가합니다.


// For GPGS

#include "gpg/android_initialization.h"


JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)

{

    JniHelper::setJavaVM(vm);

// For GPGS

gpg::AndroidInitialization::JNI_OnLoad(vm);


    cocos_android_app_init(JniHelper::getEnv());

    return JNI_VERSION_1_4;

}


4. /proj.android-studio/app/jni/hellocpp/main.cpp 파일을 수정합니다. 굵은 글씨 추가 입니다.

#include "AppDelegate.h"

#include "cocos2d.h"

#include "platform/android/jni/JniHelper.h"

#include <jni.h>

#include <android/log.h>


#include "GPGSManager.h"


#define  LOG_TAG    "main"

#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)


using namespace cocos2d;


void cocos_android_app_init (JNIEnv* env) {

    LOGD("cocos_android_app_init");

    AppDelegate *pAppDelegate = new AppDelegate();

}



extern "C"

{

JNIEXPORT void 

Java_org_cocos2dx_cpp_AppActivity_nativeInitGPGS(JNIEnv*  env, jobject thiz, jobject activity)

{

    gpg::AndroidPlatformConfiguration platform_configuration;

    platform_configuration.SetActivity(activity);

    GPGSManager::InitServices(platform_configuration

                               //  ,[](gpg::AuthOperation op) {

                               //      LOGD("Start oauth action ... by Jacky_Android");

                               //     //  if (op == gpg::AuthOperation::SIGN_IN) {

                               //     //      LOGD("Signing In...");

                               //     //   } else {

                               //     //      LOGD("Signing Out...");

                               //     // }

                               //  },

                               //  [](gpg::AuthOperation op, gpg::AuthStatus status) {

                               //      LOGD("Finished oauth action ... by Jacky_Android");

                               //      // if (op == gpg::AuthOperation::SIGN_IN) {

                               //      //     LOGD("Signing In.");

                               //      // } else {

                               //      //     LOGD("Signing Out.");

                               //      // }

                               //      switch( status )

                               //      {

                               //          case gpg::AuthStatus::VALID:

                               //              LOGD("Signed in!!");

                               //              break;

                               //          case gpg::AuthStatus::ERROR_INTERNAL:

                               //          case gpg::AuthStatus::ERROR_NOT_AUTHORIZED:

                               //          case gpg::AuthStatus::ERROR_VERSION_UPDATE_REQUIRED:

                               //          case gpg::AuthStatus::ERROR_TIMEOUT:

                               //          default:

                               //              LOGD("Sign-in failure");

                               //              break;

                               //      }

                               // }

                               );


}


JNIEXPORT void

Java_org_cocos2dx_cpp_AppActivity_nativeOnActivityResult(

    JNIEnv* env, jobject thiz, jobject activity, jint requestCode,

    jint resultCode, jobject data) {

  gpg::AndroidSupport::OnActivityResult(env, activity, requestCode, resultCode,

                                        data);

}


JNIEXPORT void

Java_org_cocos2dx_cpp_AppActivity_nativeOnActivityCreated(

    JNIEnv* env, jobject thiz, jobject activity, jobject saved_instance_state) {

  gpg::AndroidSupport::OnActivityCreated(env, activity, saved_instance_state);

}


JNIEXPORT void

Java_org_cocos2dx_cpp_AppActivity_nativeOnActivityDestroyed(

    JNIEnv* env, jobject thiz, jobject activity) {

  gpg::AndroidSupport::OnActivityDestroyed(env, activity);

}


JNIEXPORT void

Java_org_cocos2dx_cpp_AppActivity_nativeOnActivityPaused(

    JNIEnv* env, jobject thiz, jobject activity) {

  gpg::AndroidSupport::OnActivityPaused(env, activity);

}


JNIEXPORT void

Java_org_cocos2dx_cpp_AppActivity_nativeOnActivityResumed(

    JNIEnv* env, jobject thiz, jobject activity) {

  gpg::AndroidSupport::OnActivityResumed(env, activity);

}


JNIEXPORT void

Java_org_cocos2dx_cpp_AppActivity_nativeOnActivitySaveInstanceState(

    JNIEnv* env, jobject thiz, jobject activity, jobject out_state) {

  gpg::AndroidSupport::OnActivitySaveInstanceState(env, activity, out_state);

}


JNIEXPORT void

Java_org_cocos2dx_cpp_AppActivity_nativeOnActivityStarted(

    JNIEnv* env, jobject thiz, jobject activity) {

  gpg::AndroidSupport::OnActivityStarted(env, activity);

}


JNIEXPORT void

Java_org_cocos2dx_cpp_AppActivity_nativeOnActivityStopped(

    JNIEnv* env, jobject thiz, jobject activity) {

  gpg::AndroidSupport::OnActivityStopped(env, activity);

}


}




5. /proj.android-studio/app/src/org/cocos2dx/cpp/AppActivity.java 를 수정합니다.




import android.app.Activity;


클래스 선언부

// gpg

    // Implemented in C++.

    private static native void nativeInitGPGS(AppActivity act);

    public static native void nativeOnActivityResult(Activity activity, int requestCode, int resultCode, Intent data);

    public static native void nativeOnActivityCreated(Activity activity, Bundle savedInstanceState);

    private static native void nativeOnActivityDestroyed(Activity activity);

    private static native void nativeOnActivityPaused(Activity activity);

    private static native void nativeOnActivityResumed(Activity activity);

    private static native void nativeOnActivitySaveInstanceState(Activity activity, Bundle outState);

    private static native void nativeOnActivityStarted(Activity activity);

    private static native void nativeOnActivityStopped(Activity activity);

    // gpg end


 protected void onCreate(Bundle savedInstanceState)

{

    super.onCreate(savedInstanceState);


// gpg

        nativeInitGPGS(this);

        nativeOnActivityCreated(this, savedInstanceState);

        // gpg end

}



    @Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

// for gpg

        nativeOnActivityResult(this, requestCode,resultCode, data);

        // gpg end

}


@Override

public void onDestroy() {

        super.onDestroy();


// gpg

        nativeOnActivityDestroyed(this);

}



@Override

protected void onStart() {

        super.onStart();


// gpg

        nativeOnActivityStarted(this);

}


@Override

protected void onStop() {

        super.onStop();


        // gpg

        nativeOnActivityStopped(this);

}




    @Override

    protected void onResume() {

        super.onResume();

        // Log.d(TAG, "onResume");


        // gpg

        nativeOnActivityResumed(this);


    }


    @Override

    protected void onPause() {

        // TODO Auto-generated method stub

        super.onPause();


        // gpg

        nativeOnActivityPaused(this);

    }


    @Override

    public void onSaveInstanceState(Bundle outState) {

        super.onSaveInstanceState(outState);


        // gpg

        nativeOnActivitySaveInstanceState(this, outState);

    }



이렇게 해서 빌드하면 Dex~~어쩌고저쩌고하면서 컴파일은되는데 apk만들면서 에러가 납니다 이것은 아래굵은 글씨 추가하거나 하는 방법들이 있다고 google검색하면 나옵니다. 머가 모자라서 그렇다는데.. 이렇게 넘어갑니다.


defaultConfig {

        applicationId "com.xxxxxx.xxxxxxx"

        minSdkVersion 11

        targetSdkVersion 23

        versionCode 1

        versionName "1.0"

        multiDexEnabled true

    }


여기까지입니다.



추가로

만약 release빌드를 하는데  minifyEnabled 옵션을 true로 했다면 

/proj.android-studio/app/proguard-rules.pro 에서 아래 부분 추가합니다. 최소화 예외되는목록인듯합니다.


-keep public class org.cocos2dx.lib.* {

    *;

}


-keep public class org.cocos2dx.cpp.AppActivity {

    *;

}


# google play service

-keep class com.google.android.gms.** { *; }

-dontwarn com.google.android.gms.**


-keep class * extends java.util.ListResourceBundle {

    protected Object[][] getContents();

}


-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {

    public static final *** NULL;

}


-keepnames @com.google.android.gms.common.annotation.KeepName class *

-keepclassmembernames class * {

    @com.google.android.gms.common.annotation.KeepName *;

}


-keepnames class * implements android.os.Parcelable {

    public static final ** CREATOR;

}