NativeActivity에서 이전 Android Game Development Kit에 포함되어 있음
이 페이지에서는 Android 게임 프로젝트의 NativeActivity
에서 GameActivity
로 이전하는 방법을 설명합니다.
GameActivity
는 Android 프레임워크의 NativeActivity
를 기반으로 하며 향상된 기능과 새로운 기능을 포함합니다.
- Jetpack의
Fragment
를 지원합니다. - 소프트 키보드 통합을 용이하게 하기 위해
TextInput
지원을 추가합니다. NativeActivity
onInputEvent
인터페이스가 아닌GameActivity
Java 클래스의 터치 및 키 이벤트를 처리합니다.
이전하기 전에 프로젝트에서 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_static
prefab 모듈로 가져옵니다.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_pollAll(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_pollAll()
루프 외부에 있습니다.android_app::activity->clazz
사용을android_app:: activity->javaGameActivity
로 바꿉니다. GameActivity는 JavaGameActivity
인스턴스의 이름을 바꿉니다.
추가 단계
이전 단계에서는 NativeActivity의 기능을 다루었으나 GameActivity
는 사용하면 좋은 추가 기능을 제공합니다.
- TextInput
- 게임 컨트롤러
- 프래그먼트
- NativeAppGlueAppCmd에 정의된 새 창 인셋 명령어
이러한 기능을 살펴보고 게임에 맞게 적절하게 채택하는 것이 좋습니다.
GameActivity 또는 다른 AGDK 라이브러리에 관한 질문이나 제안할 사항이 있으면 버그를 작성하여 알려주시기 바랍니다.