打包 通用 Windows 平台 (UWP) DirectX 游戏

更大的通用 Windows 平台(UWP)游戏,尤其是那些支持具有区域特定资产的多种语言或具有可选高清资产的多种语言的游戏,可以轻松膨胀到大型大小。 在本主题中,了解如何使用应用包和应用捆绑包自定义应用,以便客户仅接收实际需要的资源。

除了应用包模型之外,Windows 10 还支持将两种类型的包组合在一起的应用捆绑包:

  • 应用包包含特定于平台的可执行文件和库。 通常,UWP 游戏最多可以有三个应用包:每个应用包一个用于 x86、x64 和 Arm CPU 体系结构。 特定于该硬件平台的所有代码和数据都必须包含在其应用包中。 应用包还应包含游戏以基线级别保真和性能运行的所有核心资产。
  • 资源包包含可选的或扩展的平台不可知的数据,例如游戏资产(纹理、网格、声音、文本)。 UWP 游戏可以包含一个或多个资源包,包括用于高清资产或纹理的资源包、DirectX 功能级别 11+ 资源或特定于语言的资产和资源。

有关应用捆绑包和应用包的详细信息,请阅读 “定义应用资源”。

虽然你可以在应用包中放置所有内容,但这效率低下且冗余。 为什么每个平台复制了三次相同的大型纹理文件,尤其是可能不使用它的 Arm 平台? 一个很好的目标是尽量减少客户必须下载的内容,以便他们可以更快地开始玩游戏,节省设备上的空间,并避免可能的按流量计费的带宽成本。

若要使用 UWP 应用安装程序的此功能,请务必在游戏开发早期考虑应用和资源打包的目录布局和文件命名约定,以便工具和源可以正确输出它们,从而简化打包。 在开发或配置资产创建和管理工具和脚本以及编写加载或引用资源的代码时,请遵循本文档中概述的规则。

为什么创建资源包?

创建应用(尤其是可在许多区域设置或各种 UWP 硬件平台中销售的游戏应用)时,通常需要包含多个版本的多个文件来支持这些区域设置或平台。 例如,如果要在 美国 和日本发布游戏,则可能需要一组英语语音文件用于 en-us 区域设置,另一组用于 jp-jp 区域设置的日语语音文件。 或者,如果要在游戏中对 Arm 设备以及 x86 和 x64 平台使用图像,则必须为每个 CPU 体系结构上传一次相同的映像资产 3 次。

此外,如果你的游戏有很多高清资源不适用于较低 DirectX 功能级别的平台,为什么将它们包含在基线应用包中,并要求用户下载设备无法使用的大量组件? 将这些高 def 资源分离为一个可选资源包意味着,支持这些高 def 资源的设备的客户可以按带宽(可能按流量计费)带宽获取这些资源,而那些没有高端设备的客户可以更快地获得游戏,并且网络使用成本较低。

游戏资源包的内容候选项包括:

  • 国际区域设置特定资产(本地化文本、音频或图像)
  • 不同设备缩放系数的高分辨率资产(1.0x、1.4x 和 1.8x)
  • 用于更高 DirectX 功能级别的高清资产(9、10 和 11)

这一切在作为 UWP 项目的一部分的 package.appxmanifest 和最终包的目录结构中定义。 由于新的 Visual Studio UI,如果遵循本文档中的过程,则无需手动编辑它。

重要提示:通过 Windows.ApplicationModel.Resources* API 来处理这些资源的加载和管理。 如果使用这些应用模型资源 API 为区域设置、缩放因子或 DirectX 功能级别加载正确的文件,则无需使用显式文件路径加载资产;相反,只需为资源 API 提供所需的资产的通用文件名,并让资源管理系统获取用户当前平台和区域设置配置的资源的正确变体(可以直接通过这些 API 指定)。

