Configurar o projeto de jogo

Observação

Este tópico faz parte da série de tutoriais Criar um jogo simples da Plataforma Universal do Windows (UWP) com DirectX. O tópico nesse link define o contexto da série.

A primeira etapa no desenvolvimento do seu jogo é criar um projeto no Microsoft Visual Studio. Depois de configurar um projeto especificamente para o desenvolvimento de jogos, você poderá reutilizá-lo posteriormente como uma espécie de modelo.

Objetivos

  • Crie um novo projeto no Visual Studio usando um modelo de projeto.
  • Entenda o ponto de entrada e a inicialização do jogo examinando o arquivo de origem da classe App .
  • Olhe para o loop do jogo.
  • Examine o arquivo package.appxmanifest do projeto.

Criar um novo projeto no Visual Studio

Observação

Para saber mais sobre como configurar o Visual Studio para desenvolvimento em C++/WinRT, incluindo instalação e uso da VSIX (Extensão do Visual Studio) para C++/WinRT e o pacote NuGet (que juntos fornecem um modelo de projeto e suporte ao build), confira Suporte do Visual Studio para C++/WinRT.

Primeiro, instale (ou atualize) a versão mais recente do Vsix (Extensão do Visual Studio) do C++/WinRT; Veja a nota acima. Em seguida, no Visual Studio, crie um novo projeto com base no modelo de projeto do Aplicativo Principal (C++/WinRT). Direcione a versão mais recente em disponibilidade geral (ou seja, que não esteja em versão prévia) do SDK do Windows.

Examine a classe App para entender IFrameworkViewSource e IFrameworkView

No projeto do aplicativo principal, abra o arquivo App.cppde código-fonte. Lá está a implementação da classe App , que representa o aplicativo e seu ciclo de vida. Nesse caso, é claro, sabemos que o aplicativo é um jogo. Mas vamos nos referir a ele como um aplicativo para falar de forma mais geral sobre como um aplicativo UWP (Plataforma Universal do Windows) é inicializado.

A função wWinMain

A função wWinMain é o ponto de entrada para o aplicativo. Aqui está a aparência do wWinMain (de App.cpp).

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    CoreApplication::Run(winrt::make<App>());
}

Criamos uma instância da classe App (essa é a única instância de App criada) e a passamos para o método estático CoreApplication.Run. Observe que CoreApplication.Run espera uma interface IFrameworkViewSource. Portanto, a classe App precisa implementar essa interface.

As próximas duas seções deste tópico descrevem as interfaces IFrameworkViewSource e IFrameworkView. Essas interfaces (assim como CoreApplication.Run) representam uma maneira de seu aplicativo fornecer ao Windows um provedor de exibição. O Windows usa esse provedor de exibição para conectar seu aplicativo ao shell do Windows para que você possa lidar com eventos de ciclo de vida do aplicativo.

A interface IFrameworkViewSource

A classe App realmente implementa IFrameworkViewSource, como você pode ver na listagem abaixo.

struct App : winrt::implements<App, IFrameworkViewSource, IFrameworkView>
{
    ...
    IFrameworkView CreateView()
    {
        return *this;
    }
    ...
}

Um objeto que implementa IFrameworkViewSource é um objeto de fábrica do provedor de exibição. O trabalho desse objeto é fabricar e retornar um objeto de provedor de exibição.

IFrameworkViewSource tem o único método IFrameworkViewSource::CreateView. Windows chama essa função no objeto que você passa para CoreApplication.Run. Como você pode ver acima, a implementação App::CreateView desse método retorna *this. Em outras palavras, o objeto App retorna a si mesmo. Como IFrameworkViewSource::CreateView tem um tipo de valor retornado de IFrameworkView, segue-se que a classe App também precisa implementar essa interface. E você pode ver que isso acontece na lista acima.

A interface IFrameworkView

Um objeto que implementa IFrameworkView é um objeto de provedor de exibição. E agora fornecemos ao Windows esse provedor de exibição. É o mesmo objeto App que criamos no wWinMain. Portanto, a classe App serve como fábrica de provedor de exibição e provedor de exibição.

Agora o Windows pode chamar as implementações da classe App dos métodos de IFrameworkView. Nas implementações desses métodos, seu aplicativo tem a chance de executar tarefas como inicialização, começar a carregar os recursos necessários, conectar os manipuladores de eventos apropriados e receber o CoreWindow que seu aplicativo usará para exibir sua saída.

Suas implementações dos métodos de IFrameworkView são chamadas na ordem mostrada abaixo.

E aqui está o esqueleto da classe App (in App.cpp), mostrando as assinaturas desses 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() { ... }
    ...
}

Esta foi apenas uma introdução ao IFrameworkView. Entramos em muito mais detalhes sobre esses métodos e como implementá-los em Definir a estrutura do aplicativo UWP do jogo.

Arrume o projeto

O projeto de Aplicativo Principal que você criou a partir do modelo de projeto contém funcionalidades que devemos organizar neste momento. Depois disso, podemos usar o projeto para recriar o jogo da galeria de tiro (Simple3DGameDX). Faça as seguintes alterações na classe App em App.cpp.

  • Exclua seus membros de dados.
  • Excluir OnPointerPressed, OnPointerMoved e AddVisual
  • Exclua o código de SetWindow.

