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:
No Visual Studio 2019, selecione Arquivo > Novo > Projeto...
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.
- 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.
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.
Tópicos relacionados
- Estrutura do aplicativo do Marble Maze
- Como desenvolver o Marble Maze, um jogo UWP em C++ no DirectX