GameActivity'yi kullanmaya başlayın Android Game Development Kit'in bir parçasıdır.

Bu kılavuzda GameActivity kurulumu, entegrasyonu ve Android oyununuzdaki etkinliklerin nasıl yönetileceği açıklanmaktadır.

GameActivity, kritik API'leri kullanma sürecini basitleştirerek C veya C++ oyununuzu Android'e taşımanıza yardımcı olur. Daha önce NativeActivity, oyunlar için önerilen sınıftı. GameActivity, bu sınıfı oyunlar için önerilen sınıf olarak değiştirir ve API düzeyi 19 ile geriye dönük olarak uyumludur.

GameActivity'nin entegre edildiği bir örnek için oyun-örnekleri deposuna bakın.

Başlamadan önce

Dağıtım edinmek için GameActivity sürüme göz atın.

Derlemenizi ayarlama

Android'de Activity, oyununuz için giriş noktası olarak kullanılır ve içinde çizim yapabileceğiniz Window görevi de görür. Birçok oyun, C veya C++ oyun kodlarına geçiş yapmak için JNI kodunu kullanırken NativeActivity ile ilgili sınırlamaları ortadan kaldırmak için bu Activity oyununu kendi Java veya Kotlin sınıfıyla genişletir.

GameActivity aşağıdaki özellikleri sunar:

GameActivity bir Android Arşivi (AAR) olarak dağıtılır. Bu AAR, AndroidManifest.xml uygulamanızda kullandığınız Java sınıfının yanı sıra GameActivity uygulamasının Java tarafını uygulamanın C/C++ uygulamasına bağlayan C ve C++ kaynak kodunu içerir. GameActivity 1.2.2 veya sonraki bir sürümünü kullanıyorsanız C/C++ statik kitaplığı da sağlanır. Uygun olduğunda, kaynak kodu yerine statik kitaplığı kullanmanızı öneririz.

Bu kaynak dosyalarını veya statik kitaplığı, yerel kitaplıkları ve kaynak kodunu CMake projenize ya da NDK derlemenize sunan Prefab aracılığıyla derleme işleminizin bir parçası olarak dahil edin.

  1. GameActivity kitaplığı bağımlılığını oyununuzun build.gradle dosyasına eklemek için Jetpack Android Games sayfasındaki talimatları uygulayın.

  2. Android Plugin Version (AGP) 4.1+ ile aşağıdaki adımları uygulayarak prefab'i etkinleştirin:

    • Modülünüzün build.gradle dosyasındaki android bloğuna aşağıdaki kodu ekleyin:
    buildFeatures {
        prefab true
    }
    
    android.prefabVersion=2.0.0
    

    Önceki AGP sürümlerini kullanıyorsanız ilgili yapılandırma talimatları için prefab belgelerindeki talimatları uygulayın.

  3. Projenize aşağıdaki gibi C/C++ statik kitaplığını veya C/++ kaynak kodunu içe aktarın.

    Statik kitaplık

    Projenizin CMakeLists.txt dosyasındaki game-activity statik kitaplığını game-activity_static prefab modülüne aktarın:

    find_package(game-activity REQUIRED CONFIG)
    target_link_libraries(${PROJECT_NAME} PUBLIC log android
    game-activity::game-activity_static)
    

    Kaynak kodu

    Projenizin CMakeLists.txt dosyasına game-activity paketini içe aktarın ve hedefinize ekleyin. game-activity paketi için libandroid.so gerekir. Bu nedenle, eksikse paketi içe aktarmanız gerekir.

    find_package(game-activity REQUIRED CONFIG)
    ...
    target_link_libraries(... android game-activity::game-activity)
    

    Ayrıca şu dosyaları projenizin CmakeLists.txt öğesine ekleyin: GameActivity.cpp, GameTextInput.cpp ve android_native_app_glue.c.

Android, Etkinliğinizi nasıl başlatır?

Android sistemi, etkinlik yaşam döngüsünün belirli aşamalarına karşılık gelen geri çağırma yöntemlerini çağırarak Etkinlik örneğinizde kod çalıştırır. Android'in etkinliğinizi başlatabilmesi ve oyununuzu başlatabilmesi için etkinliğinizi Android Manifest'te uygun özelliklerle beyan etmeniz gerekir. Daha fazla bilgi için Etkinliklere Giriş bölümünü inceleyin.

Android Manifesti

Her uygulama projesinin, proje kaynağı kümesinin kök dizininde bir AndroidManifest.xml dosyası olmalıdır. Manifest dosyası, uygulamanızla ilgili temel bilgileri Android derleme araçlarına, Android işletim sistemine ve Google Play'e açıklar. Bu hizmet ve özellikler arasında aşağıdakiler bulunmaktadır:

