ในการวาดสิ่งต่างๆ และสไปรท์ในเกม คุณจะต้องกำหนดค่า ตัวแปรดิสเพลย์ แพลตฟอร์ม และบริบท ตั้งค่าการแสดงผลใน Game Loop ให้วาดฉากและวัตถุแต่ละชิ้น
การวาดรูปภาพบนหน้าจอสำหรับเกม C หรือ C++ ทำได้ 2 วิธีดังนี้ OpenGL ES หรือ Vulkan
- OpenGL ES เป็นส่วนหนึ่งของ Open Graphics ข้อกําหนดของ Library (OpenGL®) มีไว้สำหรับอุปกรณ์เคลื่อนที่ เช่น Android ดูวิธีกำหนดค่า OpenGL ES สำหรับเกมของคุณในหัวข้อนี้ 
- หากคุณใช้ Vulkan สำหรับเกมของคุณ โปรดอ่าน การเริ่มต้นใช้งาน Vulkan 
ก่อนเริ่มต้นใช้งาน
หากคุณยังไม่ได้อ่าน ตั้งค่าออบเจ็กต์ GameActivity ใน โปรเจ็กต์ Android
ตั้งค่าตัวแปร OpenGL ES
- คุณจะต้องมีจอแสดงผล surface บริบท และ กำหนดค่าเพื่อแสดงผลเกม เพิ่ม ตัวแปร OpenGL ES ต่อไปนี้ไปยังไฟล์ส่วนหัวของเกมเอนจินของคุณ - class NativeEngine { //... private: EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLContext mEglContext; EGLConfig mEglConfig; bool mHasFocus, mIsVisible, mHasWindow; bool mHasGLObjects; bool mIsFirstFrame; int mSurfWidth, mSurfHeight; }
- ในตัวสร้างสำหรับ Game Engine ให้กำหนดค่าเริ่มต้นสำหรับ ตัวแปร - 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; }
- เริ่มต้นการแสดงผล - 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; }
- พื้นผิวอาจเป็นบัฟเฟอร์นอกหน้าจอ (บัฟเฟอร์) ที่จัดสรรโดย EGL หรือ ที่ระบบปฏิบัติการ Android จัดสรรให้ เริ่มต้นแพลตฟอร์มนี้: - 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; }
- เริ่มต้นบริบทการแสดงผล ตัวอย่างนี้สร้างองค์ประกอบ บริบท OpenGL ES 2.0: - 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; }
- กำหนดการตั้งค่า OpenGL ES ก่อนวาด ตัวอย่างนี้ดำเนินการที่ จุดเริ่มต้นของทุกเฟรม โดยเปิดใช้การทดสอบความลึก กำหนดสีที่ชัดเจนเป็น สีดำ ซึ่งจะล้างบัฟเฟอร์สีและความลึก - 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); }
แสดงผลด้วย Game Loop
- Game Loop จะแสดงเฟรมหนึ่งและแสดงซ้ำไปเรื่อยๆ จนกว่าผู้ใช้จะเลิกเล่น ระหว่างเฟรม เกมของคุณอาจมีลักษณะดังนี้ - ประมวลผลกิจกรรม เช่น อินพุต เอาต์พุตเสียง และเหตุการณ์เกี่ยวกับเครือข่าย 
- อัปเดตตรรกะของเกมและอินเทอร์เฟซผู้ใช้ 
- แสดงผลเฟรมไปยังจอแสดงผล 
 - หากต้องการแสดงผลเฟรมไปยังจอแสดงผล จะเรียกเมธอด - DoFrameใน Game Loop อยู่เสมอ:- 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(); } } }
- ในเมธอด - DoFrameให้ค้นหามิติข้อมูลพื้นผิวปัจจุบัน คำขอ- SceneManagerเพื่อแสดงผลเฟรมและสลับบัฟเฟอร์การแสดงผล- 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()); } }
แสดงภาพฉากและวัตถุ
- Game Loop จะประมวลผลลำดับชั้นของฉากและวัตถุที่มองเห็นได้เพื่อแสดงผล ในตัวอย่าง Endless Tunnel - SceneManagerติดตามหลายฉาก โดยเปิดใช้เพียงฉากเดียวในแต่ละครั้ง ในตัวอย่างนี้ ฉากปัจจุบันคือ แสดงผล:- void SceneManager::DoFrame() { if (mSceneToInstall) { InstallScene(mSceneToInstall); mSceneToInstall = NULL; } if (mHasGraphics && mCurScene) { mCurScene->DoFrame(); } }
- ฉากอาจมีพื้นหลัง ข้อความ สไปรท์ และ ออบเจ็กต์ในเกม แสดงผลตามลำดับที่เหมาะกับเกมของคุณ ตัวอย่างนี้ แสดงผลพื้นหลัง ข้อความ และวิดเจ็ต: - 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); }
แหล่งข้อมูล
อ่านข้อมูลต่อไปนี้เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับ OpenGL ES และ Vulkan
- OpenGL ES - รูปภาพและกราฟิกใน Android 
- OpenGL ES - ภาพรวมในแหล่งที่มา Android 
- Vulkan - เริ่มต้นใช้งานใน NDK 
- Vulkan - ภาพรวม ในแหล่งที่มา Android 
- ทำความเข้าใจ Game Loop ของ Android - เรียนรู้วิธีก้าวเดิน เฟรม บัฟเฟอร์คิว จัดการ Callback ของ VSYNC และจัดการชุดข้อความ 