资源打包的资源采用以下两种基本方法之一进行指定:

  • 资产文件具有相同的文件名,资源包特定版本放置在特定的命名目录中。 这些目录名称由系统保留。 例如,\en-us、\scale-140、\dxfl-dx11。
  • 资产文件存储在具有任意名称的文件夹中,但文件以公共标签命名,该标签附加了系统保留的字符串来表示语言或其他限定符。 具体来说,限定符字符串会紧跟一般化文件名后的下划线字符(“_”)。 例如,\assets\menu_option1_lang-en-us.png、\assets\menu_option1_scale-140.png、\assets\coolsign_dxfl-dx11.dds。 还可以合并这些字符串。 例如,\assets\menu_option1_scale-140_lang-en-us.png。

    注意 当用于文件名而不是单独用于目录名称时,语言限定符必须采用形式“lang-<tag>”,例如“lang-en-us”,如定制语言、比例和其他限定符的资源中所述。

目录名称可以组合在资源打包中的其他特定性。 但是,它们不能冗余。 例如,\en-us\menu_option1_lang-en-us.png 就是多余的。

只要每个资源目录中的目录结构相同,就可以指定资源目录下所需的任何非保留子目录名称。 例如,\dxfl-dx10\assets\textures\coolsign.dds。 加载或引用资产时,必须通用化路径名称,删除语言、缩放或 DirectX 功能级别的任何限定符,无论它们位于文件夹节点还是文件名中。 例如,若要在代码中引用具有一个 \dxfl-dx10\assets\textures\coolsign.dds 变体的资源,请使用 \assets\textures\coolsign.dds。 同样,若要引用具有变体 \images\background_scale-140.png 的资源,请使用 \images\background.png。

下面是以下保留的目录名称和文件名下划线前缀:

