пятница, 14 марта 2014 г.

Сжатие текстур андроида в Unity

В айфонах почти все хорошо. Сделал приложение, потестил на паре девайсов (iphone 4s, ipad2) и можешь быть почти уверен, что с остальными не будет головной боли.

У андроида зоопарк устройств. Это приносит огромные проблемы. В первую очередь, с тестированием и оптимизацией. Далеко не факт, что оптимизация для одних девайсов положительно скажется для других девайсов тоже.

Поскольку очень много оптимизации связывается с отрисовкой, нужно уделить большое внимание отрисовываемым текстурам, а именно сжатию. Unity поддерживает несколько видов сжатия для разных видеокарт: ETC1, ATC, DXT, PVRTC, ETC2. Более того, можно не только руками для каждой текстуры выставлять желаемое сжатие, а задать AndroidBuildSubtarget - при билде все запакуется в лучшем виде.
Внимание, вопрос: в какой формат нужно запаковывать? Ответ: зависит от того, сколько APK вы хотите вылить в google-play.
Дело в том, что магазин приложений гугл поддерживает выливку нескольких билдов для разных девайсов. Это сделано специально для того, чтобы приложения поддерживало как можно больше устройств. Там можно задать разные apk для разных видеокарт, разных пропорций экранов, разных разрешений и т.д. Но есть одно большое НО: рекомендуется использовать эту возможность только тогда, когда есть полное понимание, что одним билдом не обойтись.  Потому что разработку нескольких билдов тяжело поддерживать, а также должна быть правильная систематизация версий, ведь в том же мануале сказано, что гугл отдает поддерживаемый билд с наивысшей версией. То есть, если видеокарта поддерживает etc билд и для нее предпочтительнее atc, но версия etc > версии atc, гугл отдаст etc билд.

Короче говоря, если вы не хотите запариваться, юзайте только один билд и сжимайте в etc. Конечно, вы немного потеряете в производительности (потому что не-etc видеокарты распаковывают etc-сжатую текстуру), но можете быть почти уверены, что ваши текстуры будут выглядеть нормально.
Что делать тем, кто хочет по максимуму: надо организовать правильную генерацию номера версии и в будущем всегда ее поддерживать. Я для себя поставил за правило следующую схему генерации версии:

0411079, или более наглядно 04 1 1079, где первые две цифры - min android sdk(04 - это что-то типа android froyo или еще древнее), второе число - это номер сжатия(об этом позже), а последние цифры - это номер версии билда, без точек(1.07.9).

Как правильно выбрать номер сжатия, чтобы приоритетный билд попал в нужное устройство. Надо исходить из логики: что реже используется, то и надо выше ставить приоритет. А статистика такова: etc > atc > dxt > pvrtc > etc2. Именно в таком порядке их можно и занумеровать.

А чтобы не мучиться каждый раз с генерацией билда и билд-номера, стоит завести один маленький скрипт:

[MenuItem("Custom/Build apks for all Texture Compressions")]
    private static void BuildApksForAllCompressions()
    {
        BuildAndroid(AndroidBuildSubtarget.DXT);
        BuildAndroid(AndroidBuildSubtarget.ATC);
        BuildAndroid(AndroidBuildSubtarget.PVRTC);
        BuildAndroid(AndroidBuildSubtarget.ETC);

        Debug.Log("Finished building");
    }

    //Version code looks like this: 04 1 1079
    //first two numbers = API LEVEL (04)
    //second two numbers = Android Subtarget (1)
    //Last numbers = version number, revision (1079, got from "1.07.9")
    private static int GenerateVersionCode(AndroidBuildSubtarget target)
    {
        string number = string.Format("{0}{1}{2}", ((int) PlayerSettings.Android.minSdkVersion).ToString("D2"),
            GetTargetVersion(target), PlayerSettings.bundleVersion);
        number = number.Replace(".", "");
        int result = int.Parse(number);
        Debug.Log("Android " + target + " version: " + number + ", " + result);
        return result;
    }

  //all devices support etc, so it has lowest value
    private static int GetTargetVersion(AndroidBuildSubtarget target)
    {
        switch (target)
        {
            case AndroidBuildSubtarget.ETC:
                return 4;
            case AndroidBuildSubtarget.ATC:
                return 5;
            case AndroidBuildSubtarget.DXT:
                return 6;
            case AndroidBuildSubtarget.PVRTC:
                return 7;
            default:
                return 0;
        }
    }

    private static void BuildAndroid(AndroidBuildSubtarget target)
    {
        if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android)
            EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTarget.Android);
        EditorUserBuildSettings.androidBuildSubtarget = target;

        //to include more build options use bitwise OR, for instance: BuildOptions options = BuildOptions.Development | BuildOptions.ShowBuiltPlayer;
        var bo = BuildOptions.None;
        PlayerSettings.Android.keystoreName = "keystore_location";
        PlayerSettings.Android.keyaliasName = "alias";
        PlayerSettings.Android.keystorePass = "password";
        PlayerSettings.Android.keyaliasPass = "password";

        PlayerSettings.bundleIdentifier = "yourbundle";
        PlayerSettings.Android.bundleVersionCode = GenerateVersionCode(target);

        try
        {
            string apkName = "your_apk_name"
            BuildPipeline.BuildPlayer(
                (from scene in EditorBuildSettings.scenes where scene.enabled select scene.path).ToArray(), apkName,
                BuildTarget.Android, bo);
        }
        catch (Exception e)
        {
            Debug.Log("Error: " + e.Message);
        }
    }
Как-то так. Да, при выливании в сторы, нужно заливать билды в том же порядке. Если перепутать, то стор просто отвергнет билд с меньшим номером.

Комментариев нет:

Отправить комментарий