Compartilhar via


Amostra de fundamentos do Marble Maze

Esse tópico descreve as características fundamentais do projeto Marble Maze, como, por exemplo, como ele usa o Visual C++ no ambiente do Windows Runtime, como ele é criado e estruturado e como ele é desenvolvido. O tópico também descreve várias das convenções que são usadas no código.

Observação

A amostra de código que corresponde a este documento pode ser encontrada na amostra do jogo Marble Maze no DirectX .

Aqui estão alguns dos principais pontos que este documento aborda para quando você planejar e desenvolver seu jogo da Plataforma Universal do Windows (UWP).

  • Use o modelo DirectX 11 App (Universal Windows - C++/CX) no Visual Studio para criar seu jogo UWP no DirectX.
  • O Windows Runtime fornece classes e interfaces para que você possa desenvolver aplicativos UWP de maneira mais moderna e orientada a objetos.
  • Use referências de objeto com o símbolo de circunflexo (^) para gerenciar o tempo de vida das variáveis do Windows Runtime, Microsoft::WRL::ComPtr para gerenciar o tempo de vida de objetos COM e std::shared\_ptr ou std::unique\_ptr para gerenciar o tempo de vida de todos os outros objetos C++ com alocação de heap.
  • Na maioria dos casos, use o manuseio de exceções em vez de códigos de resultado para lidar com erros inesperados.
  • Use anotações SAL junto com ferramentas de análise de código para obter ajuda e descobrir erros no seu aplicativo.

Criando o projeto do Visual Studio

Se tiver baixado e extraído a amostra, você poderá abrir o arquivo MarbleMaze_VS2017.sln (na pasta C++) no Visual Studio e ver o código na sua frente.

Quando criamos o projeto do Marble Maze no Visual Studio, começamos por um projeto existente. No entanto, se você ainda não tiver um projeto existente que forneça a funcionalidade básica necessária para o jogo UWP no DirectX, recomendamos que você crie um projeto com base no modelo DirectX 11 App (Universal Windows - C++/CX), porque ele fornece um aplicativo em 3D básico que funciona. Para fazer isso, siga estas etapas:

  1. No Visual Studio 2019, selecione Arquivo > Novo > Projeto...

  2. Na janela Criar um novo projeto, selecione DirectX 11 App (Universal Windows - C++/CX). Se não estiver vendo essa opção, você talvez não tenha os componentes necessários instalados. Confira Modificar o Visual Studio 2019 adicionando ou removendo cargas de trabalho e componentes para obter informações sobre como instalar componentes adicionais.

Novo projeto

  1. Selecione Avançar e, em seguida, insira um Nome de Projeto, um Local onde os arquivos serão armazenados e um Nome de solução. A seguir, selecione Criar.

Uma configuração de projeto importante no modelo DirectX 11 App (Universal Windows - C++/CX) é a opção /ZW, que permite que o programa use as extensões de linguagem do Windows Runtime. Essa opção é habilitada por padrão quando você usa o modelo do Visual Studio. Confira as opções de Compilador e Vinculador (C++/CX) para obter mais informações sobre como configurar opções de compilador no Visual Studio.

Cuidado A opção /ZW não é compatível com opções como /clr. No caso do /clr, isso significa que você não pode ter tanto o .NET Framework quanto o Windows Runtime como destino para o mesmo projeto do Visual C++.

 

Cada aplicativo UWP que você adquire na Microsoft Store vem na forma de um pacote de aplicativo. Um pacote do aplicativo contém um manifesto de pacote, que contém informações sobre o seu aplicativo. Por exemplo, você pode especificar as funcionalidades (ou seja, o acesso obrigatório a recursos protegidos do sistema ou dados do usuário) do seu aplicativo. Se você determinar que seu aplicativo requer determinadas funcionalidades, use o manifesto de pacote para declarar as funcionalidades obrigatórias. O manifesto também permite que você especifique propriedades do projeto, como as rotações de dispositivo com suporte, tela de abertura e imagens para o bloco do aplicativo. Você pode editar o manifesto abrindo Package.appxmanifest no seu projeto. Para obter mais informações sobre pacotes de aplicativos, confira Como empacotar aplicativos.

Como compilar, implantar e executar o jogo

