OpenGL ES ile grafikleri yapılandırma

Oyununuzda nesne ve imge çizmek için görüntü, yüzey ve bağlam değişkenlerini yapılandırmanız, oyun döngünüzde oluşturmayı ayarlamanız ve her sahne ile nesneyi çizmeniz gerekir.

C veya C++ oyunlarında ekrana resim çizmenin iki yolu vardır, örneğin OpenGL ES veya Vulkan.

Başlamadan önce

Henüz yapmadıysanız Android projenizde bir GameActivity nesnesi oluşturun.

OpenGL ES değişkenlerini ayarlama

  1. Oyununuzu oluşturmak için ekran, yüzey, bağlam ve yapılandırmaya ihtiyacınız olacak. Oyun motorunuzun başlık dosyasına aşağıdaki OpenGL ES değişkenlerini ekleyin:

    class NativeEngine {
     //...
     private:
      EGLDisplay mEglDisplay;
      EGLSurface mEglSurface;
      EGLContext mEglContext;
      EGLConfig mEglConfig;
    
      bool mHasFocus, mIsVisible, mHasWindow;
      bool mHasGLObjects;
      bool mIsFirstFrame;
    
      int mSurfWidth, mSurfHeight;
    }
    
  2. Oyun motorunuzun kurucusunda, değişkenlerle ilgili varsayılan değerleri başlatın.

    NativeEngine::NativeEngine(struct android_app *app) {
      //...
      mEglDisplay = EGL_NO_DISPLAY;
      mEglSurface = EGL_NO_SURFACE;
      mEglContext = EGL_NO_CONTEXT;
      mEglConfig = 0;
    
      mHasFocus = mIsVisible = mHasWindow = false;
      mHasGLObjects = false;
      mIsFirstFrame = true;
    
      mSurfWidth = mSurfHeight = 0;
    }
    
  3. Oluşturmak için ekranı başlatın.

    bool NativeEngine::InitDisplay() {
      if (mEglDisplay != EGL_NO_DISPLAY) {
        return true;
      }
    
      mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
      if (EGL_FALSE == eglInitialize(mEglDisplay, 0, 0)) {
        LOGE("NativeEngine: failed to init display, error %d", eglGetError());
        return false;
      }
      return true;
    }
    
  4. Yüzey, EGL tarafından ayrılmış ekran dışı arabellek (pbuffer) veya Android OS tarafından ayrılmış bir pencere olabilir. Bu yüzeyi başlatın:

    bool NativeEngine::InitSurface() {
      ASSERT(mEglDisplay != EGL_NO_DISPLAY);
      if (mEglSurface != EGL_NO_SURFACE) {
        return true;
      }
    
      EGLint numConfigs;
      const EGLint attribs[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // request OpenGL ES 2.0
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_BLUE_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_RED_SIZE, 8,
        EGL_DEPTH_SIZE, 16,
        EGL_NONE
      };
    
      // Pick the first EGLConfig that matches.
      eglChooseConfig(mEglDisplay, attribs, &mEglConfig, 1, &numConfigs);
      mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mApp->window,
                                           NULL);
      if (mEglSurface == EGL_NO_SURFACE) {
        LOGE("Failed to create EGL surface, EGL error %d", eglGetError());
        return false;
      }
      return true;
    }
    
  5. Oluşturma bağlamını başlatın. Bu örnek, bir OpenGL ES 2.0 bağlamı oluşturur:

    bool NativeEngine::InitContext() {
      ASSERT(mEglDisplay != EGL_NO_DISPLAY);
      if (mEglContext != EGL_NO_CONTEXT) {
        return true;
      }
    
      // OpenGL ES 2.0
      EGLint attribList[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
      mEglContext = eglCreateContext(mEglDisplay, mEglConfig, NULL, attribList);
      if (mEglContext == EGL_NO_CONTEXT) {
        LOGE("Failed to create EGL context, EGL error %d", eglGetError());
        return false;
      }
      return true;
    }
    
  6. Çizimden önce OpenGL ES ayarlarınızı yapılandırın. Bu örnek, her karenin başında yürütülür. Derinlik testi sağlar, şeffaf rengi siyah olarak ayarlar, renk ve derinlik arabelleklerini temizler.

    void NativeEngine::ConfigureOpenGL() {
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glEnable(GL_DEPTH_TEST);
      glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    }
    

Oyun döngüsüyle oluştur

  1. Oyun döngüsü bir kare oluşturur ve kullanıcı çıkana kadar süresiz olarak tekrarlanır. Oyununuz kareler arasında:

    • Giriş, ses çıkışı ve ağ iletişimi etkinlikleri gibi işleme etkinlikleri.

    • Oyun mantığını ve kullanıcı arayüzünü güncelleyin.

    • Ekran için bir kare oluşturun.

    Ekrana bir kare oluşturmak için DoFrame yöntemi, oyun döngüsünde süresiz olarak çağrılır:

    void NativeEngine::GameLoop() {
      // Loop indefinitely.
      while (1) {
        int events;
        struct android_poll_source* source;
    
        // If not animating, block until we get an event.
        while ((ALooper_pollAll(IsAnimating() ? 0 : -1, NULL, &events,
                                (void **) &source)) >= 0) {
          // Process events.
          ...
        }
    
        // Render a frame.
        if (IsAnimating()) {
            DoFrame();
        }
      }
    }
    
  2. DoFrame yönteminde geçerli yüzey boyutlarını sorgulayın, bir çerçeve oluşturmak için SceneManager isteğinde bulunun ve görüntü arabelleklerini değiştirin.

    void NativeEngine::DoFrame() {
      ...
      // Query the current surface dimension.
      int width, height;
      eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &width);
      eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &height);
    
      // Handle dimension changes.
      SceneManager *mgr = SceneManager::GetInstance();
      if (width != mSurfWidth || height != mSurfHeight) {
        mSurfWidth = width;
        mSurfHeight = height;
        mgr->SetScreenSize(mSurfWidth, mSurfHeight);
        glViewport(0, 0, mSurfWidth, mSurfHeight);
      }
      ...
      // Render scenes and objects.
      mgr->DoFrame();
    
      // Swap buffers.
      if (EGL_FALSE == eglSwapBuffers(mEglDisplay, mEglSurface)) {
        HandleEglError(eglGetError());
      }
    }
    

