在 Android 应用包中指定目标纹理压缩格式

纹理是图像,可以应用于 3D 模型的表面。纹理也用于 2D 渲染器绘制元素,例如精灵或背景。本页介绍游戏中使用的常用纹理压缩格式以及如何在 Android 应用包中指定目标格式。在开始本指南之前,请先阅读 关于 Android 应用包Play Asset Delivery

背景

GPU 通常支持一组纹理压缩格式。纹理压缩格式(或 TCF)是一种针对 GPU 优化的文件格式。与使用内存中的 RGBA 值数组相比,GPU 加载和渲染纹理的速度更快,并且使用的内存更少。此支持是在硬件级别完成的:GPU 制造商将组件嵌入图形卡芯片中,这些组件读取、解压缩和渲染支持的格式。

以下是现代移动硬件上的常用纹理压缩格式

  • ASTC:最近设计的格式,旨在取代之前的格式。由于支持各种块大小,因此比之前的格式更灵活。使用此格式是优化游戏大小的好方法。
  • ETC2:支持所有支持 OpenGL ES 3.0 及更高版本的设备。这包括几乎所有活跃的 Android 移动设备。

以下百分比的 Android 设备支持这些格式

纹理压缩格式 支持该格式的 Google Play 设备的百分比
ASTC >80%
ETC2 >95%

运行 Google Play 游戏 for PC 的台式计算机 GPU 也支持此格式

  • DDS 或 S3TC:有时称为 BCn、DXTC 或 DXTn

不再推荐的旧版纹理压缩格式包括

  • ETC1:支持大多数设备。此格式不支持透明度,但游戏可以使用第二个纹理文件来存储 alpha 分量。
  • PVRTC:iOS 游戏中很流行,也支持部分 Android 设备。

ETC1 支持仅是支持非常旧的遗留设备或不支持 OpenGL ES 3.0 及更高版本的特定 Android TV 设备的游戏的要求。

默认格式

由于有这么多可用的格式(支持级别各不相同),您可能不知道在构建游戏纹理时应使用哪些格式。作为一项保障措施,应用包格式允许您为每个资产包选择默认的纹理压缩格式。如果设备不支持其他指定的格式,则使用此默认格式的资产将被安装。

除非您要针对非常旧的设备硬件,否则 ETC2 是一个很好的默认格式选择。您应该使用 ETC2 格式,这些格式保证在 OpenGL ES 3.0 中受支持。这些格式在 Vulkan 图形 API 中也可用。

ASTC 格式定义了各种压缩块大小,允许您在降低图像质量与获得更高压缩率之间进行选择。根据源艺术素材的性质,您可以为给定纹理选择较小或较大的块大小,以保持可接受的视觉质量。

如果您的游戏支持 Google Play Games for PC 并且使用 Vulkan,则应包含 S3TC 纹理。S3TC 格式受所有桌面 GPU 支持。

构建应用包

Google Play 使用 Android 应用包为每个用户的设备配置生成和提供优化的 APK,因此用户只需下载运行游戏所需的代码和资源。这些优化的 APK 包含一组纹理资产,并使用设备上最优的压缩格式进行格式化。

如果您的游戏未在 Unity 中,请 使用 Gradle 构建应用包。高级用户可能希望 使用 bundletool

如果您的游戏在 Unity 中,Unity 2021.3 及更高版本支持使用 Play Asset Delivery 的应用包。有关更多信息,请参阅 Unity 文档。您可以 使用 Unity 插件 在较低版本的 Unity 中构建应用包。

