لرسم كائنات ورسومات متحركة في لعبتك، ستحتاج إلى تهيئة عرض متغيّرات العرض وإظهارها والسياق، وإعداد العرض في حلقة الألعاب، لرسم كل مشهد وكائن.
هناك طريقتان لرسم صور على الشاشة للعبة C أو C++، وبالتحديد OpenGL ES أو Vulkan
OpenGL ES هو جزء من Open Graphics مواصفات المكتبة (OpenGL® ) مخصص للأجهزة المحمولة مثل Android. تعرَّف على كيفية ضبط OpenGL ES. للعبتك في هذا الموضوع.
إذا كنت تستخدم Vulkan في لعبتك، اقرأ بدء استخدام Vulkan الدليل.
قبل البدء
إذا لم تكن قد قمت بذلك بالفعل، إعداد كائن GameActivity في مشروع Android.
إعداد متغيّرات OpenGL ES
ستحتاج إلى شاشة السطح، السياق، الضبط لعرض لعبتك. إضافة متغيّرات OpenGL ES التالية في ملف العنوان الخاص بمحرّك اللعبة:
class NativeEngine { //... private: EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLContext mEglContext; EGLConfig mEglConfig; bool mHasFocus, mIsVisible, mHasWindow; bool mHasGLObjects; bool mIsFirstFrame; int mSurfWidth, mSurfHeight; }
في الدالة الإنشائية لمحرك لعبتك، قم بتهيئة القيم الافتراضية المتغيرات.
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; }
يمكن أن يكون السطح مخزنًا مؤقتًا خارج الشاشة (pتخزين مؤقت) مخصص بواسطة 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); }
العرض مع حلقة الألعاب
تعرض حلقة الألعاب إطارًا وتتكرر إلى أجل غير مسمى إلى أن يستقيل المستخدم. بين اللقطات، يمكن أن:
معالجة الأحداث مثل والإدخال وإخراج الصوت وأحداث الاتصال بالشبكات.
عدِّل منطق اللعبة وواجهة المستخدم.
اعرض إطارًا على الشاشة.
لعرض إطار على الشاشة، تُسمى الطريقة
DoFrame
. إلى أجل غير مسمى في حلقة الألعاب: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()); } }
عرض مشاهد وكائنات
وتعالج حلقة الألعاب تسلسلاً هرميًا للمشاهد والعناصر المرئية التي يمكن عرضها. في مثال 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.
التعرّف على تكرارات ألعاب Android: تعلّم إيقاع اللعبة الإطارات، والمستودعات الاحتياطية في قائمة الانتظار، والتعامل مع استدعاءات VSYNC، وإدارة سلاسل المحادثات.