Configurar el proyecto de juego
Nota:
Este tema forma parte de la serie de tutoriales Crear un juego sencillo para la Plataforma universal de Windows (UWP) con DirectX. El tema de ese vínculo establece el contexto de la serie.
El primer paso para desarrollar el juego es crear un proyecto en Microsoft Visual Studio. Después de configurar un proyecto específicamente para el desarrollo de juegos, podría volver a usarlo más adelante como un tipo de plantilla.
Objetivos
- Cree un proyecto en Visual Studio mediante una plantilla de proyecto.
- Comprenda el punto de entrada y la inicialización del juego examinando el archivo de origen de la clase App .
- Mira el bucle del juego.
- Revise el archivo package.appxmanifest del proyecto.
Creación de un proyecto en Visual Studio
Nota:
Para obtener más información sobre cómo instalar Visual Studio para la implementación de C++/WinRT incluida la instalación y el uso de la Extensión de Visual Studio (VSIX) para C++/WinRT y el paquete NuGet (que juntos proporcionan la plantilla de proyecto y compatibilidad de la compilación), consulte Compatibilidad de Visual Studio para C++/WinRT.
En primer lugar, instale (o actualice a) la versión más reciente de la extensión de Visual Studio de C++/WinRT (VSIX); consulte la nota anterior. A continuación, en Visual Studio, cree un nuevo proyecto basado en la plantilla de proyecto Core App (C++/WinRT). Elija como destino la versión más reciente disponible de manera general (es decir, no en versión preliminar) de Windows SDK.
Revise la clase App para comprender IFrameworkViewSource e IFrameworkView
En el proyecto core App, abra el archivo App.cpp
de código fuente . En hay la implementación de la clase App , que representa la aplicación y su ciclo de vida. En este caso, por supuesto, sabemos que la aplicación es un juego. Pero nos referiremos a ella como una aplicación para hablar más generalmente sobre cómo se inicializa una aplicación de Plataforma universal de Windows (UWP).
La función wWinMain
La función wWinMain es el punto de entrada de la aplicación. Este es el aspecto de wWinMain (de App.cpp
).
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
CoreApplication::Run(winrt::make<App>());
}
Realizamos una instancia de la clase App (esta es la única instancia de La aplicación que se crea) y la pasamos al método static CoreApplication.Run . Tenga en cuenta que CoreApplication.Run espera una interfaz IFrameworkViewSource. Por lo tanto, la clase App debe implementar esa interfaz.
En las dos secciones siguientes de este tema se describen las interfaces IFrameworkViewSource e IFrameworkView. Esas interfaces (así como CoreApplication.Run) representan una manera de que la aplicación proporcione Windows con un proveedor de vistas. Windows usa ese proveedor de vistas para conectar la aplicación con el shell de Windows para poder controlar los eventos del ciclo de vida de la aplicación.
Interfaz IFrameworkViewSource
La clase App implementa realmente IFrameworkViewSource, como puede ver en la lista siguiente.
struct App : winrt::implements<App, IFrameworkViewSource, IFrameworkView>
{
...
IFrameworkView CreateView()
{
return *this;
}
...
}
Un objeto que implementa IFrameworkViewSource es un objeto de fábrica del proveedor de vistas. El trabajo de ese objeto es fabricar y devolver un objeto de proveedor de vistas.
IFrameworkViewSource tiene el método único IFrameworkViewSource::CreateView. Windows llama a esa función en el objeto que se pasa a CoreApplication.Run. Como puede ver anteriormente, la implementación App::CreateView de ese método devuelve *this
. En otras palabras, el objeto App se devuelve a sí mismo. Puesto que IFrameworkViewSource::CreateView tiene un tipo de valor devuelto de IFrameworkView, sigue que la clase App también debe implementar esa interfaz. Y puede ver que lo hace en la lista anterior.
Interfaz IFrameworkView
Un objeto que implementa IFrameworkView es un objeto de proveedor de vistas. Y ahora hemos proporcionado Windows con ese proveedor de vistas. Es el mismo objeto App que creamos en wWinMain. Por lo tanto, la clase App actúa como generador de proveedores de vistas y como proveedor de vistas.
Ahora Windows puede llamar a las implementaciones de la clase App de los métodos de IFrameworkView. En las implementaciones de esos métodos, la aplicación tiene la oportunidad de realizar tareas como la inicialización, comenzar a cargar los recursos que necesitará, conectar los controladores de eventos adecuados y recibir el CoreWindow que la aplicación usará para mostrar su salida.
Se llama a las implementaciones de los métodos de IFrameworkView en el orden que se muestra a continuación.
- Initialize
- SetWindow
- Cargar
- Se genera el evento CoreApplicationView::Activated . Por lo tanto, si ha registrado (opcionalmente) para controlar ese evento, se llama al controlador OnActivated en este momento.
- Ejecutar
- Uninitialize
Y este es el esqueleto de la clase App (en App.cpp
), que muestra las firmas de esos métodos.
struct App : winrt::implements<App, IFrameworkViewSource, IFrameworkView>
{
...
void Initialize(Windows::ApplicationModel::Core::CoreApplicationView const& applicationView) { ... }
void SetWindow(Windows::UI::Core::CoreWindow const& window) { ... }
void Load(winrt::hstring const& entryPoint) { ... }
void OnActivated(
Windows::ApplicationModel::Core::CoreApplicationView const& applicationView,
Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) { ... }
void Run() { ... }
void Uninitialize() { ... }
...
}
Se trata de una introducción a IFrameworkView. Vamos a profundizar mucho más sobre estos métodos y cómo implementarlos, en Definir el marco de la aplicación para UWP del juego.
Ordenar el proyecto
El proyecto core App que creó a partir de la plantilla de proyecto contiene funcionalidades que debemos ordenar en este momento. Después, podemos usar el proyecto para volver a crear el juego de galería de disparos (Simple3DGameDX). Realice los siguientes cambios en la clase App en App.cpp
.
- Elimine sus miembros de datos.
- Eliminar OnPointerPressed, OnPointerMoved y AddVisual
- Elimine el código de SetWindow.
El proyecto se compilará y ejecutará, pero solo mostrará un color sólido en el área de cliente.
Bucle del juego
Para obtener una idea del aspecto de un bucle de juego, mire en el código fuente del juego de ejemplo Simple3DGameDX que descargó.
La clase App tiene un miembro de datos, denominado m_main, de tipo GameMain. Y ese miembro se usa en App::Run como este.
void Run()
{
m_main->Run();
}
Puedes encontrar GameMain::Run en GameMain.cpp
. Es el bucle principal del juego, y este es un esquema muy aproximado de que muestra las características más importantes.
void GameMain::Run()
{
while (!m_windowClosed)
{
if (m_visible)
{
CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
Update();
m_renderer->Render();
m_deviceResources->Present();
}
else
{
CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
}
}
}
Y esta es una breve descripción de lo que hace este bucle principal del juego.
Si la ventana del juego no está cerrada, envíe todos los eventos, actualice el temporizador y, a continuación, represente y presente los resultados de la canalización de gráficos. Hay mucho más que decir sobre esas preocupaciones, y lo haremos en los temas Definir el marco de la aplicación para UWP del juego, Marco de representación I: Introducción a la representación y Marco de representación II: Representación del juego. Pero esta es la estructura de código básica de un juego DirectX para UWP.
Revise y actualice el archivo package.appxmanifest.
El archivo Package.appxmanifest contiene metadatos sobre un proyecto de UWP. Esos metadatos se usan para empaquetar e iniciar el juego, y para su envío a Microsoft Store. El archivo también contiene información importante que usa el sistema del jugador para proporcionar acceso a los recursos del sistema que el juego necesita para ejecutarse.
Inicie el diseñador de manifiestos haciendo doble clic en el archivo Package.appxmanifest en Explorador de soluciones.
Para obtener más información sobre el archivo package.appxmanifest y el empaquetado, consulta Diseñador de manifiestos. Por ahora, eche un vistazo a la pestaña Funcionalidades y examine las opciones proporcionadas.
Si no seleccionas las funcionalidades que usa tu juego, como el acceso a Internet para el panel global de puntuación alta, no podrás acceder a los recursos ni características correspondientes. Al crear un nuevo juego, asegúrate de seleccionar las funcionalidades necesarias para las API a las que llama el juego.
Ahora echemos un vistazo al resto de los archivos que vienen con el juego de ejemplo Simple3DGameDX .
Revisión de otras bibliotecas importantes y archivos de código fuente
Si tienes intención de crear un tipo de plantilla de proyecto de juego para ti mismo, de modo que puedas volver a usarlo como punto de partida para proyectos futuros, entonces querrás copiar GameMain.h
y salir del proyecto Simple3DGameDX que descargó y agregarlos al nuevo proyecto GameMain.cpp
de Core App. Estudie esos archivos, aprenda lo que hacen y quite todo lo que sea específico de Simple3DGameDX. También convierta en comentario todo lo que dependa del código que aún no haya copiado. Solo a modo de ejemplo, GameMain.h
depende de GameRenderer.h
. Podrás quitar la marca de comentario a medida que copias más archivos de Simple3DGameDX.
Esta es una breve encuesta de algunos de los archivos de Simple3DGameDX que encontrarás útiles para incluir en tu plantilla, si estás haciendo uno. En cualquier caso, son igualmente importantes para comprender cómo funciona Simple3DGameDX .
Archivo de origen | Carpeta de archivos | Descripción |
---|---|---|
DeviceResources.h/.cpp | Sectores públicos | Define la clase DeviceResources, que controla todos los recursos del dispositivo DirectX. También define la interfaz IDeviceNotify , que se usa para notificar a la aplicación que el dispositivo del adaptador de gráficos se ha perdido o se ha vuelto a crear. |
DirectXSample.h | Sectores públicos | Implementa funciones auxiliares como ConvertDipsToPixels. ConvertDipsToPixels convierte una longitud en píxeles independientes del dispositivo (DIP) a una longitud en píxeles físicos. |
GameTimer.h/.cpp | Sectores públicos | Define un temporizador de alta resolución útil para juegos o aplicaciones de representación interactiva. |
GameRenderer.h/.cpp | Representación | Define la clase GameRenderer , que implementa una canalización de representación básica. |
GameHud.h/.cpp | Representación | Define una clase para representar una pantalla de cabeza (HUD) para el juego, usando Direct2D y DirectWrite. |
VertexShader.hlsl y VertexShaderFlat.hlsl | Sombreadores | Contiene el código de lenguaje de sombreador de alto nivel (HLSL) para sombreadores de vértices básicos. |
PixelShader.hlsl y PixelShaderFlat.hlsl | Sombreadores | Contiene el código de lenguaje de sombreador de alto nivel (HLSL) para sombreadores de píxeles básicos. |
ConstantBuffers.hlsli | Sombreadores | Contiene definiciones de estructura de datos para búferes de constantes y estructuras de sombreador usadas para pasar matrices de proyección de vista de modelo (MVP) y datos por vértice al sombreador de vértices. |
pch.h/.cpp | N/D | Contiene C++/WinRT, Windows y DirectX comunes. |
Pasos siguientes
En este punto, hemos mostrado cómo crear un nuevo proyecto para UWP para un juego DirectX, hemos visto algunas de las piezas en él y hemos empezado a pensar en cómo convertir ese proyecto en una especie de plantilla que se puede volver a usar para juegos. También hemos visto algunas de las piezas importantes del juego de ejemplo Simple3DGameDX .
La siguiente sección es Definir el marco de la aplicación para UWP del juego. Allí veremos más detenidamente cómo funciona Simple3DGameDX .