Nos menus suspensos na parte superior do Visual Studio, à esquerda do botão Play verde, selecione a configuração da sua implantação. Recomendamos defini-la como Depuração, tendo como alvo a arquitetura do seu dispositivo (x86 para 32 bits, x64 para 64 bits) e para o seu Computador Local. Você também pode testá-lo em um Computador Remoto ou em um Dispositivo conectado por USB. Em seguida, clique no botão Play verde para compilar e implantar no seu dispositivo.

Depurar; x64; Máquina local

Como controlar o jogo

Você pode usar o toque, o acelerômetro, um controlador de jogo ou o mouse para controlar o Marble Maze.

  • Use o teclado direcional no controlador para mudar o item de menu ativo.
  • Use o toque, o botão A ou Iniciar no controlador ou o mouse para escolher um item do menu.
  • Use o toque, o acelerômetro, a alavanca analógica esquerda ou o mouse para inclinar o labirinto.
  • Use o toque, o botão A ou Iniciar no controlador ou o mouse para fechar menus como a tabela de pontuações máximas.
  • Use o botão Iniciar no controlador ou a tecla P no teclado para pausar ou retomar o jogo.
  • Use o botão Voltar no controlador ou a tecla Home no teclado para reiniciar o jogo.
  • Quando a tabela de pontuações máximas estiver visível, use o botão Voltar no controlador ou a tecla Home no teclado para limpar todas as pontuações.

Convenções de código

O Windows Runtime é uma interface de programação que você pode usar para criar aplicativos UWP que são executados apenas em um ambiente de aplicativo especial. Esses aplicativos usam funções autorizadas, tipos de dados e dispositivos, sendo distribuídos usando a Microsoft Store. No nível mais baixo, o Windows Runtime consiste de uma Interface Binária de Aplicativo (ABI). ABI é um contrato binário de baixo nível que torna as APIs do Windows Runtime acessíveis a várias linguagens de programação, como JavaScript, linguagens .NET e Visual C++.

Para chamar APIs do Windows Runtime do JavaScript e do .NET, essas linguagens requerem projeções específicas para o ambiente de cada linguagem. Quando você chama uma API do Windows Runtime do JavaScript ou do .NET, você está invocando a projeção, que, por sua vez, chama a função ABI subjacente. Embora você possa chamar as funções de ABI diretamente no C++, a Microsoft também fornece projeções para C++, porque simplificam muito o consumo de APIs do Windows Runtime e, ao mesmo tempo, mantêm um alto desempenho. A Microsoft também fornece extensões de linguagem para Visual C++ que dão suporte, especificamente, às projeções do Windows Runtime. Muitas dessas extensões de linguagem são semelhantes à sintaxe para a linguagem do C++/CLI. No entanto, em vez de serem direcionados para o Common Language Runtime (CLR), os aplicativos nativos usam essa sintaxe para serem direcionados para o Windows Runtime. O modificador da referência de objeto, ou circunflexo (^), é uma parte importante dessa nova sintaxe porque permite a exclusão automática de objetos de runtime por meio da contagem de referência. Em vez de chamar métodos como AddRef e Release para gerenciar o tempo de vida de um objeto do Windows Runtime, o runtime exclui o objeto quando nenhum outro componente faz referência a ele, como, por exemplo, quando ele sai do escopo ou você define todas as referências como nullptr. Outro aspecto importante do uso do Visual C++ para criar aplicativos UWP é a palavra-chave ref new. Use ref new em vez de novo para criar objetos do Windows Runtime contados por referência. Para obter mais informações, confira Tipo de Sistema (C++/CX).

Importante

Você só precisa usar ^ e ref new quando criar objetos do Windows Runtime ou componentes do Windows Runtime. Você pode usar a sintaxe C++ padrão quando escrever o código do aplicativo básico que não usa o Windows Runtime.

O Marble Maze usa ^ junto com Microsoft::WRL::ComPtr para gerenciar objetos com alocação de heap e minimizar perdas de memória. Recomendamos o uso do ^ para gerenciar a tempo de vida das variáveis do Windows Runtime, ComPtr para gerenciar o tempo de vida das variáveis do COM (como, por exemplo, quando você usa o DirectX) e std::shared\_ptr ou std::unique\_ptr para gerenciar a tempo de vida de todos os outros objetos C++ com alocação de heap.

 

Para obter mais informações sobre as extensões de linguagem que estão disponíveis para um aplicativo UWP em C++, confira Referência Visual da Linguagem C++ (C++/CX).

Tratamento de erros

