NativeActivity에서 이전 Android Game Development Kit에 포함되어 있음
이 페이지에서는 Android 게임 프로젝트의 NativeActivity에서 GameActivity로 이전하는 방법을 설명합니다.
GameActivity는 Android 프레임워크의 NativeActivity를 기반으로 하며 향상된 기능과 새로운 기능을 포함합니다.
- Jetpack의
Fragment를 지원합니다. - 소프트 키보드 통합을 용이하게 하기 위해
TextInput지원을 추가합니다. NativeActivityonInputEvent인터페이스가 아닌GameActivityJava 클래스의 터치 및 키 이벤트를 처리합니다.
이전하기 전에 프로젝트에서 GameActivity를 설정하고 통합하는 방법을 설명하는 시작 가이드를 읽어보는 것이 좋습니다.
Java 빌드 스크립트 업데이트
GameActivity는 Jetpack 라이브러리로 배포됩니다. 시작 가이드에 설명된 Gradle 스크립트 업데이트 단계를 적용해야 합니다.
프로젝트의
gradle.properties파일에서 Jetpack 라이브러리를 사용 설정합니다.android.useAndroidX=true필요한 경우 동일한
gradle.properties파일에서 Prefab 버전을 지정합니다. 예를 들면 다음과 같습니다.android.prefabVersion=2.0.0앱의
build.gradle파일에서 Prefab 기능을 사용 설정합니다.android { ... // other configurations buildFeatures.prefab true }애플리케이션에
GameActivity종속 항목을 추가합니다.core및games-activity라이브러리를 추가합니다.- 현재 지원되는 최소 API 수준이 16 미만인 경우 최소 16으로 업데이트합니다.
- 컴파일된 SDK 버전을
games-activity라이브러리에 필요한 버전으로 업데이트합니다. Jetpack에는 일반적으로 출시 빌드 시간에 최신 SDK 버전이 필요합니다.
업데이트된
build.gradle파일은 다음과 같습니다.android { compiledSdkVersion 33 ... // other configurations. defaultConfig { minSdkVersion 16 } ... // other configurations. buildFeatures.prefab true } dependencies { implementation 'androidx.core:core:1.9.0' implementation 'androidx.games:games-activity:1.2.2' }
Kotlin 또는 Java 코드 업데이트
NativeActivity를 시작 활동으로 사용하여 전체 화면 애플리케이션을 만들 수 있습니다. 현재 GameActivity는 시작 활동으로 사용할 수 없습니다. 앱은 GameActivity에서 클래스를 가져와 시작 활동으로 사용해야 합니다. 전체 화면 앱을 만들기 위해서는 추가로 구성을 변경해야 합니다.
다음 단계에서는 애플리케이션에서 NativeActivity를 시작 활동으로 사용한다고 가정합니다. 그러지 않는 경우 대부분의 단계를 건너뛰어도 됩니다.
새 시작 활동을 호스팅하는 Kotlin 또는 Java 파일을 만듭니다. 예를 들어 다음 코드는 시작 활동으로
MainActivity를 만들고 애플리케이션의 기본 네이티브 라이브러리libAndroidGame.so를 로드합니다.Kotlin
class MainActivity : GameActivity() { override fun onResume() { super.onResume() // Use the function recommended from the following page: // https://d.android.com/training/system-ui/immersive hideSystemBars() } companion object { init { System.loadLibrary("AndroidGame") } } }
Java
public class MainActivity extends GameActivity { protected void onResume() { super.onResume(); // Use the function recommended from // https://d.android.com/training/system-ui/immersive hideSystemBars(); } static { System.loadLibrary("AndroidGame"); } }
res\values\themes.xml파일에서 전체 화면 앱 테마를 만듭니다.<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <style name="Application.Fullscreen" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowFullscreen">true</item> <item name="android:windowContentOverlay">@null</item>" </style> </resources>AndroidManifest.xml파일에서 애플리케이션에 이 테마를 적용합니다.<application android:theme=”@style/Application.Fullscreen”> <!-- other configurations not listed here. --> </application>전체 화면 모드에 관한 자세한 안내는 games-samples 저장소에서 몰입형 가이드 및 구현 예를 참고하세요.
이 이전 가이드에서는 네이티브 라이브러리 이름을 변경하지 않습니다. 이름을 변경하는 경우 네이티브 라이브러리 이름이 다음 세 위치에서 일관되도록 합니다.
Kotlin 또는 Java 코드
System.loadLibrary(“AndroidGame”)AndroidManifest.xml:<meta-data android:name="android.app.lib_name" android:value="AndroidGame" />C/C++ 빌드 스크립트 파일 내부(예:
CMakeLists.txt)add_library(AndroidGame ...)
C/C++ 빌드 스크립트 업데이트
이 섹션의 안내에서는 cmake를 예로 사용합니다. 애플리케이션에서 ndk-build를 사용한다면 ndk-build 문서 페이지에 설명된 상응하는 명령어에 매핑해야 합니다.
GameActivity의 C/C++ 구현은 소스 코드 출시를 제공하고 있습니다. 버전 1.2.2 이상의 경우 정적 라이브러리 출시가 제공됩니다. 정적 라이브러리는 권장되는 출시 유형입니다.
이 출시는 prefab 유틸리티와 함께 AAR 내에 패키징됩니다. 네이티브 코드에는 GameActivity의 C/C++ 소스와 native_app_glue 코드가 포함됩니다. 애플리케이션의 C/C++ 코드와 함께 빌드해야 합니다.
NativeActivity 애플리케이션은 이미 NDK 내에서 제공되는 native_app_glue 코드를 사용합니다. GameActivity의 native_app_glue 버전으로 이를 대체해야 합니다. 그 외에는 시작 가이드에 설명된 모든 cmake 단계가 적용됩니다.
C/C++ 정적 라이브러리나 C/++ 소스 코드를 다음과 같이 프로젝트로 가져옵니다.
정적 라이브러리
프로젝트의
CMakeLists.txt파일에서game-activity정적 라이브러리를game-activity_staticprefab 모듈로 가져옵니다.find_package(game-activity REQUIRED CONFIG) target_link_libraries(${PROJECT_NAME} PUBLIC log android game-activity::game-activity_static)소스 코드
프로젝트의
CMakeLists.txt파일에서game-activity패키지를 가져와서 대상에 추가합니다.game-activity패키지에는libandroid.so가 필요하므로, 없는 경우 가져와야 합니다.find_package(game-activity REQUIRED CONFIG) ... target_link_libraries(... android game-activity::game-activity)다음과 같이 NDK의
native_app_glue코드에 대한 모든 참조를 삭제합니다.${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ... set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")소스 코드 출시를 사용하는 경우
GameActivity소스 파일을 포함합니다. 그 이외의 경우에는 이 단계를 건너뛰세요.get_target_property(game-activity-include game-activity::game-activity INTERFACE_INCLUDE_DIRECTORIES) add_library(${PROJECT_NAME} SHARED main.cpp ${game-activity-include}/game-activity/native_app_glue/android_native_app_glue.c ${game-activity-include}/game-activity/GameActivity.cpp ${game-activity-include}/game-text-input/gametextinput.cpp)
UnsatisfiedLinkError 문제 해결
com.google.androidgamesdk.GameActivity.initializeNativeCode() 함수에서 UnsatsifiedLinkError가 발생하면 다음 코드를 CMakeLists.txt 파일에 추가하세요.
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -u \
Java_com_google_androidgamesdk_GameActivity_initializeNativeCode")
C/C++ 소스 코드 업데이트
다음 단계를 따라 애플리케이션의 NativeActivity 참조를 GameActivity로 바꿉니다.
GameActivity와 함께 출시된native_app_glue를 사용합니다. 모든android_native_app_glue.h사용을 검색하여 다음과 같이 바꿉니다.#include <game-activity/native_app_glue/android_native_app_glue.h>모션 이벤트 필터와 키 이벤트 필터를 모두
NULL로 설정합니다. 그러면 앱이 모든 입력 기기에서 입력 이벤트를 수신할 수 있습니다. 일반적으로android_main()함수 내에서 이 작업을 실행합니다.void android_main(android_app* app) { ... // other init code. android_app_set_key_event_filter(app, NULL); android_app_set_motion_event_filter(app, NULL); ... // additional init code, and game loop code. }AInputEvent관련 코드를 삭제하고 GameActivity의InputBuffer구현으로 바꿉니다.while (true) { // Read all pending events. int events; struct android_poll_source* source; // If not animating, block forever waiting for events. // If animating, loop until all events are read, then continue // to draw the next frame of animation. while ((ALooper_pollOnce(engine.animating ? 0 : -1, nullptr, &events, (void**)&source)) >= 0) { // Process this app cycle or inset change event. if (source) { source->process(source->app, source); } ... // Other processing. // Check if app is exiting. if (state->destroyRequested) { engine_term_display(&engine); return; } } // Process input events if there are any. engine_handle_input(state); if (engine.animating) { // Draw a game frame. } } // Implement input event handling function. static int32_t engine_handle_input(struct android_app* app) { auto* engine = (struct engine*)app->userData; auto ib = android_app_swap_input_buffers(app); if (ib && ib->motionEventsCount) { for (int i = 0; i < ib->motionEventsCount; i++) { auto *event = &ib->motionEvents[i]; int32_t ptrIdx = 0; switch (event->action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_POINTER_DOWN: case AMOTION_EVENT_ACTION_POINTER_UP: // Retrieve the index for the starting and the ending of any secondary pointers ptrIdx = (event->action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_UP: engine->state.x = GameActivityPointerAxes_getAxisValue( &event->pointers[ptrIdx], AMOTION_EVENT_AXIS_X); engine->state.y = GameActivityPointerAxes_getAxisValue( &event->pointers[ptrIdx], AMOTION_EVENT_AXIS_Y); break; case AMOTION_EVENT_ACTION_MOVE: // Process the move action: the new coordinates for all active touch pointers // are inside the event->pointers[]. Compare with our internally saved // coordinates to find out which pointers are actually moved. Note that there is // no index embedded inside event->action for AMOTION_EVENT_ACTION_MOVE (there // might be multiple pointers moved at the same time). ... break; } } android_app_clear_motion_events(ib); } // Process the KeyEvent in a similar way. ... return 0; }NativeActivity의
AInputEvent에 연결된 로직을 검토하고 업데이트합니다. 이전 단계에서 본 바와 같이 GameActivity의InputBuffer처리는ALooper_pollOnce()루프 외부에 있습니다.android_app::activity->clazz사용을android_app:: activity->javaGameActivity로 바꿉니다. GameActivity는 JavaGameActivity인스턴스의 이름을 바꿉니다.
추가 단계
이전 단계에서는 NativeActivity의 기능을 다루었으나 GameActivity는 사용하면 좋은 추가 기능을 제공합니다.
- TextInput
- 게임 컨트롤러
- 프래그먼트
- NativeAppGlueAppCmd에 정의된 새 창 인셋 명령어
이러한 기능을 살펴보고 게임에 맞게 적절하게 채택하는 것이 좋습니다.
GameActivity 또는 다른 AGDK 라이브러리에 관한 질문이나 제안할 사항이 있으면 버그를 작성하여 알려주시기 바랍니다.