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 リポジトリの実装例をご覧ください。
この移行ガイドでは、ネイティブ ライブラリ名を変更しません。変更する場合は、ネイティブ ライブラリ名が次の 3 つの場所で一致していることを確認してください。
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/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 で定義されている新しいウィンドウ InSet コマンド。
こうした機能を試し、必要に応じてご自身のゲームに導入することをおすすめします。
GameActivity や他の AGDK ライブラリに関する質問や提案がある場合は、バグを登録してお知らせください。