העברה מ-NativeActivity חלק מ-Android Game Development Kit.
בדף הזה נסביר איך לבצע העברה מ
NativeActivity
עד
GameActivity
בפרויקט המשחק ל-Android.
GameActivity
מבוסס על NativeActivity
מ-Android
עם שיפורים ותכונות חדשות:
- תמיכה בערך
Fragment
מ-Jetpack. - הוספת תמיכה ב-
TextInput
כדי לאפשר שילוב של מקלדת עם יכולת שחזור. - טיפול באירועים מרכזיים ובאירועים מרכזיים ברמת Java
GameActivity
במקום ממשקNativeActivity
onInputEvent
.
לפני ההעברה, מומלץ לקרוא את
מדריך לתחילת העבודה, שמתאר
כדי להגדיר ולשלב את GameActivity
בפרויקט.
עדכונים לגבי סקריפט build של Java
GameActivity
מופץ בתור
ספריית Jetpack. צריך להקפיד להחיל את שלבי העדכון של הסקריפט של Gradle
במדריך לתחילת העבודה:
מפעילים את ספריית Jetpack בקובץ
gradle.properties
של הפרויקט:android.useAndroidX=true
אפשר לציין גרסה Prefab באותו קובץ
gradle.properties
, לדוגמה:android.prefabVersion=2.0.0
הפעלת התכונה Prefab בקובץ
build.gradle
של האפליקציה:android { ... // other configurations buildFeatures.prefab true }
מוסיפים את התלות
GameActivity
לאפליקציה:- מוסיפים את הספריות
core
ו-games-activity
. - אם רמת ה-API המינימלית הנתמכת הנוכחית שלך היא פחות מ-16, צריך לעדכן אותה. ל-16 תווים לפחות.
- צריך לעדכן את גרסת ה-SDK שעברה הידור לגרסה ש-
games-activity
שנדרשת. בדרך כלל, Jetpack מחייב את גרסת ה-SDK העדכנית ביותר את זמן ה-build של יחידת ההפצה.
קובץ
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
כפעילות ההפעלה טוען את ספריית ה-Native הראשית של האפליקציה,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>
הוראות מפורטות למצב מסך מלא זמינות במאמר והטמעה לדוגמה מאגר הדוגמאות למשחקים
המדריך הזה להעברת נתונים לא משנה את השם של הספרייה המקורית. אם משנים לוודא שהשמות של הספריות המקוריות עקביים בשלושת השמות הבאים מיקומים:
קוד Kotlin או Java:
System.loadLibrary(“AndroidGame”)
AndroidManifest.xml
:<meta-data android:name="android.app.lib_name" android:value="AndroidGame" />
בתוך קובץ סקריפט ה-build C/C++, לדוגמה
CMakeLists.txt
:add_library(AndroidGame ...)
עדכונים לגבי סקריפט build של C/C++
בהוראות בקטע הזה נעשה שימוש ב-cmake
כדוגמה. אם הבקשה שלכם
משתמשת ב-ndk-build
, צריך למפות אותן לפקודות המקבילות שמתוארות כאן
ndk-build לדף התיעוד.
הטמעת C/C++ של GameActivity סיפקה גרסה של קוד מקור. לגרסה 1.2.2 ואילך יש גרסת ספרייה סטטית. ההגדרות הסטטיות הוא סוג ההפצה המומלץ.
יחידת ההפצה ארוזה ב-AAR עם
prefab
של Google. הקוד המקורי כולל את מקורות C/C++ של GameActivity
קוד native_app_glue
. הם צריכים להיבנות יחד עם
בקוד C/C++ של האפליקציה.
NativeActivity
אפליקציות כבר משתמשות בnative_app_glue
הקוד נשלח בתוך NDK. יש להחליף אותה בגרסה של GameActivity
מתוך native_app_glue
. חוץ מזה, כל cmake
השלבים מתועדים ב-
מומלץ לעיין במדריך לתחילת העבודה:
מייבאים את הספרייה הסטטית C/C++ או את קוד המקור C/++ אל פרויקט כפי שמפורט בהמשך.
ספרייה סטטית
בקובץ
CMakeLists.txt
של הפרויקט, מייבאים את הנתונים הסטטייםgame-activity
את הספרייה למודול הטרוםgame-activity_static
: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)
מסירים את כל ההפניות לקוד
native_app_glue
של NDK, כמו:${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
אם מופיע UnsatsifiedLinkError
עבור
פונקציית com.google.androidgamesdk.GameActivity.initializeNativeCode()
, הוספה
את הקוד הבא לקובץ CMakeLists.txt
שלך:
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -u \
Java_com_google_androidgamesdk_GameActivity_initializeNativeCode")
עדכונים בקוד המקור של C/C++
צריך לפעול לפי השלבים הבאים כדי להחליף NativeActivity
קובצי עזר ב-
אפליקציה עם GameActivity
:
צריך להשתמש ב-
native_app_glue
שהושק עםGameActivity
. חיפוש ו החלפת כל השימוש ב-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
כמו שמוצג בשלב הקודם,InputBuffer
של GameActivity העיבוד נמצא מחוץ ללולאה שלALooper_pollAll()
.החלפת השימוש ב-
android_app::activity->clazz
ב-android_app:: activity->javaGameActivity
. השם של GameActivity השתנה מופע של JavaGameActivity
.
שלבים נוספים
השלבים הקודמים מתייחסים לפונקציונליות של NativeActivity, אבל ב-GameActivity
לגבי פיצ'רים נוספים שאולי תרצו להשתמש בהם:
- TextInput (קלט טקסט).
- בקר משחקים.
- מקטע.
- פקודות InSets של חלון חדש מוגדרות ב-NativeAppGlueAppCmd.
מומלץ לבדוק את התכונות האלה וליישם אותן בהתאם משחקים.
אם יש לך שאלות או המלצות לגבי GameActivity או AGDK אחר ספריות, ליצור באג כדי לדווח לנו.