O projeto será compilado e executado, mas exibirá apenas uma cor sólida na área do cliente.

O loop do jogo

Para ter uma ideia de como é um loop de jogo, procure no código-fonte do jogo de exemplo Simple3DGameDX que você baixou.

A classe App tem um membro de dados, chamado m_main, do tipo GameMain. E esse membro é usado em App::Run assim.

void Run()
{
    m_main->Run();
}

Você pode encontrar GameMain::Run em GameMain.cpp. É o loop principal do jogo, e aqui está um esboço muito aproximado dele mostrando os recursos mais 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);
        }
    }
}

E aqui está uma breve descrição do que esse loop principal do jogo faz.

Se a janela do jogo não estiver fechada, despache todos os eventos, atualize o temporizador e, em seguida, renderize e apresente os resultados do pipeline de gráficos. Há muito mais a dizer sobre essas preocupações, e faremos isso nos tópicos Definir a estrutura do aplicativo UWP do jogo, Estrutura de renderização I: introdução à renderização e Estrutura de renderização II: renderização do jogo. Mas essa é a estrutura básica de código de um jogo UWP DirectX.

Revise e atualize o arquivo package.appxmanifest

O arquivo Package.appxmanifest contém metadados sobre um projeto UWP. Esses metadados são usados para empacotar e iniciar seu jogo e para envio para a Microsoft Store. O arquivo também contém informações importantes que o sistema do jogador usa para fornecer acesso aos recursos do sistema que o jogo precisa para ser executado.

Inicie o designer de manifesto clicando duas vezes no arquivo Package.appxmanifest no Gerenciador de Soluções.

Captura de tela do editor de manifesto package.appx.

Para obter mais informações sobre o arquivo package.appxmanifest e o empacotamento, consulte Designer de Manifesto. Por enquanto, dê uma olhada na guia Recursos e veja as opções fornecidas.

Captura de tela com os recursos padrão de um aplicativo Direct3D.

Se você não selecionar os recursos que seu jogo usa, como acesso à Internet para o quadro de recordes globais, não poderá acessar os recursos nem recursos correspondentes. Ao criar um novo jogo, certifique-se de selecionar todos os recursos necessários para as APIs que seu jogo chama.

Agora vamos examinar o restante dos arquivos que acompanham o jogo de exemplo Simple3DGameDX .

Revise outras bibliotecas importantes e arquivos de código-fonte

Se você pretende criar um tipo de modelo de projeto de jogo para si mesmo, para que possa reutilizá-lo como ponto de partida para projetos futuros, convém copiar GameMain.h e retirar do projeto Simple3DGameDX que você baixou e adicioná-los ao seu novo projeto GameMain.cpp do aplicativo principal. Estude esses arquivos, aprenda o que eles fazem e remova qualquer coisa específica do Simple3DGameDX. Comente também qualquer coisa que dependa do código que você ainda não copiou. Apenas a título de exemplo, GameMain.h depende de GameRenderer.h. Você poderá descomentar à medida que copia mais arquivos do Simple3DGameDX.

Aqui está uma breve pesquisa de alguns dos arquivos no Simple3DGameDX que você achará útil incluir em seu modelo, se estiver fazendo um. De qualquer forma, eles são igualmente importantes para entender como o próprio Simple3DGameDX funciona.

Arquivo de origem Pasta de arquivo Descrição
DeviceResources.h/.cpp Utilidades Define a classe DeviceResources, que controla todos os recursos do dispositivo DirectX. Também define a interface IDeviceNotify , usada para notificar seu aplicativo de que o dispositivo do adaptador gráfico foi perdido ou recriado.
DirectXSample.h Utilidades Implementa funções auxiliares, como ConvertDipsToPixels. ConvertDipsToPixels converte um comprimento em DIPs (pixels independentes de dispositivo) em um comprimento em pixels físicos.
GameTimer.h/.cpp Utilidades Define um temporizador de alta resolução útil para jogos ou aplicativos de renderização interativa.
GameRenderer.h/.cpp Tempo de Define a classe GameRenderer , que implementa um pipeline de renderização básico.
GameHud.h/.cpp Tempo de Define uma classe para renderizar um HUD (exibição de alerta) para o jogo, usando Direct2D e DirectWrite.
VertexShader.hlsl e VertexShaderFlat.hlsl Sombreadores Contém o código HLSL (linguagem de sombreador de alto nível) para sombreadores de vértice básicos.
PixelShader.hlsl e PixelShaderFlat.hlsl Sombreadores Contém o código HLSL (linguagem de sombreador de alto nível) para sombreadores de pixel básicos.
ConstantBuffers.hlsli Sombreadores Contém definições de estrutura de dados para buffers constantes e estruturas de sombreador usadas para passar matrizes MVP (model-view-projection
pch.h/.cpp N/D Contém inclui C++/WinRT, Windows e DirectX comuns.

Próximas etapas

Neste ponto, mostramos como criar um novo projeto UWP para um jogo DirectX, examinamos algumas das peças nele e começamos a pensar em como transformar esse projeto em um tipo de modelo reutilizável para jogos. Também examinamos algumas das peças importantes do jogo de exemplo Simple3DGameDX .

A próxima seção é Definir a estrutura do aplicativo UWP do jogo. Lá, veremos mais de perto como o Simple3DGameDX funciona.