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;
}
'Programing' 카테고리의 다른 글
MongoDB 윈도우에 설치하기 (0) | 2016.12.22 |
---|---|
안드로이드 inapp 영수증 서버검증 C++ (8) | 2016.12.21 |
서버 죽을때 간단하게 자동 재실행 시키기 (0) | 2016.12.21 |
MonngoDB C++ 드라이버 설치하기 (0) | 2016.12.21 |
Teamcity AndroidStudio Apk배포 하기 (0) | 2016.08.07 |