O Marble Maze usa o manuseio de exceções como a principal forma de lidar com erros inesperados. Embora o código de jogos tradicionalmente use códigos de erro ou registros em log, como valores HRESULT, para indicar erros, o manuseio de exceções tem duas vantagens principais. Em primeiro lugar, pode facilitar a leitura e a manutenção do código. Do ponto de vista de um código, o manuseio de exceções é uma maneira mais eficiente de propagar um erro para uma rotina capaz de manuseá-lo. O uso de códigos de erro normalmente requer que cada função propague erros explicitamente. Uma segunda vantagem é que você pode configurar o depurador do Visual Studio para ser interrompido quando ocorrer uma exceção, para que você pode possa parar imediatamente no local e no contexto do erro. O Windows Runtime também usa extensivamente o manuseio de exceções. Portanto, ao usar o manuseio de exceção no código, você pode integrar o manuseio de todos os erros em um único modelo.

Recomendamos que você use as seguintes convenções no seu modelo de manuseio de erros:

  • Use exceções para comunicar erros inesperados.

  • Não use exceções para controlar o fluxo do código.

  • Só capture as exceções que você pode manusear com segurança e das quais pode se recuperar. Caso contrário, não capture a exceção e permita que o aplicativo seja encerrado.

  • Quando chamar uma rotina do DirectX que retorna HRESULT, use a função DX::ThrowIfFailed. Essa função é definida em DirectXHelper.h. ThrowIfFailed irá gerar uma exceção se o HRESULT fornecido for um código de erro. Por exemplo, E_POINTER faz com que ThrowIfFailed gere Platform::NullReferenceException.

    Ao usar ThrowIfFailed, coloque a chamada do DirectX em uma linha separada para ajudar a aumentar a legibilidade do código, conforme mostrado no exemplo a seguir.

    // Identify the physical adapter (GPU or card) this device is running on.
    ComPtr<IDXGIAdapter> dxgiAdapter;
    DX::ThrowIfFailed(
        dxgiDevice->GetAdapter(&dxgiAdapter)
        );
    
  • Embora recomendemos que você evite o uso de HRESULT para erros inesperados, é mais importante evitar o uso do manuseio de exceções para controlar o fluxo do código. Portanto, é preferível usar um valor retornado HRESULT sempre que necessário para controlar o fluxo do código.

Anotações de SAL

Use anotações SAL junto com ferramentas de análise de código para obter ajuda e descobrir erros no seu aplicativo.

Usando a linguagem de anotação do código-fonte da Microsoft (SAL), você pode anotar, ou descrever, como uma função usa seus parâmetros. As anotações SAL também descrevem valores de retorno. As anotações SAL funcionam com a ferramenta de Análise de Código do C/C++ para descobrir possíveis defeitos no código-fonte do C e do C++. Entre os erros de codificação comuns reportados pela ferramenta estão estouros de buffer, memória não inicializada, desreferências de ponteiro nulo, memória e perdas de recurso.

Pense em usar o método BasicLoader::LoadMesh, que é declarado em BasicLoader.h. Esse método usa _In_ para especificar que o nome do arquivo é um parâmetro de entrada (usado, portanto, apenas para ler), _Out_ para especificar que vertexBuffer e indexBuffer são parâmetros de saída (usados, portanto, apenas para gravar) e _Out_opt_ especificar que vertexCount e indexCount são parâmetros de saída opcionais (podendo ser usados para gravar). Como vertexCount e indexCount são parâmetros de saída opcionais, têm permissão para serem nullptr. A ferramenta de Análise de Código do C/C++ examina as chamadas para esse método para garantir que os parâmetros que transmite atendam a esses critérios.

void LoadMesh(
    _In_ Platform::String^ filename,
    _Out_ ID3D11Buffer** vertexBuffer,
    _Out_ ID3D11Buffer** indexBuffer,
    _Out_opt_ uint32* vertexCount,
    _Out_opt_ uint32* indexCount
    );

Para realizar a análise do código no seu aplicativo, na barra de menus, escolha Compilar > Executar Análise de Código na Solução. Para obter mais informações sobre análise de código, confira Como analisar a qualidade do código C/C++ usando Análise de Código.

A lista completa das anotações disponíveis está definida em sal.h. Para obter mais informações, confira Anotações SAL.

Próximas etapas

Leia Estrutura do aplicativo do Marble Maze para obter informações sobre como o código do aplicativo Marble Maze é estruturado e como a estrutura de um aplicativo UWP do DirectX difere da de um aplicativo de área de trabalho tradicional.