GameActivity 시작하기 Android Game Development Kit의 일부
이 가이드에서는 Android 게임에서 GameActivity
를 설정 및 통합하고 이벤트를 처리하는 방법을 설명합니다.
GameActivity
는 중요한 API 사용 프로세스를 간소화하여 C 또는 C++ 게임을 Android에 출시할 수 있도록 지원합니다.
이전에는 NativeActivity
가 게임에 권장되는 클래스였습니다. 현재는 GameActivity
가 그 자리를 대체하고 있으며 API 수준 19와 하위 호환도 됩니다.
GameActivity를 통합하는 샘플은 games-samples 저장소를 참고하세요.
시작하기 전에
배포판을 가져오려면 GameActivity
출시를 참고하세요.
빌드 설정
Android에서 Activity
는 게임의 진입점 역할을 하며 게임 내에 불러올 Window
도 제공합니다. 많은 게임에서 JNI
코드를 사용하여 C 또는 C++ 게임 코드와 연결하면서 NativeActivity
의 제한사항을 우회하기 위해 자체 자바 또는 Kotlin 클래스로 Activity
를 확장합니다.
GameActivity
에서 제공하는 기능은 다음과 같습니다.
AppCompatActivity
에서의 상속으로 Android Jetpack 아키텍처 구성요소를 사용할 수 있습니다.다른 Android UI 요소와 상호작용할 수 있도록 하는
SurfaceView
로 렌더링됩니다.Java 활동 이벤트를 처리합니다. 그 결과, C 인터페이스를 통해 모든 Android UI 요소(예:
EditText
,WebView
또는Ad
)를 게임에 통합할 수 있습니다.NativeActivity
와 유사한 C API 그리고android_native_app_glue
라이브러리를 제공합니다.
GameActivity
는 Android 보관 파일(AAR)로 배포됩니다. 이 AAR에는 AndroidManifest.xml
에서 사용하는 Java 클래스와 GameActivity
의 Java 측을 앱의 C/C++ 구현에 연결하는 C 및 C++ 소스 코드가 포함되어 있습니다. GameActivity
1.2.2 이상을 사용하는 경우 C/C++ 정적 라이브러리도 제공됩니다. 해당하는 경우 소스 코드 대신 정적 라이브러리를 사용하는 것이 좋습니다.
CMake 프로젝트나 NDK 빌드에 네이티브 라이브러리와 소스 코드를 노출하는 Prefab
을 통해 이러한 소스 파일이나 정적 라이브러리를 빌드 프로세스의 일부로 포함하세요.
Jetpack Android 게임 페이지의 안내에 따라
GameActivity
라이브러리 종속 항목을 게임의build.gradle
파일에 추가합니다.Android 플러그인 버전(AGP) 4.1 이상으로 다음을 실행하여 prefab을 사용 설정합니다.
- 모듈
build.gradle
파일의android
블록에 다음을 추가합니다.
buildFeatures { prefab true }
- Prefab 버전을 선택하고
gradle.properties
파일로 설정합니다.
android.prefabVersion=2.0.0
이전 AGP 버전을 사용하는 경우 관련 구성 안내는 prefab 문서를 따르세요.
- 모듈
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)
또한 프로젝트의
CmakeLists.txt
에GameActivity.cpp
,GameTextInput.cpp
,android_native_app_glue.c
파일을 포함합니다.
Android에서 Activity를 실행하는 방법
Android 시스템은 활동 수명 주기의 특정 단계에 해당하는 콜백 메서드를 호출하여 활동 인스턴스의 코드를 실행합니다. Android에서 활동을 실행하고 게임을 시작하려면 Android 매니페스트에서 적절한 속성으로 활동을 선언해야 합니다. 자세한 내용은 Activity 소개를 참조하세요.
Android 매니페스트
모든 앱 프로젝트의 프로젝트 소스 세트 루트에는 AndroidManifest.xml 파일이 있어야 합니다. 매니페스트 파일은 Android 빌드 도구, Android 운영체제, Google Play에 앱에 관한 필수 정보를 설명합니다. 여기에는 다음과 같은 항목이 포함됩니다.
Google Play에서 게임을 고유하게 식별하는 패키지 이름 및 앱 ID.
활동, 서비스, broadcast receiver, 콘텐츠 제공자 등 앱 구성요소.
시스템 또는 다른 앱의 보호되는 부분에 액세스하는 권한
게임의 하드웨어 및 소프트웨어 요구사항을 지정하는 기기 호환성
GameActivity
및NativeActivity
의 네이티브 라이브러리 이름(기본값은 libmain.so)
게임에 GameActivity 구현
기본 활동 자바 클래스(
AndroidManifest.xml
파일 내activity
요소에 지정된 클래스)를 만들거나 찾습니다. 이 클래스를 변경하여com.google.androidgamesdk
패키지에서GameActivity
를 확장합니다.import com.google.androidgamesdk.GameActivity; public class YourGameActivity extends GameActivity { ... }
네이티브 라이브러리가 정적 블록을 사용하여 로드되었는지 확인합니다.
public class EndlessTunnelActivity extends GameActivity { static { // Load the native library. // The name "android-game" depends on your CMake configuration, must be // consistent here and inside AndroidManifect.xml System.loadLibrary("android-game"); } ... }
라이브러리 이름이 기본 이름(
libmain.so
)이 아닌 경우AndroidManifest.xml
에 네이티브 라이브러리를 추가합니다.<meta-data android:name="android.app.lib_name" android:value="android-game" />
android_main 구현
android_native_app_glue
라이브러리는 게임이 기본 스레드에서의 블록을 방지하기 위해 별도의 스레드에서GameActivity
수명 주기 이벤트를 관리하는 데 사용하는 소스 코드 라이브러리입니다. 라이브러리를 사용할 때 콜백을 등록하여 터치 입력과 같은 수명 주기 이벤트를 처리합니다. 이벤트를 수신합니다.GameActivity
보관 파일에는 자체 버전의android_native_app_glue
라이브러리가 포함되어 있으므로 NDK 출시에 포함된 버전은 사용할 수 없습니다. 게임에서 NDK에 포함된android_native_app_glue
라이브러리를 사용하는 경우GameActivity
버전으로 전환합니다.android_native_app_glue
라이브러리 소스 코드를 프로젝트에 추가하면GameActivity
와 연결됩니다. 이 라이브러리에 의해 호출되어 게임의 진입점 역할을 하는android_main
함수를 구현합니다. 이 함수에는android_app
구조가 전달됩니다. 이는 게임 및 엔진에 따라 다를 수 있습니다. 다음 예를 참고하세요.#include <game-activity/native_app_glue/android_native_app_glue.h> extern "C" { void android_main(struct android_app* state); }; void android_main(struct android_app* app) { NativeEngine *engine = new NativeEngine(app); engine->GameLoop(); delete engine; }
NativeAppGlueAppCmd에서 정의된 앱 주기 이벤트의 폴링 및 처리와 같이 기본 게임 루프에서
android_app
을 처리합니다. 예를 들어 다음 스니펫은_hand_cmd_proxy
함수를NativeAppGlueAppCmd
핸들러로 등록하고 앱 주기 이벤트를 폴링한 후 처리를 위해android_app::onAppCmd
의 등록된 핸들러로 전송합니다.void NativeEngine::GameLoop() { mApp->userData = this; mApp->onAppCmd = _handle_cmd_proxy; // register your command handler. mApp->textInputState = 0; while (1) { int events; struct android_poll_source* source; // If not animating, block until we get an event; // If animating, don't block. while ((ALooper_pollAll(IsAnimating() ? 0 : -1, NULL, &events, (void **) &source)) >= 0) { if (source != NULL) { // process events, native_app_glue internally sends the outstanding // application lifecycle events to mApp->onAppCmd. source->process(source->app, source); } if (mApp->destroyRequested) { return; } } if (IsAnimating()) { DoFrame(); } } }
자세한 내용은 Endless Tunnel 구현을 참조하세요. NDK 예 주요 차이점은 다음 섹션에서 설명하는 것처럼 이벤트를 처리하는 방법입니다.
이벤트 처리
입력 이벤트가 앱에 도달할 수 있게 하려면 android_app_set_motion_event_filter
및 android_app_set_key_event_filter
를 사용하여 이벤트 필터를 만들고 등록합니다.
기본적으로 native_app_glue
라이브러리는 SOURCE_TOUCHSCREEN 입력의 모션 이벤트만 허용합니다. 세부정보는 참조 문서 및 android_native_app_glue
구현 코드를 확인하세요.
입력 이벤트를 처리하려면 게임 루프에서 android_app_swap_input_buffers()
를 사용해 android_input_buffer
에 관한 참조를 가져옵니다. 여기에는 마지막 폴링 이후 발생한 모션 이벤트 및 키 이벤트가 포함되어 있습니다. 포함된 이벤트 수는 각각 motionEventsCount
및 keyEventsCount
에 저장됩니다.
게임 루프의 각 이벤트를 반복하고 처리합니다. 이 예에서 다음 코드는
motionEvents
를 반복하고handle_event
를 통해 처리합니다.android_input_buffer* inputBuffer = android_app_swap_input_buffers(app); if (inputBuffer && inputBuffer->motionEventsCount) { for (uint64_t i = 0; i < inputBuffer->motionEventsCount; ++i) { GameActivityMotionEvent* motionEvent = &inputBuffer->motionEvents[i]; if (motionEvent->pointerCount > 0) { const int action = motionEvent->action; const int actionMasked = action & AMOTION_EVENT_ACTION_MASK; // Initialize pointerIndex to the max size, we only cook an // event at the end of the function if pointerIndex is set to a valid index range uint32_t pointerIndex = GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT; struct CookedEvent ev; memset(&ev, 0, sizeof(ev)); ev.motionIsOnScreen = motionEvent->source == AINPUT_SOURCE_TOUCHSCREEN; if (ev.motionIsOnScreen) { // use screen size as the motion range ev.motionMinX = 0.0f; ev.motionMaxX = SceneManager::GetInstance()->GetScreenWidth(); ev.motionMinY = 0.0f; ev.motionMaxY = SceneManager::GetInstance()->GetScreenHeight(); } switch (actionMasked) { case AMOTION_EVENT_ACTION_DOWN: pointerIndex = 0; ev.type = COOKED_EVENT_TYPE_POINTER_DOWN; break; case AMOTION_EVENT_ACTION_POINTER_DOWN: pointerIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); ev.type = COOKED_EVENT_TYPE_POINTER_DOWN; break; case AMOTION_EVENT_ACTION_UP: pointerIndex = 0; ev.type = COOKED_EVENT_TYPE_POINTER_UP; break; case AMOTION_EVENT_ACTION_POINTER_UP: pointerIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); ev.type = COOKED_EVENT_TYPE_POINTER_UP; break; case AMOTION_EVENT_ACTION_MOVE: { // Move includes all active pointers, so loop and process them here, // we do not set pointerIndex since we are cooking the events in // this loop rather than at the bottom of the function ev.type = COOKED_EVENT_TYPE_POINTER_MOVE; for (uint32_t i = 0; i < motionEvent->pointerCount; ++i) { _cookEventForPointerIndex(motionEvent, callback, ev, i); } break; } default: break; } // Only cook an event if we set the pointerIndex to a valid range, note that // move events cook above in the switch statement. if (pointerIndex != GAMEACTIVITY_MAX_NUM_POINTERS_IN_MOTION_EVENT) { _cookEventForPointerIndex(motionEvent, callback, ev, pointerIndex); } } } android_app_clear_motion_events(inputBuffer); }
자세한 내용은 GitHub 샘플
_cookEventForPointerIndex()
및 기타 관련 함수를 정의합니다.완료되면 방금 처리한 이벤트의 큐를 삭제해야 합니다.
android_app_clear_motion_events(mApp);
추가 리소스
GameActivity
에 관해 자세히 알아보려면 다음을 참고하세요.
- GameActivity 및 AGDK 출시 노트
- GameActivity에서 GameTextInput 사용
- NativeActivity 이전 가이드
- GameActivity 참조 문서
- GameActivity 구현
GameActivity 관련 버그를 신고하거나 새로운 기능을 요청하려면 GameActivity Issue Tracker를 사용하세요.