ในการวาดสิ่งต่างๆ และสไปรท์ในเกม คุณจะต้องกำหนดค่า ตัวแปรดิสเพลย์ แพลตฟอร์ม และบริบท ตั้งค่าการแสดงผลใน 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 และจัดการชุดข้อความ