Organiza tus páginas con colecciones
Guarda y categoriza el contenido según tus preferencias.
Para dibujar objetos en tu juego, deberás configurar las variables de pantalla, superficie y contexto; establecer la renderización del bucle de juego; y dibujar cada escena y objeto.
Existen dos maneras de dibujar imágenes en la pantalla para un juego en C o C++: puedes usar OpenGL ES o Vulkan.
OpenGL ES forma parte de la especificación Open Graphics Library (OpenGL®) destinada a dispositivos móviles como los Android. Descubre cómo configurar OpenGL ES para tu juego en este tema.
Para renderizar tu juego, necesitarás una pantalla, una superficie, un contexto y una configuración. Agrega las siguientes variables de OpenGL ES al archivo de encabezado del motor de juego:
boolNativeEngine::InitDisplay(){if(mEglDisplay!=EGL_NO_DISPLAY){returntrue;}mEglDisplay=eglGetDisplay(EGL_DEFAULT_DISPLAY);if(EGL_FALSE==eglInitialize(mEglDisplay,0,0)){LOGE("NativeEngine: failed to init display, error %d",eglGetError());returnfalse;}returntrue;}
La superficie puede ser un búfer fuera de la pantalla (pbuffer) asignado por EGL o una ventana asignada por el SO Android. Inicializa esta superficie:
boolNativeEngine::InitSurface(){ASSERT(mEglDisplay!=EGL_NO_DISPLAY);if(mEglSurface!=EGL_NO_SURFACE){returntrue;}EGLintnumConfigs;constEGLintattribs[]={EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,// request OpenGL ES 2.0EGL_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());returnfalse;}returntrue;}
Inicializa el contexto de renderización. En este ejemplo, se crea un contexto de OpenGL ES 2.0:
boolNativeEngine::InitContext(){ASSERT(mEglDisplay!=EGL_NO_DISPLAY);if(mEglContext!=EGL_NO_CONTEXT){returntrue;}// OpenGL ES 2.0EGLintattribList[]={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());returnfalse;}returntrue;}
Establece la configuración de OpenGL ES antes de dibujar. Este ejemplo se ejecuta al principio de cada fotograma. Permite las pruebas de profundidad, establece el color claro en negro y borra los búferes de color y profundidad.
El bucle de juego renderiza un fotograma y continúa indefinidamente hasta que el usuario cierra el juego.
Entre fotogramas, el juego puede hacer lo siguiente:
Actualizar la lógica del juego y la interfaz de usuario
Renderizar un fotograma en la pantalla
Para renderizar un fotograma en la pantalla, se llama al método DoFrame de forma indefinida en el bucle de juego:
voidNativeEngine::GameLoop(){// Loop indefinitely.while(1){intevents;structandroid_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();}}}
En el método DoFrame, consulta las dimensiones actuales de la superficie, solicita a SceneManager que renderice un fotograma e intercambia los búferes de la pantalla.
voidNativeEngine::DoFrame(){...// Query the current surface dimension.intwidth,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());}}
Cómo renderizar escenas y objetos
El bucle de juego procesa una jerarquía de escenas y objetos visibles para su renderización.
En el ejemplo de Endless Tunnel, un SceneManager realiza un seguimiento de varias escenas, con solo una escena activa a la vez. En este ejemplo, se renderiza la escena actual:
Según el juego que elijas, una escena puede contener fondo, texto y objetos del juego. Renderiza los elementos en el orden adecuado para tu juego. En este ejemplo, se renderizan el fondo, el texto y los widgets:
voidUiScene::DoFrame(){// clear screenglClearColor(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 elseif(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(inti=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);}
Recursos
Consulta los siguientes recursos a fin de obtener más información sobre OpenGL ES y Vulkan:
Vulkan: Descripción general en la fuente de Android
Cómo comprender los bucles de juego de Android: Aprende a establecer el ritmo de los fotogramas, a formar colas de búferes, a controlar las devoluciones de llamada de VSYNC y a administrar subprocesos.
El contenido y las muestras de código que aparecen en esta página están sujetas a las licencias que se describen en la Licencia de Contenido. Java y OpenJDK son marcas registradas de Oracle o sus afiliados.
Última actualización: 2025-07-27 (UTC)
[[["Fácil de comprender","easyToUnderstand","thumb-up"],["Resolvió mi problema","solvedMyProblem","thumb-up"],["Otro","otherUp","thumb-up"]],[["Falta la información que necesito","missingTheInformationINeed","thumb-down"],["Muy complicado o demasiados pasos","tooComplicatedTooManySteps","thumb-down"],["Desactualizado","outOfDate","thumb-down"],["Problema de traducción","translationIssue","thumb-down"],["Problema con las muestras o los códigos","samplesCodeIssue","thumb-down"],["Otro","otherDown","thumb-down"]],["Última actualización: 2025-07-27 (UTC)"],[],[],null,["# Configure graphics with OpenGL ES\n\nTo draw objects and sprites in your game, you will need to configure the\ndisplay, surface and context variables, set up rendering in your game loop, and\ndraw each scene and object.\n\nThere are two ways to draw images to the screen for a C or C++ game, namely with\n[OpenGL ES](/develop/ui/views/graphics/opengl/about-opengl), or\n[Vulkan](/ndk/guides/graphics/getting-started).\n\n- [OpenGL ES](/develop/ui/views/graphics/opengl/about-opengl) is part of the [Open Graphics\n Library (OpenGL®)](https://www.khronos.org/opengles/) specification\n intended for mobile devices such as Android. Learn how to configure OpenGL ES\n for your game in this topic.\n\n- If you use Vulkan for your game, read the\n [Getting started with Vulkan](/ndk/guides/graphics/getting-started)\n guide.\n\n| **Note:** The code in this topic is based on the [Endless Tunnel](https://github.com/android/ndk-samples/tree/master/endless-tunnel) sample, where details may differ for your game. Understand and adapt these concepts for your specific use case.\n\nBefore you get started\n----------------------\n\nIf you haven't already done so,\n[set up a GameActivity object](/games/agdk/game-activity) in your\nAndroid project.\n\nSet up OpenGL ES variables\n--------------------------\n\n1. You will need a [display](/reference/android/opengl/EGLDisplay),\n [surface](/reference/android/opengl/EGLSurface),\n [context](/reference/android/opengl/EGLContext), and\n [config](/reference/android/opengl/EGLConfig) to render your game. Add the\n following OpenGL ES variables to your game engine's header file:\n\n class NativeEngine {\n //...\n private:\n EGLDisplay mEglDisplay;\n EGLSurface mEglSurface;\n EGLContext mEglContext;\n EGLConfig mEglConfig;\n\n bool mHasFocus, mIsVisible, mHasWindow;\n bool mHasGLObjects;\n bool mIsFirstFrame;\n\n int mSurfWidth, mSurfHeight;\n }\n\n2. In the constructor for your game engine, initialize the default values for\n the variables.\n\n NativeEngine::NativeEngine(struct android_app *app) {\n //...\n mEglDisplay = EGL_NO_DISPLAY;\n mEglSurface = EGL_NO_SURFACE;\n mEglContext = EGL_NO_CONTEXT;\n mEglConfig = 0;\n\n mHasFocus = mIsVisible = mHasWindow = false;\n mHasGLObjects = false;\n mIsFirstFrame = true;\n\n mSurfWidth = mSurfHeight = 0;\n }\n\n3. Initialize the display to render.\n\n bool NativeEngine::InitDisplay() {\n if (mEglDisplay != EGL_NO_DISPLAY) {\n return true;\n }\n\n mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);\n if (EGL_FALSE == eglInitialize(mEglDisplay, 0, 0)) {\n LOGE(\"NativeEngine: failed to init display, error %d\", eglGetError());\n return false;\n }\n return true;\n }\n\n4. The surface can be an off-screen buffer (pbuffer) allocated by EGL, or a\n window allocated by the Android OS. Initialize this surface:\n\n bool NativeEngine::InitSurface() {\n ASSERT(mEglDisplay != EGL_NO_DISPLAY);\n if (mEglSurface != EGL_NO_SURFACE) {\n return true;\n }\n\n EGLint numConfigs;\n const EGLint attribs[] = {\n EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // request OpenGL ES 2.0\n EGL_SURFACE_TYPE, EGL_WINDOW_BIT,\n EGL_BLUE_SIZE, 8,\n EGL_GREEN_SIZE, 8,\n EGL_RED_SIZE, 8,\n EGL_DEPTH_SIZE, 16,\n EGL_NONE\n };\n\n // Pick the first EGLConfig that matches.\n eglChooseConfig(mEglDisplay, attribs, &mEglConfig, 1, &numConfigs);\n mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mApp-\u003ewindow,\n NULL);\n if (mEglSurface == EGL_NO_SURFACE) {\n LOGE(\"Failed to create EGL surface, EGL error %d\", eglGetError());\n return false;\n }\n return true;\n }\n\n5. Initialize the rendering context. This example creates an\n [OpenGL ES 2.0](/reference/android/opengl/GLES20) context:\n\n bool NativeEngine::InitContext() {\n ASSERT(mEglDisplay != EGL_NO_DISPLAY);\n if (mEglContext != EGL_NO_CONTEXT) {\n return true;\n }\n\n // OpenGL ES 2.0\n EGLint attribList[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };\n mEglContext = eglCreateContext(mEglDisplay, mEglConfig, NULL, attribList);\n if (mEglContext == EGL_NO_CONTEXT) {\n LOGE(\"Failed to create EGL context, EGL error %d\", eglGetError());\n return false;\n }\n return true;\n }\n\n6. Configure your OpenGL ES settings before drawing. This example is executed at\n the beginning of every frame. It enables depth testing, sets the clear color to\n black, and clears the color and depth buffers.\n\n void NativeEngine::ConfigureOpenGL() {\n glClearColor(0.0f, 0.0f, 0.0f, 1.0f);\n glEnable(GL_DEPTH_TEST);\n glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);\n }\n\nRender with the game loop\n-------------------------\n\n1. The game loop renders a frame and repeats indefinitely until the user quits.\n Between frames, your game may:\n\n - [Process events](/games/agdk/game-activity/get-started#handle-events) such as\n input, [audio output](/games/sdk/oboe), and networking events.\n\n - Update the game logic and user interface.\n\n - Render a frame to the display.\n\n To render a frame to the display, the `DoFrame` method is called\n indefinitely in the game loop: \n\n void NativeEngine::GameLoop() {\n // Loop indefinitely.\n while (1) {\n int events;\n struct android_poll_source* source;\n\n // If not animating, block until we get an event.\n while ((ALooper_pollAll(IsAnimating() ? 0 : -1, NULL, &events,\n (void **) &source)) \u003e= 0) {\n // Process events.\n ...\n }\n\n // Render a frame.\n if (IsAnimating()) {\n DoFrame();\n }\n }\n }\n\n2. In the `DoFrame` method, query the current surface dimensions, request\n `SceneManager` to render a frame, and swap the display buffers.\n\n void NativeEngine::DoFrame() {\n ...\n // Query the current surface dimension.\n int width, height;\n eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &width);\n eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &height);\n\n // Handle dimension changes.\n SceneManager *mgr = SceneManager::GetInstance();\n if (width != mSurfWidth || height != mSurfHeight) {\n mSurfWidth = width;\n mSurfHeight = height;\n mgr-\u003eSetScreenSize(mSurfWidth, mSurfHeight);\n glViewport(0, 0, mSurfWidth, mSurfHeight);\n }\n ...\n // Render scenes and objects.\n mgr-\u003eDoFrame();\n\n // Swap buffers.\n if (EGL_FALSE == eglSwapBuffers(mEglDisplay, mEglSurface)) {\n HandleEglError(eglGetError());\n }\n }\n\nRender scenes and objects\n-------------------------\n\n1. The game loop processes a hierarchy of visible scenes and objects to render.\n In the Endless Tunnel example, a `SceneManager` keeps track of multiple scenes,\n with only one scene active at a time. In this example, the current scene is\n rendered:\n\n void SceneManager::DoFrame() {\n if (mSceneToInstall) {\n InstallScene(mSceneToInstall);\n mSceneToInstall = NULL;\n }\n\n if (mHasGraphics && mCurScene) {\n mCurScene-\u003eDoFrame();\n }\n }\n\n2. Depending on your game, a scene may contain background, text, sprites and\n game objects. Render them in the order suitable for your game. This example\n renders the background, text, and widgets:\n\n void UiScene::DoFrame() {\n // clear screen\n glClearColor(0.0f, 0.0f, 0.0f, 1.0f);\n glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n glDisable(GL_DEPTH_TEST);\n\n RenderBackground();\n\n // Render the \"Please Wait\" sign and do nothing else\n if (mWaitScreen) {\n SceneManager *mgr = SceneManager::GetInstance();\n mTextRenderer-\u003eSetFontScale(WAIT_SIGN_SCALE);\n mTextRenderer-\u003eSetColor(1.0f, 1.0f, 1.0f);\n mTextRenderer-\u003eRenderText(S_PLEASE_WAIT, mgr-\u003eGetScreenAspect() * 0.5f,\n 0.5f);\n glEnable(GL_DEPTH_TEST);\n return;\n }\n\n // Render all the widgets.\n for (int i = 0; i \u003c mWidgetCount; ++i) {\n mWidgets[i]-\u003eRender(mTrivialShader, mTextRenderer, mShapeRenderer,\n (mFocusWidget \u003c 0) ? UiWidget::FOCUS_NOT_APPLICABLE :\n (mFocusWidget == i) ? UiWidget::FOCUS_YES : UiWidget::FOCUS_NO,tf);\n }\n glEnable(GL_DEPTH_TEST);\n }\n\nResources\n---------\n\nRead the following for more information about OpenGL ES and Vulkan:\n\n- [OpenGL ES](/develop/ui/views/graphics/opengl/about-opengl) - Images and graphics in Android.\n\n- [OpenGL ES](https://source.android.com/devices/graphics/arch-egl-opengl) -\n Overview in Android Source.\n\n- [Vulkan](/ndk/guides/graphics/getting-started) - Getting started in NDK.\n\n- [Vulkan](https://source.android.com/devices/graphics/arch-vulkan) - Overview\n in Android Source.\n\n- [Understand Android game loops](/games/develop/gameloops) - learn to pace\n frames, queue buffers, handle VSYNC callbacks, and manage threads."]]