Stay organized with collections Save and categorize content based on your preferences.

Di chuyển từ NativeActivity Một phần trong Android Game Development Kit.

GameActivity dựa trên NativeActivity từ khung Android với các tính năng nâng cao mới:

  • Hỗ trợ Fragment từ Jetpack.
  • Thêm tùy chọn hỗ trợ TextInput để hỗ trợ tích hợp bàn phím mềm.
  • Xử lý các sự kiện nhấn và chính trong lớp GameActivity thay vì giao diện NativeActivity onInputEvent.

Việc di chuyển từ NativeActivity sang GameActivity tương đối đơn giản và yêu cầu những thay đổi sau đây về ứng dụng:

  1. Cập nhật tập lệnh bản dựng Java và C/C++ của bạn.
  2. Tích hợp mã nguồn Java và lớp nguồn C/C++ mới.

Phần còn lại của hướng dẫn này trình bày chi tiết về các bước cần thiết. Trước khi di chuyển, bạn nên đọc hướng dẫn bắt đầu sử dụng để biết thông tin cấp cao về cách hoạt động của GameActivity.

Cập nhật tập lệnh bản dựng Java

GameActivity được phân phối dưới dạng một thư viện Jetpack; đảm bảo áp dụng tập lệnh Gradle để cập nhật các bước mô tả trong hướng dẫn bắt đầu sử dụng:

  1. Bật thư viện Jetpack trong tệp gradle.properties của dự án:

    android.useAndroidX=true
    
  2. Nếu muốn, bạn có thể chỉ định một phiên bản Prefab, trong cùng một tệp gradle.properties, ví dụ:

    android.prefabVersion=2.0.0
    
  3. Bật tính năng Prefab trong tệp build.gradle của ứng dụng:

    android {
        ... // other configurations
        buildFeatures.prefab true
    }
    
  4. Thêm phần phụ thuộc GameActivity vào ứng dụng của bạn:

    1. Thêm thư viện coregames-activity.
    2. Nếu cấp độ API tối thiểu được hỗ trợ hiện tại của bạn là dưới 16, hãy cập nhật lên ít nhất 16.
    3. Cập nhật phiên bản SDK đã biên dịch lên phiên bản mà thư viện games-activity yêu cầu. Jetpack thường yêu cầu phiên bản SDK mới nhất tại thời điểm tạo bản phát hành.

    Tệp build.gradle mới cập nhật của bạn có thể trông như sau:

    android {
        compiledSdkVersion 31
        ... // other configurations.
        defaultConfig {
            minSdkVersion 16
        }
        ... // other configurations.
    
        buildFeatures.prefab true
    }
    dependencies {
        implementation 'androidx.core:core:1.4.2'
        implementation 'androidx.games:games-activity:1.1.0'
    }
    

Cập nhật mã Kotlin hoặc Java

NativeActivity có thể được dùng làm hoạt động khởi động và tạo ứng dụng toàn màn hình. Hiện tại, GameActivity không thể được sử dụng làm hoạt động khởi động. Các ứng dụng phải lấy một lớp từ GameActivity và sử dụng lớp đó làm hoạt động khởi động. Bạn cũng phải thực hiện các thay đổi khác đối với cấu hình để tạo ứng dụng toàn màn hình.

Các bước sau đây giả định ứng dụng của bạn sử dụng NativeActivity làm hoạt động khởi động. Nếu trường hợp này không xảy ra, bạn có thể bỏ qua hầu hết các trường hợp.

  1. Tạo một tệp trong Kotlin hoặc Java để lưu trữ hoạt động khởi động mới. Ví dụ: mã sau đây tạo MainActivity làm hoạt động khởi động và tải thư viện gốc chính của ứng dụng, 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");
          }
      }
    
  2. Tạo giao diện toàn ứng dụng ở chế độ toàn màn hình trong tệp 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. Áp dụng giao diện cho ứng dụng trong tệp AndroidManifest.xml:

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

    Để xem hướng dẫn chi tiết về cách điều chỉnh cho chế độ toàn màn hình, vui lòng tham khảo hướng dẫn nhúng và ví dụ về cách triển khai trong kho lưu trữ mẫu trò chơi.