使用 Gradle

  1. 将项目 build.gradle 文件中的 Android Gradle 插件版本更新到 4.1 或更高版本(例如,com.android.tools.build:gradle:4.1.0)。

  2. 确定要针对的游戏设备类型集以及它们支持的纹理压缩格式(有关格式的更多信息,请参阅 背景)。

  3. 为上一步中的每个纹理压缩格式构建资产版本。这可能涉及使用 TexturePacker 等软件生成精灵表,或运行将原始资产转换为特定格式的脚本(例如,astc-encoder)。

  4. 创建 资产包(请参阅 为 C++ 或 Java 构建),其中包含游戏资产,并由 Play Asset Delivery 使用。例如,您可以为每个关卡创建一个资产包,或为游戏的不同部分创建资产包。

  5. 在资产包内,添加每个要支持的纹理压缩格式的目录。将 支持的后缀 添加到与包含文件使用的纹理压缩格式相对应的纹理目录名称。

    创建一个名称中没有后缀的目录(例如,common/src/main/assets/textures/)。在此目录中,放置纹理资产的默认格式。此默认格式应受大多数设备支持(例如,ETC1 或 ETC2)。如果设备不支持其他指定的格式(例如,下表中的 PVRTC 和 ASTC),则 Google Play 商店将安装此目录。

    目录之前 目录之后
    common 资产包
    common/build.gradle
    common/src/main/assets/textures/...
    common 资产包
    common/build.gradle
    common/src/main/assets/textures/...
    common/src/main/assets/textures#tcf_astc/...
    common/src/main/assets/textures#tcf_pvrtc/...
    level1 资产包
    level1/build.gradle
    level1/src/main/assets/textures/...
    level1 资产包
    level1/build.gradle
    level1/src/main/assets/textures/...
    level1/src/main/assets/textures#tcf_astc/...
    level1/src/main/assets/textures#tcf_pvrtc/...
    level2 资产包
    level2/build.gradle
    level2/src/main/assets/textures/...
    level2 资产包
    level2/build.gradle
    level2/src/main/assets/textures/...
    level2/src/main/assets/textures#tcf_astc/...
    level2/src/main/assets/textures#tcf_pvrtc/...
  6. 更新应用的 build.gradle 文件以启用按纹理拆分资产包

    // In the app build.gradle file:
    android {
        ...
        bundle {
            texture {
                enableSplit true
            }
        }
    }
    
  7. 在 Android Studio 中,选择构建 > 生成签名捆绑包/APK,或从命令行启动 Gradle 任务 以生成捆绑包。

使用 Google Play Unity 插件

获取 Play Asset Delivery 的 Unity 插件(或包) 以使用纹理目标资产包创建应用包。

准备资产

