Marble Maze 范例基础知识

本主题介绍 Marble Maze 项目的基本特征,例如,如何在 Windows 运行时环境中使用 Visual C++、如何创建和构建 Visual Maze 项目及其构建方式。 本主题还介绍了代码中使用的几个约定。

注释

DirectX Marble Maze 游戏示例中找到与此文档对应的示例代码。

以下是本文档在规划和开发通用 Windows 平台(UWP)游戏时讨论的一些要点。

  • 使用 Visual Studio 中的 DirectX 11 应用(通用 Windows - C++/CX) 模板创建 DirectX UWP 游戏。
  • Windows 运行时提供类和接口,以便你可以以更现代、面向对象的方式开发 UWP 应用。
  • 将对象引用与 hat (^) 符号配合使用来管理 Windows 运行时变量的生存期,Microsoft::WRL::ComPtr 来管理 COM 对象的生存期,std::shared_ptrstd::unique_ptr 来管理其他所有堆分配的 C++ 对象的生存期。
  • 在大多数情况下,使用异常处理(而不是结果代码)来处理意外错误。
  • 结合使用 SAL 注释和 代码分析工具以帮助发现应用中的错误。

创建 Visual Studio 项目

如果你已下载并提取了示例,你可以在 Visual Studio 中打开位于C++ 文件夹中的 MarbleMaze_VS2017.sln 文件,这样代码就会显示在你面前。

创建 Marble Maze 的 Visual Studio 项目时,我们开始使用现有项目。 但是,如果你还没有提供 DirectX UWP 游戏所需的基本功能的现有项目,我们建议你基于 Visual Studio DirectX 11 应用(通用 Windows - C++/CX) 模板创建项目,因为它提供了基本的 3D 应用程序。 为此,请执行以下步骤:

  1. 在 Visual Studio 2019 中,选择 “文件 > 新建 > 项目...”

  2. 创建新项目 窗口中,选择 DirectX 11 应用(通用 Windows - C++/CX)。 如果未看到此选项,可能尚未安装所需的组件,请参阅 通过添加或删除工作负载和组件来修改 Visual Studio 2019, 了解如何安装其他组件。

新建项目

  1. 选择 下一步,然后输入 项目名称、存储文件的 位置解决方案名称,然后选择 创建

DirectX 11 应用(通用 Windows - C++/CX) 模板中的一个重要项目设置是 /ZW 选项,使程序能够使用 Windows 运行时语言扩展。 使用 Visual Studio 模板时,默认启用此选项。 有关如何在 Visual Studio 中设置编译器选项的详细信息,请参阅 编译器和链接器选项(C++/CX)

警告/ZW 选项与 /clr等选项不兼容。 对于 /clr,这意味着无法在同一个 Visual C++ 项目中同时针对 .NET 框架和 Windows 运行时进行配置。

 

从 Microsoft 应用商店获取的每个 UWP 应用都以应用包的形式提供。 应用包包含包清单,其中包含有关应用的信息。 例如,您可以指定应用程序的功能(即对受保护的系统资源或用户数据的访问权限)。 如果确定应用需要某些功能,请使用包清单声明所需的功能。 清单还允许你指定项目属性,例如受支持的设备旋转、磁贴图像和初始屏幕。 可以通过在项目中打开 Package.appxmanifest 来编辑清单。 有关应用包的详细信息,请参阅 打包应用

生成、部署和运行游戏

在 Visual Studio 顶部的下拉菜单中,选择位于绿色播放按钮左侧的部署配置。 建议将其设置为 调试,针对设备的体系结构(x86,32 位;x64,64 位)以及 本地计算机。 您还可以在 远程计算机上测试,或者在通过 USB 连接的 设备 上进行测试。 然后单击绿色播放按钮以构建并部署到您的设备。

调试;x64;本地计算机

控制游戏

可以使用触摸、加速计、游戏控制器或鼠标来控制 Marble Maze。

  • 使用控制器上的方向键更改当前菜单项。
  • 使用触摸、控制器上的 A 或“开始”按钮或鼠标选取菜单项。
  • 使用触摸、加速计、左操纵杆或鼠标倾斜迷宫。
  • 使用触摸、控制器上的 A 或“开始”按钮或鼠标关闭诸如高分表等菜单。
  • 使用控制器上的“开始”按钮或键盘上的 P 键暂停或恢复游戏。
  • 使用控制器上的“后退”按钮或键盘上的“开始”键重启游戏。
  • 当高分表可见时,请使用控制器上的“后退”按钮或键盘上的“开始”键清除所有分数。

代码约定

Windows 运行时是一个编程接口,可用于创建仅在特殊应用程序环境中运行的 UWP 应用。 此类应用使用授权的函数、数据类型和设备,并从 Microsoft 应用商店分发。 在最低级别,Windows 运行时由应用程序二进制接口(ABI)组成。 ABI 是一种低级二进制协定,使 Windows 运行时 API 可供多个编程语言(如 JavaScript、.NET 语言和 Visual C++)访问。

