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

Bu kılavuzda, GameActivity'i nasıl kurup entegre edeceğiniz ve Android oyununuzdaki etkinlikleri nasıl yöneteceğiniz açıklanmaktadır.

GameActivity, kritik API'leri kullanma sürecini basitleştirerek C veya C++ oyununuzu Android'e taşımanıza yardımcı olur. Önceden NativeActivity oyunlar için önerilen sınıftı. GameActivity, oyunlar için önerilen sınıfın yerini alır ve API düzeyi 19 ile geriye dönük uyumludur.

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

Başlamadan önce

Dağıtım almak için GameActivity sürümlerini inceleyin.

Derlemenizi ayarlama

Android'de, Activity, oyununuz için giriş noktası görevi görür ve içinde çizim yapmak üzere Window olanağı sağlar. Birçok oyun, C veya C++ oyun kodlarına geçiş yapmak için JNI kodunu kullanırken NativeActivity oyunundaki 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, 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 daha yeni bir sürüm kullanıyorsanız C/C++ statik kitaplığı da sağlanır. Uygun durumlarda, kaynak kodu yerine statik kitaplığı kullanmanızı öneririz.

Bu kaynak dosyaları veya statik kitaplığı, Prefab aracılığıyla derleme sürecinizin bir parçası olarak dahil edin. Bu işlem, yerel kitaplıkları ve kaynak kodunu CMake projenize veya NDK derlemenize gösterir.

  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ğıdakileri yaparak prefab'i etkinleştirin:

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

    Daha eski AGP sürümlerini kullanıyorsanız ilgili yapılandırma talimatları için prefab dokümanlarındaki 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ında 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ında game-activity paketini içe aktarın ve hedefinize ekleyin. game-activity paketi için libandroid.so gerekir. Bu nedenle, bu paket yoksa içe aktarmanız gerekir.

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

    Ayrıca şu dosyaları da projenizin CmakeLists.txt bölümüne 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 Android Manifest'te uygun özelliklerle etkinliğinizi bildirmeniz 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. Bunu başarmak için şunlardan yararlanırız:

Oyununuzda GameActivity'yi uygulama

  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. GameActivity sınıfını com.google.androidgamesdk paketinden 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 geri çağırmayı, dokunmatik giriş etkinlikleri gibi yaşam döngüsü olaylarını işlemek için kaydedersiniz. GameActivity arşivinde android_native_app_glue kitaplığının kendi sürümü yer alır. Bu nedenle NDK sürümlerindeki sürümü kullanamazsınız. Oyunlarınız NDK'da bulunan 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ı bir işlevi uygulayın. android_app adlı bir yapıdan geçer. Bu, oyununuz ve motorunuz için farklılık gösterebilir. 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. android_app işlemini ana oyun döngünüzde yapın. Örneğin, NativeAppGlueAppCmd'de tanımlanan uygulama döngüsü etkinliklerini yoklama ve işleme. Örneğin, aşağıdaki snippet _hand_cmd_proxy işlevini NativeAppGlueAppCmd işleyici olarak kaydeder, ardından uygulama döngüsü etkinliklerini sorgular 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, bir sonraki bölümde gösterildiği gibi etkinliklerin nasıl ele alınacağıdı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 koduna göz atın.

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 bölümlerinde depolanır.

  1. Oyun döngünüzdeki her etkinliği yineleyin ve yönetin. Bu örnekte, aşağıdaki kod motionEvents yinelenir 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 diğer ilgili işlevlerin uygulanması için GitHub örneğine bakın.

  2. İşiniz bittiğinde, az önce gerçekleştirdiğiniz etkinlikler sırasını temizlemeyi unutmayın:

    android_app_clear_motion_events(mApp);
    

Ek kaynaklar

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

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