资产类型 资源包目录名称 资源包文件名后缀
本地化资产 Windows 10 的所有可能语言或语言和区域设置组合。 (文件夹名称中不需要限定符前缀“lang-”。 “_”,后跟语言、区域设置,或者语言-区域设置说明符。 例如,分别为“_en”、“_us”或“_en-us”。
缩放因子资产 scale-100, scale-140, scale-180. 这些分别用于 1.0x、1.4x 和 1.8x UI 缩放因子。 “_”,后跟“scale-100”、“scale-140”或“scale-180”。
DirectX 功能级别资产 dxfl-dx9、dxfl-dx10 和 dxfl-dx11。 这些分别适用于 DirectX 9、10 和 11 功能级别。 “_”,后跟“dxfl-dx9”、“dxfl-dx10”或“dxfl-dx11”。

 

定义本地化语言资源包

特定于区域设置的文件放置在以语言命名的项目目录中(例如,“en”)。

将应用配置为支持多种语言的本地化资产时,应:

  • 为每个语言和区域设置创建应用子目录(或文件版本),你将支持(例如 en-us、jp-jp、zh-cn、fr-fr 等)。

  • 在开发过程中,将所有资产(如本地化的音频文件、纹理和菜单图形)的副本置于相应的语言区域设置子目录中,即使它们在不同语言或区域设置中不同。 为获得最佳用户体验,如果用户尚未获取其区域设置的可用语言资源包(或者在下载和安装后意外删除该资源包),请确保用户收到警报。

  • 确保每个资产或字符串资源文件(.resw)在每个目录中具有相同的名称。 例如,menu_option1.png 在 \en-us 和 \jp-jp 目录中应该具有相同名称,即使该文件的内容适用于不同的语言。 在本例中,你会看到它们的名称为 \en-us\menu_option1.png 和 \jp-jp\menu_option1.png。

    注意 你可以选择将区域设置附加到文件名,并将其存储在相同的目录中;例如,\assets\menu_option1_lang-en-us.png、\assets\menu_option1_lang-jp-jp.png。

     

  • 使用 Windows.ApplicationModel.Resources 和 Windows.ApplicationModel.Resources.Core 中的 API 为应用指定和加载特定于区域设置的资源。 此外,使用不包含特定区域设置的资产引用,因为这些 API 根据用户的设置确定正确的区域设置,然后检索用户的正确资源。

  • 在 Microsoft Visual Studio 2015 中,请选择“项目 --> 存储 -> 创建应用包...”,然后创建此程序包。

定义缩放因子资源包

Windows 10 提供三个用户界面缩放因子:1.0x、1.4x 和 1.8x。 根据多种组合因素在安装过程中设置每个显示器的缩放值:屏幕大小、屏幕分辨率以及用户与屏幕的假定平均距离。 用户还可以调整缩放因子以提高可读性。 你的游戏应同时识别 DPI 和缩放因子感知,以获得最佳体验。 此意识的一部分意味着为每个三个缩放因子创建关键视觉资产的版本。 这还包括指针交互和命中测试!

将应用配置为支持不同 UWP 应用缩放因素的资源包时,应:

  • 为每个缩放因子创建应用子目录(或文件版本),你将支持(scale-100、scale-140 和 scale-180)。

  • 在开发过程中,将每个缩放因子资源目录中所有资产的规模因子适当副本放在一起,即使它们在缩放因子中不同也是如此。

  • 确保每个资产在每个目录中具有相同的名称。 例如,menu_option1.png 在 \scale-100 和 \scale-180 目录中应该具有相同名称,即使该文件的内容不同。 在本例中,你会看到它们的名称为 \scale-100\menu_option1.png 和 \scale-140\menu_option1.png。

    注意 同样,你可以选择将比例系数后缀附加到文件名,并将其存储在相同的目录中;例如,\assets\menu_option1_scale-100.png、\assets\menu_option1_scale-140.png。

     

  • 使用 Windows.ApplicationModel.Resources.Core 中的 API 加载资产。 资产引用应通用化(无后缀),不显示特定的刻度变体。 系统将检索显示和用户设置的相应缩放资产。

  • 在 Visual Studio 2015 中,请选择“项目 -> 存储 -> 创建应用包...”,然后创建此程序包。

定义 DirectX 功能级别资源包

DirectX 功能级别对应于 DirectX 早期版本和当前版本的 GPU 功能集(特别是 Direct3D)。 这包括着色器模型规范和功能、着色器语言支持、纹理压缩支持和整体图形管道功能。

基线应用包应使用基线纹理压缩格式:BC1、BC2 或 BC3。 这些格式可由任何 UWP 设备使用,从低端 Arm 平台到专用多 GPU 工作站和媒体计算机。

应在资源包中添加 DirectX 功能级别 10 或更高版本的纹理格式支持,以节省本地磁盘空间和下载带宽。 这允许对 11(如 BC6H 和 BC7)使用更高级的压缩方案。 (有关详细信息,请参阅 Direct3D 11 中的纹理块压缩。这些格式对于新式 GPU 支持的高分辨率纹理资产更高效,并且使用它们可提高游戏在高端平台上的外观、性能和空间要求。

DirectX 功能级别 支持的纹理压缩
9 BC1、BC2、BC3
10 BC4、BC5
11 BC6H、BC7

 

此外,每个 DirectX 功能级别都支持不同的着色器模型版本。 可以基于每个功能级别创建已编译的着色器资源,并且可以包含在 DirectX 功能级别资源包中。 此外,某些更高版本的着色器模型可以使用早期着色器模型版本无法使用资产,例如普通地图。 这些着色器模型特定的资产也应包含在 DirectX 功能级别资源包中。

资源机制主要侧重于资产支持的纹理格式,因此它仅支持 3 个整体功能级别。 如果你需要为类似于 DX9_1 与 DX9_3 的子级别(点版本)设置独立的着色器,你的资源管理和呈现代码必须明确处理它们。

将应用配置为支持不同 DirectX 功能级别的资源包时,应:

  • 为每个 DirectX 功能级别创建应用子目录(或文件版本),你将支持 (dxfl-dx9、dxfl-dx10 和 dxfl-dx11)。

  • 在开发过程中,在每个功能级别资源目录中放置特定于功能级别的资产。 与区域设置和缩放因子不同,对于游戏中的每个功能级别,你可能具有不同的呈现代码分支,并且如果你有纹理、已编译的着色器或其他仅用于所有受支持功能级别的一个或子集的其他资产,请将相应的资产放在使用它们的功能级别的目录中。 对于跨所有功能级别加载的资产,请确保每个功能级别资源目录都有一个具有相同名称的版本。 例如,对于名为“coolsign.dds”的功能级别独立纹理,请将使用 BC3 格式压缩的版本放置到 \dxfl-dx9 目录中,并将使用 BC7 格式压缩的版本放置到 \dxfl-dx11 目录中。

  • 确保每个资产(如果可用于多个功能级别)在每个目录中具有相同的名称。 例如,coolsign.dds 在 \dxfl-dx9 和 \dxfl-dx11 目录中应该具有相同名称,即使该文件的内容不相同。 在本例中,你会看到它们的名称为 \dxfl-dx9\coolsign.dds 和 \dxfl-dx11\coolsign.dds。

    注意 同样,你可以选择将比例系数后缀附加到文件名,并将其存储在相同的目录中;例如,\textures\coolsign_dxfl-dx9.dds、\textures\coolsign_dxfl-dx11.dds。

     

  • 配置图形资源时,声明支持的 DirectX 功能级别。

    D3D_FEATURE_LEVEL featureLevels[] = 
    {
      D3D_FEATURE_LEVEL_11_1,
      D3D_FEATURE_LEVEL_11_0,
      D3D_FEATURE_LEVEL_10_1,
      D3D_FEATURE_LEVEL_10_0,
      D3D_FEATURE_LEVEL_9_3,
      D3D_FEATURE_LEVEL_9_1
    };
    
    ComPtr<ID3D11Device> device;
    ComPtr<ID3D11DeviceContext> context;
    D3D11CreateDevice(
        nullptr,                    // Use the default adapter.
        D3D_DRIVER_TYPE_HARDWARE,
        0,                      // Use 0 unless it is a software device.
        creationFlags,          // defined above
        featureLevels,          // What the app will support.
        ARRAYSIZE(featureLevels),
        D3D11_SDK_VERSION,      // This should always be D3D11_SDK_VERSION.
        &device,                    // created device
        &m_featureLevel,            // The feature level of the device.
        &context                    // The corresponding immediate context.
    );
    
  • 使用 Windows.ApplicationModel.Resources.Core 中的 API 加载资源。 资产引用应通用化(无后缀),从而排除功能级别。 但是,与语言和缩放不同,系统不会自动确定哪个功能级别最适合给定的显示;由你决定基于代码逻辑。 确定后,请使用 API 通知 OS 首选功能级别。 然后,系统将能够基于该首选项检索正确的资产。 下面是一个代码示例,演示如何通知应用平台的当前 DirectX 功能级别:

    // Set the current UI thread's MRT ResourceContext's DXFeatureLevel with the right DXFL. 
    
    Platform::String^ dxFeatureLevel;
        switch (m_featureLevel)
        {
        case D3D_FEATURE_LEVEL_9_1:
        case D3D_FEATURE_LEVEL_9_2:
        case D3D_FEATURE_LEVEL_9_3:
            dxFeatureLevel = L"DX9";
            break;
        case D3D_FEATURE_LEVEL_10_0:
        case D3D_FEATURE_LEVEL_10_1:
            dxFeatureLevel = L"DX10";
            break;
        default:
            dxFeatureLevel = L"DX11";
        }
    
        ResourceContext::SetGlobalQualifierValue(L"DXFeatureLevel", dxFeatureLevel);
    

注意

在代码中,直接按名称(或功能级别目录下面的路径)加载纹理。 请勿包括功能级别目录名称或后缀。 例如,加载“textures\coolsign.dds”,而不是“dxfl-dx11\textures\coolsign.dds”或“textures\coolsign_dxfl-dx11.dds”。

  • 现在, 使用 ResourceManager 找到与当前 DirectX 功能级别匹配的文件。 ResourceManager 返回一个 ResourceMap,使用 ResourceMap::GetValue(或 ResourceMap::TryGetValue)和提供的 ResourceContext 进行查询。 这会返回最符合通过调用 SetGlobalQualifierValue 指定的 DirectX 功能级别的 ResourceCandidate

    // An explicit ResourceContext is needed to match the DirectX feature level for the display on which the current view is presented.
    
    auto resourceContext = ResourceContext::GetForCurrentView();
    auto mainResourceMap = ResourceManager::Current->MainResourceMap;
    
    // For this code example, loader is a custom ref class used to load resources.
    // You can use the BasicLoader class from any of the 8.1 DirectX samples similarly.
    
    
    auto possibleResource = mainResourceMap->GetValue(
        L"Files/BumpPixelShader.cso",
        resourceContext
    );
    Platform::String^ resourceName = possibleResource->ValueAsString;
    
  • 在 Visual Studio 2015 中,请选择“项目 -> 存储 -> 创建应用包...”,然后创建此程序包。

  • 请确保在 package.appxmanifest 清单设置中启用应用捆绑包。