Информационный портал по безопасности » Программирование » Веб-разработка » Уменьшение размера файла сборки Android в Unity

 

Уменьшение размера файла сборки Android в Unity

Автор: admin от 14-08-2017, 07:50, посмотрело: 900

Размер сборки — важная характеристика мобильного приложения. Если приложение весит много, оно первым будет удалено при чистке. Также меньший размер может ускорить запуск, установку, скачивание.



Даже пустой проект в Unity весит очень много. Пустой проект под Android с настройками по умолчанию в Unity 2017.1 весит 21637 КБ. Однако его можно очень легко уменьшить до 1195212412 КБ, указав платформу для компиляции (ARMv7 и x86 соответственно).



По аналогии с этим, можно еще попробовать еще немного уменьшить вес, выбрав Graphic API. Если выбрать OpenGLES2 вместо Auto Graphics API, можно сэкономить еще 236 КБ (11716 вместо 11952). Выгода незначительна и возможна потеря в производительности, так что этого делать я не рекомендую.



Теперь поговорим о содержимом проекта. Рассмотрим 2D игру с большим количеством спрайтов.

Есть вероятность, что многие спрайты будут симметричными по одной или нескольким осям.



Давайте проверим, есть ли автоматическое сжатие на этот случай: скомпилируем сцену с выставленным Sprite Renderer с одной текстурой, например, этой.



Уменьшение размера файла сборки Android в Unity


ARMv7 билд увеличился с 11952 КБ 12046 КБ, прибавка от пустого билда составляет 94 КБ.



Теперь подготовим половину текстуры:



Уменьшение размера файла сборки Android в Unity


Поставим два Sprite Renderer с одинаковой позицией, у правого выставим Flip X для отзеркаливания, в настройках Sprite Import Settings укажем Pivot Right для совмещения зеркальных половин. Должен получиться такой же круг как и был раньше. Скомпилируем, посмотрим размер: 12000 КБ, то есть прибавка почти в два раза меньше (48 КБ против 94). Если и есть какое то специальное сжатие, то по умолчанию оно неэффективно.



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



Найдем стандартный шейдер Unity для Sprite Renderer.





Начнем с того, что убедимся, что все работает. Создаем шейдер, копируем в него код, меняем название в коде на Sprites/HorizontalSymmetry. Теперь нужно создать материал и выбрать наш шейдер. Попробуем назначить на Sprite Renderer наш материал. Должен выглядеть как раньше.



Теперь разберем шейдер. Вся магия происходит тут:



fixed4 frag(v2f IN) : SV_Target
{
//tex2D возвращает цвет текстуры с заданными координатами.
//_MainTex - используемая текстура, IN.texcoord - текущие координаты в формате 0..1
      fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
//Умножение r,g,b полученного цвета на a. 
      c.rgb *= c.a;
      return c;
}


Это функция, которая должна вернуть цвет пикселя в указанной точке. Нам даны позиция, текстура, цвет для смешения. Не стоит бояться fixed4: это просто тип данных с 4 float: r,g,b,a.



В первой строчке мы получаем цвет текстуры и после этого умножаем на некий цвет IN.color. Этот цвет — это параметр шейдера, его можно изменить в Sprite Renderer/Color.



Дальше идет домножение цвета на альфу. Связано это с тем, что прозрачность зависит не только от альфы, но и от значения rgb. Для лучшего понимания цветового пространства можно поэкспементировать:



fixed4 frag(v2f IN) : SV_Target
{            	            	            	
      fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
      c.rgb=0.5;
      c.a = 0.0;
      return c;
}


Получаем прозрачную серую текстуру. При rgb = 1 и a = 0 будет непрозрачная белая, при rgb = 0 и a = 0 полностью прозрачная, rgb = 0 и a = 1 будет черным непрозрачным.



Если все получилось, то попробуем теперь сделать горизонтальное отзеркаливание. Нам необходимо изменить эту функцию так, чтобы в первой половине изображения была помещена вся текстура, во второй, та же самая текстура, но отзеркаленная.



Эту задачу можно решить в лоб:



