از قسمت NativeActivity کیت توسعه بازی اندروید مهاجرت کنید.

این صفحه نحوه مهاجرت از NativeActivity به GameActivity را در پروژه بازی اندروید خود شرح می دهد.

GameActivity مبتنی بر NativeActivity از فریم ورک اندروید است، با پیشرفت‌ها و ویژگی‌های جدید:

  • Fragment از Jetpack پشتیبانی می کند.
  • برای تسهیل یکپارچه سازی صفحه کلید نرم، پشتیبانی TextInput را اضافه می کند.
  • رویدادهای لمسی و کلیدی را در کلاس GameActivity جاوا به جای رابط NativeActivity onInputEvent مدیریت می کند.

قبل از مهاجرت، توصیه می‌کنیم راهنمای شروع را مطالعه کنید، که نحوه راه‌اندازی و ادغام GameActivity در پروژه را توضیح می‌دهد.

به روز رسانی اسکریپت ساخت جاوا

GameActivity به عنوان یک کتابخانه Jetpack توزیع شده است. مطمئن شوید که مراحل به‌روزرسانی اسکریپت Gradle را که در راهنمای شروع کار توضیح داده شده است، اعمال کنید:

  1. کتابخانه Jetpack را در فایل gradle.properties پروژه خود فعال کنید:

    android.useAndroidX=true
    
  2. به صورت اختیاری، یک نسخه Prefab را در همان فایل gradle.properties مشخص کنید، برای مثال:

    android.prefabVersion=2.0.0
    
  3. ویژگی Prefab را در فایل build.gradle برنامه خود فعال کنید:

    android {
        ... // other configurations
        buildFeatures.prefab true
    }
    
  4. وابستگی GameActivity به برنامه خود اضافه کنید:

    1. core و کتابخانه های games-activity را اضافه کنید.
    2. اگر حداقل سطح API پشتیبانی شده فعلی شما کمتر از 16 است، آن را به حداقل 16 به روز کنید.
    3. نسخه 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'
    }
    

به روز رسانی کد کاتلین یا جاوا

NativeActivity می تواند به عنوان یک فعالیت راه اندازی استفاده شود و یک برنامه تمام صفحه ایجاد کند. در حال حاضر، GameActivity نمی تواند به عنوان فعالیت راه اندازی استفاده شود. برنامه ها باید یک کلاس از GameActivity گرفته و از آن به عنوان فعالیت راه اندازی استفاده کنند. همچنین برای ایجاد یک برنامه تمام صفحه باید تغییرات بیشتری در پیکربندی ایجاد کنید.

مراحل زیر فرض می کنند که برنامه شما از NativeActivity به عنوان فعالیت راه اندازی استفاده می کند. اگر اینطور نیست، می توانید از بسیاری از آنها صرف نظر کنید.

  1. یک فایل Kotlin یا Java برای میزبانی فعالیت راه اندازی جدید ایجاد کنید. برای مثال، کد زیر MainActivity به عنوان فعالیت راه‌اندازی ایجاد می‌کند و کتابخانه اصلی برنامه، libAndroidGame.so را بارگیری می‌کند:

    کاتلین

    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")
           }
       }
    }
    

    جاوا

      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");
          }
      }
    
  2. یک تم برنامه تمام صفحه در فایل 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>
    
  3. تم را روی برنامه در فایل AndroidManifest.xml اعمال کنید:

    <application  android:theme=”@style/Application.Fullscreen”>
         <!-- other configurations not listed here. -->
    </application>
    

    برای دستورالعمل های دقیق برای حالت تمام صفحه، به راهنمای همهجانبه و اجرای نمونه در مخزن بازی-نمونه ها مراجعه کنید.

این راهنمای مهاجرت نام کتابخانه بومی را تغییر نمی دهد. اگر آن را تغییر می‌دهید، مطمئن شوید که نام‌های کتابخانه بومی در سه مکان زیر مطابقت دارند:

  • کد کاتلین یا جاوا:

    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 نگاشت کنید.

پیاده سازی C/C++ GameActivity یک کد منبع را ارائه کرده است. برای نسخه 1.2.2 بعدی، یک نسخه کتابخانه ایستا ارائه شده است. کتابخانه ایستا نوع انتشار توصیه شده است.

نسخه در داخل AAR با ابزار prefab بسته بندی شده است. کد بومی شامل منابع C/C++ GameActivity و کد native_app_glue است. آنها باید همراه با کد C/C++ برنامه شما ساخته شوند.

برنامه‌های NativeActivity قبلاً از کد native_app_glue ارسال شده در NDK استفاده می‌کنند. باید آن را با نسخه native_app_glue GameActivity جایگزین کنید. به غیر از آن، تمام مراحل cmake مستند در راهنمای شروع کار اعمال می شود:

  • کتابخانه ایستا C/C++ یا کد منبع C/++ را به صورت زیر وارد پروژه خود کنید.

    کتابخانه ایستا

    در فایل CMakeLists.txt پروژه خود، کتابخانه استاتیک game-activity را به ماژول prefab 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 را حل کنید

اگر برای تابع 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 دنبال کنید:

  • از 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 را حذف کرده و با اجرای InputBuffer GameActivity جایگزین کنید:

    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;
    }
    
  • منطق پیوست شده به AInputEvent NativeActivity را بررسی و به‌روزرسانی کنید. همانطور که در مرحله قبل نشان داده شد، پردازش InputBuffer GameActivity خارج از حلقه ALooper_pollAll() است.

  • android_app::activity->clazz usage را با android_app:: activity->javaGameActivity جایگزین کنید. GameActivity نمونه Java GameActivity را تغییر نام می دهد.

مراحل اضافی

مراحل قبلی عملکرد NativeActivity را پوشش می دهد، اما GameActivity دارای ویژگی های اضافی است که ممکن است بخواهید از آنها استفاده کنید:

توصیه می‌کنیم این ویژگی‌ها را بررسی کنید و آن‌ها را متناسب با بازی‌های خود بکار ببرید.

اگر سؤال یا توصیه‌ای برای GameActivity یا سایر کتابخانه‌های AGDK دارید، یک اشکال ایجاد کنید تا به ما اطلاع دهید.