Sahneleri ve nesneleri oluşturma

  1. Oyun döngüsü, oluşturulacak görünür sahnelerin ve nesnelerin hiyerarşisini işler. Sonsuz Tünel örneğinde SceneManager, aynı anda yalnızca bir sahnenin etkin olduğu birden fazla sahneyi izler. Bu örnekte mevcut sahne oluşturulur:

    void SceneManager::DoFrame() {
      if (mSceneToInstall) {
        InstallScene(mSceneToInstall);
        mSceneToInstall = NULL;
      }
    
      if (mHasGraphics && mCurScene) {
        mCurScene->DoFrame();
      }
    }
    
  2. Oyununuza bağlı olarak bir sahnede arka plan, metin, imgeler ve oyun nesneleri bulunabilir. Bu öğeleri oyununuza uygun sırayla oluşturun. Bu örnekte arka plan, metin ve widget'lar oluşturulur:

    void UiScene::DoFrame() {
      // clear screen
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glDisable(GL_DEPTH_TEST);
    
      RenderBackground();
    
      // Render the "Please Wait" sign and do nothing else
      if (mWaitScreen) {
        SceneManager *mgr = SceneManager::GetInstance();
        mTextRenderer->SetFontScale(WAIT_SIGN_SCALE);
        mTextRenderer->SetColor(1.0f, 1.0f, 1.0f);
        mTextRenderer->RenderText(S_PLEASE_WAIT, mgr->GetScreenAspect() * 0.5f,
                                  0.5f);
        glEnable(GL_DEPTH_TEST);
        return;
      }
    
      // Render all the widgets.
      for (int i = 0; i < mWidgetCount; ++i) {
        mWidgets[i]->Render(mTrivialShader, mTextRenderer, mShapeRenderer,
              (mFocusWidget < 0) ? UiWidget::FOCUS_NOT_APPLICABLE :
              (mFocusWidget == i) ? UiWidget::FOCUS_YES : UiWidget::FOCUS_NO,tf);
      }
      glEnable(GL_DEPTH_TEST);
    }
    

Kaynaklar

OpenGL ES ve Vulkan hakkında daha fazla bilgi için aşağıdakileri okuyun:

  • OpenGL ES - Android'deki görüntüler ve grafikler.

  • OpenGL ES: Android Kaynağında Genel Bakış.

  • Vulkan: NDK'yı kullanmaya başlama.

  • Vulkan: Android Kaynağı'nda Genel Bakış.

  • Android oyun döngülerini anlayın - Çerçevelerin hızını, arabellekleri sıralamayı, VSYNC geri çağırmalarını işlemeyi ve ileti dizilerini yönetmeyi öğrenin.