为了从 JavaScript 和 .NET 调用 Windows 运行时 API,这些语言需要特定于每个语言环境的投影。 当您从 JavaScript 或 .NET 调用 Windows 运行时 API 时,您实际在调用的是投影层,而投影层又会调用底层的 ABI 函数。 尽管可以直接在 C++ 中调用 ABI 函数,但微软也为 C++ 提供了映射,因为它们使得使用 Windows 运行时 API 变得更加简单,同时仍保持高性能。 Microsoft还提供专门支持Windows运行时的投影的Visual C++语言扩展。 其中许多语言扩展类似于 C++/CLI 语言的语法。 但是,本机应用使用此语法来面向 Windows 运行时,而不是面向公共语言运行时(CLR)。 对象引用或 hat (^) 修饰符是此新语法的重要组成部分,因为它可以通过引用计数自动删除运行时对象。 运行时不会调用诸如 AddRefRelease 等方法来管理 Windows 运行时对象的生存期,而是在没有其他组件引用对象时删除该对象,例如,当它离开范围或将所有引用设置为 nullptr 。 使用 Visual C++创建 UWP 应用的另一个重要部分是 ref new 关键字。 使用 ref 新的,而不是 新的 来创建引用计数的 Windows 运行时对象。 有关详细信息,请参阅 类型系统(C++/CX)

重要

创建 Windows 运行时对象或创建 Windows 运行时组件时,只需使用 ^引用新的。 编写不使用 Windows 运行时的核心应用程序代码时,可以使用标准C++语法。

Marble Maze 一起使用 ^Microsoft::WRL::ComPtr 来管理堆分配的对象,并最大限度地减少内存泄漏。 建议使用 ^ 来管理 Windows 运行时变量的生存期,ComPtr 来管理 COM 变量的生存期(例如使用 DirectX 时),以及 std::shared_ptrstd::unique_ptr 来管理所有其他堆分配C++对象的生存期。

 

有关可用于 C++ UWP 应用的语言扩展的详细信息,请参阅 Visual C++ 语言参考(C++/CX)

错误处理

Marble Maze 使用异常处理作为处理意外错误的主要方法。 尽管游戏代码传统上使用日志记录或错误代码(如 HRESULT 值)来指示错误,但异常处理具有两个主要优势。 首先,它可以使代码更易于读取和维护。 从代码的角度来看,异常处理是将错误传播到可以处理该错误的例程的更高效方法。 使用错误代码通常要求每个函数显式地传播错误。 第二个优点是,可以将 Visual Studio 调试器配置为在发生异常时中断,以便可以立即在错误的位置和上下文处停止。 Windows 运行时还广泛使用异常处理。 因此,通过在代码中使用异常处理,可以将所有错误处理合并到一个模型中。

建议在错误处理模型中使用以下约定:

  • 使用异常传达意外错误。

  • 不要使用异常来控制代码流。

  • 仅捕获那些可以安全地处理和恢复的异常。 否则,请不要捕获异常并允许应用终止。

  • 调用返回 HRESULT的 DirectX 例程时,请使用 DX::ThrowIfFailed 函数。 此函数在 DirectXHelper.h中定义。 如果提供的 HRESULT 是错误代码,那么 ThrowIfFailed 将引发异常。 例如,E_POINTER 导致 ThrowIfFailed 引发 Platform::NullReferenceException

    使用 ThrowIfFailed时,请将 DirectX 调用放在单独的行上,以帮助提高代码可读性,如以下示例所示。

    // Identify the physical adapter (GPU or card) this device is running on.
    ComPtr<IDXGIAdapter> dxgiAdapter;
    DX::ThrowIfFailed(
        dxgiDevice->GetAdapter(&dxgiAdapter)
        );
    
  • 尽管我们建议避免将 HRESULT 用于意外错误,但更重要的是避免使用异常处理来控制代码流。 因此,建议在必要时使用 HRESULT 返回值来控制代码流。

SAL 注释

使用 SAL 注释和代码分析工具结合起来,以帮助发现应用中的错误。

通过使用Microsoft源代码注释语言(SAL),可以批注或描述函数如何使用其参数。 SAL 注释还描述返回值。 SAL 批注使用 C/C++ 代码分析工具,以发现 C 和C++源代码中可能存在的缺陷。 该工具报告的常见编码错误包括缓冲区溢出、未初始化内存、空指针引用以及资源和内存泄漏。

请考虑 BasicLoader::LoadMesh 方法,该方法在 BasicLoader.h中声明。 此方法使用 指定 文件名 是输入参数(因此只能读取), 指定 顶点BufferindexBuffer 是输出参数(因此只会写入),并且 指定 顶点CountindexCount 是可选的输出参数(并可能写入)。 由于 vertexCountindexCount 是可选的输出参数,因此可以设为 nullptr。 C/C++代码分析工具检查对此方法的调用,以确保它传递的参数符合这些条件。

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

若要对应用执行代码分析,请在菜单栏上选择 生成 > 在解决方案上运行代码分析。 有关代码分析的详细信息,请参阅 使用代码分析分析 C/C++ 代码质量。

可用批注的完整列表在 sal.h 中定义。 有关详细信息,请参阅 SAL 注释

后续步骤

阅读 Marble Maze 应用程序结构,了解 Marble Maze 应用程序代码的结构以及 DirectX UWP 应用的结构与传统桌面应用程序的结构有何不同。