Hướng dẫn di chuyển này không thay đổi tên thư viện gốc. Nếu bạn thay đổi tên đó, hãy đảm bảo tên của các thư viện gốc nhất quán ở ba vị trí sau:

  • Mã Kotlin hoặc Java:

    System.loadLibrary(“AndroidGame”)
    
  • AndroidManifest.xml:

    <meta-data android:name="android.app.lib_name"
            android:value="AndroidGame" />
    
  • Bên trong tệp tập lệnh bản dựng C/C++, ví dụ: CMakeLists.txt

    add_library(AndroidGame ...)
    

C/C++ tạo bản cập nhật tập lệnh

Các hướng dẫn trong phần này sử dụng cmake làm ví dụ. Nếu ứng dụng của bạn sử dụng ndk-build, bạn cần liên kết các ứng dụng đó với các lệnh tương đương được mô tả trong trang tài liệu về ndk-build.

Hiện tại, hoạt động triển khai C/C++ của GameActivity chỉ là phát hành nguồn, được đóng gói bên trong AAR với tiện ích prefab. Mã gốc bao gồm các nguồn C/C++ của GameActivity và mã native_app_glue. Các mã này cần được xây dựng cùng với mã C/C++ của ứng dụng.

Các ứng dụng NativeActivity đã sử dụng mã native_app_glue được vận chuyển bên trong NDK. Bạn phải thay thế điều này bằng phiên bản native_app_glue của GameActivity. Ngoài ra, tất cả cmake bước trong tài liệu hướng dẫn bắt đầu này đều được áp dụng:

  • Thêm game-activity vào phần phụ thuộc mô-đun C/C++ của ứng dụng:

    find_package(game-activity REQUIRED CONFIG)
        ...
    target_link_libraries(AndroidGame game-activity::game-activity)
    
  • Xóa tất cả thông tin tham chiếu đến mã native_app_glue của NDK, chẳng hạn như:

    ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c
        ...
    set(CMAKE_SHARED_LINKER_FLAGS
        "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
    
  • Bao gồm các tệp nguồn của 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)
    

Cập nhật mã nguồn C/C++

Làm theo các bước sau để thay thế các tệp tham chiếu NativeActivity trong ứng dụng của bạn bằng GameActivity:

  • Sử dụng native_app_glue được phát hành với GameActivity. Tìm kiếm và thay thế mọi hoạt động sử dụng android_native_app_glue.h thành:

    #include <game-activity/native_app_glue/android_native_app_glue.h>
    
  • Đặt cả bộ lọc sự kiện chuyển động lẫn bộ lọc sự kiện chính thành giá trị NULL để ứng dụng của bạn có thể nhận các sự kiện đầu vào từ mọi thiết bị đầu vào. Thông thường, bạn sẽ phải thực hiện việc này bên trong hàm 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.
    }
    
  • Xóa mã liên quan đến AInputEvent và thay thế mã đó bằng cách triển khai InputBuffer của 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;
    }
    
  • Xem lại và cập nhật logic được liên kết với AInputEvent của NativeActivity. Như đã thấy trong bước trước, quá trình xử lý InputBuffer của GameActivity nằm ngoài vòng lặp ALooper_pollAll().

  • Thay thế mức sử dụng android_app::activity->clazz bằng android_app:: activity->javaGameActivity. GameActivity đổi tên bản sao Java GameActivity.

Các bước bổ sung

Các bước trước có tính đến cả chức năng của NativeActivity, nhưng GameActivity có các tính năng bổ sung mà bạn có thể muốn sử dụng:

Bạn nên khám phá và sử dụng những tính năng này cho phù hợp với trò chơi của mình.

Nếu bạn có thắc mắc hoặc đề xuất về GameActivity hoặc thư viện AGDK khác, hãy tạo lỗi để báo cho chúng tôi.