要准备纹理资产以构建应用包,请执行以下操作

  1. 将场景和资产打包到多个 Unity AssetBundles 中。

  2. 确定要针对的游戏设备类型集以及它们支持的纹理压缩格式(有关格式的更多信息,请参阅 背景)。

  3. 修改游戏的构建脚本,以便为每个要支持的纹理格式多次生成 AssetBundles。请参阅以下示例脚本

    using Google.Android.AppBundle.Editor;
    using UnityEditor;
    
    public class MyBundleBuilder
    {
       [MenuItem("Assets/Build AssetBundles TCF variants")]
       public static void BuildAssetBundles()
       {
           // Describe the AssetBundles to be built:
           var assetBundlesToBuild = new []
           {
               new AssetBundleBuild
               {
                   assetBundleName = "level1-textures",
                   assetNames = new[] {"level1/character-textures", "level1/background-textures"}
               },
               new AssetBundleBuild
               {
                   assetBundleName = "level2-textures",
                   assetNames = new[] {"level2/character-textures", "level2/background-textures"}
               }
           };
    
           // Describe where to output the asset bundles and in which formats:
           var outputPath = "Assets/AssetBundles";
           var defaultTextureFormat = MobileTextureSubtarget.ETC2;
           var additionalTextureFormats = new[] { MobileTextureSubtarget.ASTC, MobileTextureSubtarget.PVRTC }
           var allowClearDirectory = true;
    
           // Generate asset bundles:
           AssetBundleBuilder.BuildAssetBundles(
               outputPath,
               assetBundlesToBuild,
               BuildAssetBundleOptions.UncompressedAssetBundle,
               defaultTextureFormat,
               additionalTextureFormats,
               allowClearDirectory);
    
           // While in this example we're using the UI to configure the
           // AssetBundles, you can use the value returned by BuildAssetBundles
           // to configure the asset packs, if you want to build the bundle
           // entirely using the scripting API.
       }
    }
    
  4. 验证每个纹理资产是否输出到名称中具有正确后缀的目录(例如,#tcf_astc)。

    验证是否输出了一个名称中没有后缀的目录(例如,Assets/AssetBundles/)。此目录包含纹理资产的默认格式。此默认格式应受大多数设备支持(例如,ETC2)。如果设备不支持其他指定的格式(例如,上一步代码中的 ASTC),则 Google Play 商店将安装此目录。

    Assets/AssetBundles.meta
    Assets/AssetBundles/AssetBundles
    Assets/AssetBundles/AssetBundles.manifest
    Assets/AssetBundles/AssetBundles.manifest.meta
    Assets/AssetBundles/AssetBundles.meta
    Assets/AssetBundles/samplescene
    Assets/AssetBundles/samplescene.manifest
    Assets/AssetBundles/samplescene.manifest.meta
    Assets/AssetBundles/samplescene.meta
    Assets/AssetBundles/texturesbundle
    Assets/AssetBundles/texturesbundle.manifest
    Assets/AssetBundles/texturesbundle.manifest.meta
    Assets/AssetBundles/texturesbundle.meta
    Assets/AssetBundles#tcf_astc.meta
    Assets/AssetBundles#tcf_astc/AssetBundles
    Assets/AssetBundles#tcf_astc/AssetBundles.manifest
    Assets/AssetBundles#tcf_astc/AssetBundles.manifest.meta
    Assets/AssetBundles#tcf_astc/AssetBundles.meta
    Assets/AssetBundles#tcf_astc/samplescene
    Assets/AssetBundles#tcf_astc/samplescene.manifest
    Assets/AssetBundles#tcf_astc/samplescene.manifest.meta
    Assets/AssetBundles#tcf_astc/samplescene.meta
    Assets/AssetBundles#tcf_astc/texturesbundle
    Assets/AssetBundles#tcf_astc/texturesbundle.manifest
    Assets/AssetBundles#tcf_astc/texturesbundle.manifest.meta
    Assets/AssetBundles#tcf_astc/texturesbundle.meta
    
  5. 选择Google > Android > 资产传递

  6. 单击添加文件夹以添加包含默认资产包的文件夹。这些包安装在不支持您定义的其他格式的设备上。

    确保为 AssetBundle 设置传递模式

    Unity AssetBundle Delivery default format

  7. 单击添加文件夹以添加包含为另一种格式构建的 AssetBundles 的文件夹(例如,ASTC)。根据需要重复此操作。

    确保为每个 AssetBundle 设置传递模式

    Unity AssetBundle Delivery ASTC format

构建

选择Google > 构建 Android 应用包以启动游戏的 Unity 构建。它还会将 AssetBundles 打包到多个资产包中,其中每个 AssetBundle 名称都转换为单个资产包。

(高级)使用 bundletool

有关 bundletool 的更多信息,请参阅 使用 bundletool 构建应用包

要创建应用包,请执行以下操作

  1. 从其 GitHub 存储库下载 bundletool

  2. 确定要针对的游戏设备类型集以及它们支持的纹理压缩格式(有关格式的更多信息,请参阅 背景)。

  3. 为上一步中的每个纹理压缩格式构建资产版本。这可能涉及使用 TexturePacker 等软件生成精灵表,或运行将原始资产转换为特定格式的脚本(例如,astc-encoder)。

  4. 创建 资产包(请参阅 为 C++ 或 Java 构建),其中包含游戏资产,并由 Play Asset Delivery 使用。例如,您可以为每个关卡创建一个资产包,或为游戏的不同部分创建资产包。

  5. 在不同的资产包中,将 支持的后缀 添加到与包含文件使用的纹理压缩格式相对应的纹理目录名称。

    创建一个名称中没有后缀的目录(例如,common/src/main/assets/textures/)。在此目录中,放置纹理资产的默认格式。此默认格式应受大多数设备支持(例如,ETC1 或 ETC2)。如果设备不支持其他指定的格式(例如,下表中的 PVRTC 和 ASTC),则 Google Play 商店将安装此目录。

    目录之前 目录之后
    common 资产包
    common/build.gradle
    common/src/main/assets/textures/...
    common 资产包
    common/build.gradle
    common/src/main/assets/textures/...
    common/src/main/assets/textures#tcf_astc/...
    common/src/main/assets/textures#tcf_pvrtc/...
    level1 资产包
    level1/build.gradle
    level1/src/main/assets/textures/...
    level1 资产包
    level1/build.gradle
    level1/src/main/assets/textures/...
    level1/src/main/assets/textures#tcf_astc/...
    level1/src/main/assets/textures#tcf_pvrtc/...
    level2 资产包
    level2/build.gradle
    level2/src/main/assets/textures/...
    level2 资产包
    level2/build.gradle
    level2/src/main/assets/textures/...
    level2/src/main/assets/textures#tcf_astc/...
    level2/src/main/assets/textures#tcf_pvrtc/...
  6. 将 TCF 维度添加到 应用包元数据文件 (BundleConfig.json) 中。使用 TEXTURE_COMPRESSION_FORMAT 作为 value 字段

    {
      ...
      "optimizations": {
        "splitsConfig": {
          "splitDimension": [
          ...
          {
             "value": "TEXTURE_COMPRESSION_FORMAT",
             "negate": false,
             "suffixStripping": {
               "enabled": true,
               "defaultSuffix": ""
              }
          }],
        }
      }
    }
    

    suffixStripping.enabled 设置为 true 以从目录名称中删除后缀(例如,#tcf_astc),以便在生成资产包时进行操作。这使您的游戏能够从已知目录名称(例如,level1/assets/textures)读取文件。某些游戏引擎可以检测文件的格式,因此您的游戏可以不关心安装了哪种格式的纹理资产。

    suffixStripping.defaultSuffix 指定 bundletool 为运行 Android 5.0(API 级别 21)及更低版本的设备生成独立 APK 时的默认目录后缀。在前面的示例表中,纹理资产的默认版本安装在这些设备上;这在大多数情况下是预期行为。

  7. 构建应用包

    bundletool build-bundle --config=BUILD_CONFIG.json \
      --modules=level1.zip,level2.zip,common.zip,base.zip --output=MY_BUNDLE.aab
    

验证应用包的内容

如果您尚未下载,请从 GitHub 存储库 下载 bundletool

通过从应用包中构建 APK 并检查它们来验证输出应用包的内容

bundletool build-apks --output=APKS.apks --bundle=MY_BUNDLE.aab
zipinfo APKS.apks

输出应类似于以下内容

toc.pb
splits/base-master.apk
splits/base-armeabi_v7a.apk
splits/…
asset-slices/level1-astc.apk
asset-slices/level1-other_tcf.apk
asset-slices/level1-pvrtc.apk

这些名称表明 TCF 目标应用得当。如果您提取关卡 APK 的内容(例如,asset-slices/level1-astc.apk),您可以验证名为 textures 的目录只有一个

测试应用包

连接设备并安装适用的资产包

bundletool install-apks --apks=APKS.apks

此命令仅安装满足设备规范的资产包。这些规范包括 ABI、屏幕密度、语言和最适用的纹理压缩格式。此操作模拟了 Google Play 商店为已发布游戏所做的操作。

要验证是否安装了正确的资产包,请执行以下任一操作

  • 使用 bundletool extract-apks 命令将为您的设备安装的 apks 输出到目录中,然后检查此目录。

    1. 提取设备的规范

      bundletool get-device-spec --output=MY_DEVICE_SPEC.json
      
    2. 使用此设备规范运行 bundletool extract-apks

      bundletool extract-apks --apks=APKS.apks --device-spec=MY_DEVICE_SPEC.json \
          --output-dir out
      
    3. 列出 out 目录中的文件并验证是否安装了正确的资产包。资产包名称以纹理格式名称为后缀(例如,level1-astc.apk)。

  • 在游戏中添加日志语句,在加载纹理时输出纹理格式。

  • 生成一组测试纹理(例如,将特定格式的纹理替换为单一亮色)。运行游戏并验证它是否存在。

如果您的应用包含 on-demandfast-follow 资产包,请使用 资产传递的本地测试解决方案

纹理目录名称的支持后缀

Google Play 了解纹理目录名称中使用的以下后缀

  • #tcf_astc 用于自适应可伸缩纹理压缩 (ASTC)
  • #tcf_atc 用于 ATI 纹理压缩 (ATC)
  • #tcf_dxt1 用于 S3 DXT1 纹理压缩 (DXT1)
  • #tcf_latc 用于亮度-alpha 纹理压缩 (LATC)
  • #tcf_paletted 用于通用调色板纹理压缩
  • #tcf_pvrtc 用于 PowerVR 纹理压缩 (PVRTC)
  • #tcf_etc1 用于爱立信纹理压缩 (ETC1)
  • #tcf_etc2 用于爱立信纹理压缩 2 (ETC2)
  • #tcf_s3tc 用于 S3 纹理压缩 (S3TC)
  • #tcf_3dc 用于 ATI 3Dc 纹理压缩 (3Dc)

Google Play 提供规则

Google Play 会检查设备使用的 OpenGL 扩展字符串以及设备支持的 OpenGL 版本。 Google Play 使用此信息来确定从 Android App Bundle 向设备传递的正确纹理格式。

Google Play 会传递 **第一个** 格式,按以下表格中列出的顺序,该格式受设备支持。

如果 App Bundle 中的任何纹理格式都不受设备支持,则 Google Play 会传递打包在 **默认格式** 中的纹理格式。(除非您针对特定设备硬件,否则 ETC1 或 ETC2 是默认格式的不错选择。)有关如何将资产打包在默认格式中的信息,请参阅 使用 bundletool使用 Google Play Unity 插件

如果资产未以默认格式打包,则 Google Play 会将应用标记为不适用于该设备。在这种情况下,用户将无法下载该应用。

格式(在 tcf_xxxx 中指定) 在具有 OpenGL 扩展字符串的设备上支持
astc GL_KHR_texture_compression_astc_ldr
pvrtc GL_IMG_texture_compression_pvrtc
s3tc GL_EXT_texture_compression_s3tc
dxt1 GL_EXT_texture_compression_dxt1
latc GL_EXT_texture_compression_latc
atc GL_AMD_compressed_ATC_texture
3dc GL_AMD_compressed_3DC_texture
etc2 不适用。设备必须支持 OpenGL ES 版本 3.0 或更高版本。
etc1 GL_OES_compressed_ETC1_RGB8_texture
调色板 GL_OES_compressed_paletted_texture