fixed4 frag(v2f IN) : SV_Target
{
//Запоминаем в новой переменной координаты пикселя
      fixed2 nIn = IN.texcoord;
//Приводим их к форме 0...2
      nIn.x = nIn.x*2;
//Если значение больше одного, то дает 1..0
      if (nIn.r>1)
            nIn.r = 2-nIn.
//Используем новые координаты текстуры
      fixed4 c = tex2D(_MainTex, nIN.texcoord) * IN.color;
//Умножаем r,g,b полученного цвета на a. 
      c.rgb *= c.a;
      return c;
}


Если немного подумать, то можно сделать решение короче, красивей, быстрей:



fixed4 frag(v2f IN) : SV_Target
{            	
//Получаем необходимые координаты
      IN.texcoord.x = 1-abs(2*IN.texcoord.x-1);
//Получаем цвет по новым координатам
      fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
//Умножаем r,g,b полученного цвета на a. 
      c.rgb *= c.a;
      return c;
}


В качестве упражнения предлагаю читателю решить эту задачу для вертикальной и двойной симметрии.



Иногда могут быть артефакты прозрачности, связанные с тем, что спрайт рисуется по умолчанию по определенному контуру (мешу). Лечится так: Sprite/Import Settings/Mesh type = Full Rect.



Этот способ уже теоретически способен сократить размер используемых текстур в 4 раза. Проверим, как поведет себя билд при четверти спрайта (используя шейдер двойной симметрии). Размер билда — 11978 КБ против 12000 (половина спрайта). Напомню, что пустой проект весил 11952 КБ. То есть, опять получилось уменьшение прибавки почти в два раза (в 3.6 от изначального круга без оптимизации).



Однако, это не предел. В моей игре использовалось большое количество шайб, обладающей радиальной симметрией. Это означает, что достаточно иметь всего одну полоску, для того чтобы задать весь круг! Причем, половинную полоску (радиус, не диаметр).



Подготовим текстуру:



Уменьшение размера файла сборки Android в Unity
Теперь дело за шейдером. Наша задача — сделать из линии круг.



Можно поступить так: найти удаленность текущей точки от центра и использовать координату текстуры с позицией (1-distance*2, 0). Умножение на два происходит потому что максимальное расстояние от центра будет 0.5, не 1. Вычитаем из единицы потому что текстура подготовлена слева (край круга) направо (центр круга).



Пример реализации:



fixed4 frag(v2f IN) : SV_Target
{            	
      fixed2 nIn = IN.texcoord;
      nIn.x = nIn.x-0.5;
      nIn.y = nIn.y-0.5;
      float dist = sqrt(nIn.x*nIn.x+nIn.y*nIn.y);            
      IN.texcoord.x = 1-dist*2;
      fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
      c.rgb *= c.a;
      return c;
}


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



Создаем материал, поставим его в Sprite Renderer, поставим Sprite = line, смотрим. Изображение будет очень узким, так что нужно растянуть спрайт (выставить большое значение Trasnform.Scale.y). Должен получиться исходный круговал.



Проверим размер билда с новым шейдером и кругом из полоски: получилось 11957 КБ. То есть прибавка от пустого проекта составляет всего лишь 5 КБ, и это включая размер шейдера.



В итоге у нас получился удобный инструмент, с помощью которого в некоторых случаях можно значительно уменьшить размер билда. Он пригоден не только для Android, но и для любой платформы поддерживающей шейдеры.

Источник: Хабрахабр

Категория: Веб-разработка / Android

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
  • bowtiesmilelaughingblushsmileyrelaxedsmirk
    heart_eyeskissing_heartkissing_closed_eyesflushedrelievedsatisfiedgrin
    winkstuck_out_tongue_winking_eyestuck_out_tongue_closed_eyesgrinningkissingstuck_out_tonguesleeping
    worriedfrowninganguishedopen_mouthgrimacingconfusedhushed
    expressionlessunamusedsweat_smilesweatdisappointed_relievedwearypensive
    disappointedconfoundedfearfulcold_sweatperseverecrysob
    joyastonishedscreamtired_faceangryragetriumph
    sleepyyummasksunglassesdizzy_faceimpsmiling_imp
    neutral_faceno_mouthinnocent