Oyununuzda GameActivity'yi uygulayın

  1. Ana etkinlik Java sınıfınızı (AndroidManifest.xml dosyanızın içindeki activity öğesinde belirtilen) oluşturun veya tanımlayın. com.google.androidgamesdk paketinden GameActivity öğesini genişletmek için bu sınıfı değiştirin:

    import com.google.androidgamesdk.GameActivity;
    
    public class YourGameActivity extends GameActivity { ... }
    
  2. Yerel kitaplığınızın başlangıçta statik blok kullanarak yüklendiğinden emin olun:

    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");
      }
      ...
    }
    
  3. Kitaplığınızın adı varsayılan ad (libmain.so) değilse yerel kitaplığınızı AndroidManifest.xml'e ekleyin:

    <meta-data android:name="android.app.lib_name"
     android:value="android-game" />
    

android_main'i uygulama

  1. android_native_app_glue kitaplığı, oyununuzun ana iş parçacığınızda engellemeyi önlemek amacıyla GameActivity yaşam döngüsü etkinliklerini ayrı bir iş parçacığında yönetmek için kullandığı bir kaynak kodu kitaplığıdır. Kitaplığı kullanırken, dokunmatik giriş etkinlikleri gibi yaşam döngüsü olaylarını işlemek için geri çağırmayı kaydedersiniz. GameActivity arşivinde android_native_app_glue kitaplığının kendi sürümü bulunduğundan NDK sürümlerine dahil olan sürümü kullanamazsınız. Oyunlarınız NDK'nın içerdiği android_native_app_glue kitaplığını kullanıyorsa GameActivity sürümüne geçin.

    android_native_app_glue kitaplığı kaynak kodunu projenize ekledikten sonra GameActivity ile arayüz oluşturur. Kitaplık tarafından çağrılan ve oyununuzun giriş noktası olarak kullanılan android_main adlı işlevi uygulayın. android_app adlı bir yapıdan geçmiştir. Bu durum, oyununuz ve motorunuz için farklı olabilir. Aşağıda bununla ilgili bir örnek verilmiştir:

    #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;
    }
    
  2. Ana oyun döngünüzdeki android_app işlemini yapın. Örneğin, NativeAppGlueAppCmd öğesinde tanımlanan uygulama döngüsü etkinliklerini yoklama ve işleme. Örneğin, aşağıdaki snippet _hand_cmd_proxy işlevini NativeAppGlueAppCmd işleyicisi olarak kaydeder, ardından uygulama döngüsü etkinliklerini yoklar ve işlenmek üzere kayıtlı işleyiciye(android_app::onAppCmd içinde) gönderir:

    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();
        }
      }
    }
    
  3. Daha fazla okumak için Sonsuz Tünel NDK örneğinin uygulanmasını inceleyin. Temel fark, etkinliklerin bir sonraki bölümde gösterildiği gibi nasıl işleneceği olacaktır.

Etkinlikleri işleme

Giriş etkinliklerinin uygulamanıza ulaşmasını sağlamak için etkinlik filtrelerinizi android_app_set_motion_event_filter ve android_app_set_key_event_filter ile oluşturup kaydedin. Varsayılan olarak, native_app_glue kitaplığı yalnızca SOURCE_TOUCHSCREEN girişinden gelen hareket etkinliklerine izin verir. Ayrıntılar için referans dokümanı ve android_native_app_glue uygulama kodunu mutlaka inceleyin.

Giriş etkinliklerini işlemek için oyun döngünüzde android_app_swap_input_buffers() ile android_input_buffer referansı alın. Bunlar, son anketten sonra gerçekleşen hareket etkinliklerini ve önemli etkinlikleri içerir. İçerilen etkinliklerin sayısı sırasıyla motionEventsCount ve keyEventsCount içinde depolanır.

  1. Oyun döngünüzdeki her etkinliği yineleyin ve yönetin. Bu örnekte, aşağıdaki kod motionEvents iterasyonunu tekrarlar ve bunları handle_event aracılığıyla işler:

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

    _cookEventForPointerIndex() ve ilgili diğer işlevlerin uygulanması için GitHub örneğine göz atın.

  2. İşiniz bittiğinde, az önce işlediğiniz etkinlikler sırasını temizlemeyi unutmayın:

    android_app_clear_motion_events(mApp);
    

Ek kaynaklar

GameActivity hakkında daha fazla bilgi için aşağıdakilere bakın:

GameActivity'ye hata bildirmek veya yeni özellikler isteğinde bulunmak için GameActivity sorun izleyiciyi kullanın.