Избранное Все заметки GitHub icon Мой Github Обо мне

Избранное

Unity Mobile Input

Выложил на Github плагин нативного ввода для мобильных устройств. Это небольшая надстройка над стандартным Unity InputField UI. Работает на iOS и Android.

Зачем он нужен:

  1. Нативное поле ввода и клавиатура
  2. Отсутствует раздражающее дополнительное поле над клавиатурой в Android
  3. Для iOS можно добавить кнопку «Готово»
  4. Настройка кнопки Return для клавиатуры: стандартная, далее, готово, поиск
  5. Для работы нужно просто повестить скрипт MobileInput на родной InputField UI

До этого, было временное решение, которое не обладало необходимыми опциями, да и ошибки были. Собственно, на то оно и временное :) Но в Unity до сих пор нет таких возможностей для ввода, поэтому пришлось сделать такой плагин.

В моей версии исправлены ошибки и отрефакторен почти весь код от старого и неподдерживаемого плагина UnityNativeEdit.

Исходники на Github

10 апреля   android   git   ios   plugin   unity   интерфейс   проекты

Unity на Linux

Оно работает!

Всмысле не оболочка Unity, а редактор Unity :)
На выходных решил проверить, как там Ubuntu и всё «вот это вот с ней»... Оказалось, что всё хорошо-то. Всё работает, обновляется, есть куча разного софта, Unity в конце концов.

Последний раз я ставил какой-то Linux года 3-4 назад, но как-то не пошло. Даже не знаю почему. Потом пересел на MacOS и забыл про Linux и Windows. И вот — Ubuntu 16.04 LTS. Полёт нормальный. Оборудование на ноутбуке определилось, тачпад, камера, звук и т. д. Конечно, я сразу загуглил «ubuntu после установки» и проделал несколько настроек, но в целом — всё норм. Почему Ubuntu? Я не разбираюсь в 100500 дистрибутивах, да и не охота как-то, поэтому выбрал, тот что на слуху.

Так вот, по поводу Unity. Оказывается сборки под линукс делаются регулярно. И последняя версия которую я скачал и установил Unity-2017.3.0p2, вполне себе работает. Правда, я пока ничего не пробовал на ней сбилдить, но обязательно проверю :) Версия под линукс всё ещё в бете, поэтому возможны косяки. Но я к этому готов.

VSCode тоже есть под линукс и отлично работает. Думал ещё SourceTree поставить, но разработчики не планируют её выпускать для линукса. Жаль конечно, но на форуме где обсуждалось (и осуждалось) это решение, я нашёл пост про GitKraken. И, он очень даже хорош! Также есть аналог маковского homebrew — linuxbrew.

В общем, линукс — это не страшно и не больно :_) На нём даже игры делать можно...

21 января   linux   unity   инструменты   мысли

Библия free2play

Почитал библию free2play. Определенно есть полезная информация. Для тех кто только начинает делать игры — must read, потому что там не только про f2p. Например:

  • с чего начать
  • основное правило: LTV должен быть больше CPI
  • «дефолтные» показатели ретеншн и монетизации
  • игровой цикл: action → reward → investment → trigger
  • игровые сессии и их продолжительность
  • экономика игры

Главы небольшие, читается легко + в каждой главе есть ссылки на другие интересные и полезные статьи, книги и видео на Youtube.

Кликабельные ссылки в TextMeshPro

Как сделать чтобы ссылки в тексте, стали «ссылками» и при клике на них открывалась страница? Просто! Достаточно открыть примеры из TextMeshPro (TMP) и добавить немного кода.

Пускай у нас есть компонент ТМР с текстом, в котором встречаются ссылки на страницы в сети, например: http://mopsicus.ru, www.unity3d.com, и другие. И нужно чтобы это выглядело примерно так, и было кликабельно:

Для этого, находим ссылки в тексте с помощью регулярного выражения, сокращаем их (по желанию) и приводим к формату для обработки ТМР.

// Check links in text
void CheckLinks () {
	Regex regx = new Regex ("((http://|https://|www\\.)([A-Z0-9.-:]{1,})\\.[0-9A-Z?;~&#=\\-_\\./]{2,})" , RegexOptions.IgnoreCase | RegexOptions.Singleline); 
	MatchCollection matches = regx.Matches (textMessage.text); 
	foreach (Match match in matches) 
		textMessage.text = textMessage.text.Replace (match.Value, ShortLink(match.Value));     	
}

// Cut long url
string ShortLink (string link) {
	string text = link;
	int left = 9; 		
	int right = 16; 		
	string cut = "..."; 	
	if (link.Length > (left + right + cut.Length)) 
		text = string.Format ("{0}{1}{2}", link.Substring (0, left), cut, link.Substring (link.Length - right, right));
	return string.Format("<#7f7fe5><u><link=\"{0}\">{1}</link></u></color>", link, text);
}

Как установить подчеркивание, цвет текста и другое смотрите в документации ТМР. Осталось повесить обработчик нажатия и определить на какую ссылку кликнули:

// Get link and open page
public void OnPointerClick (PointerEventData eventData) {
	int linkIndex = TMP_TextUtilities.FindIntersectingLink (textMessage, eventData.position, eventData.pressEventCamera);
	if (linkIndex == -1) 
		return;
	TMP_LinkInfo linkInfo = textMessage.textInfo.linkInfo[linkIndex];
	string selectedLink = linkInfo.GetLinkID();
	if (selectedLink != "") {
		Debug.LogFormat ("Open link {0}", selectedLink);
		Application.OpenURL (selectedLink);        
	}
}

Исходник на Github

Emoji в Unity

Обновлено 1.03.2017
TextMeshPro теперь входит в состав Unity и стал бесплатным!

Как добавить поддержку эмоджи в Unity? К сожалению, решения из коробки нет. Но есть самый лучший ассет для работы с текстом Text Mesh Pro. Да, он не дешевый, но он этого стоит. Может когда-нибудь Unity его купит, и это будет стандартным решением, как они сделали с Anima2D.

В версии TMP, которая скоро должна пойти в релиз, разработчик добавил поддержку Emoji. Точнее, можно делать свои атласы, юникод эмоджи будет автоматически заменяться на картинку. Чтобы были доступны все эмоджи, нужен большой атлас. Я использовал EmojiOne. Также понадобится одна отличная программа — TexturePacker и расширение для Unity TexturePacker Importer.

  1. Загружаем все эмоджи в программу и экспортируем в формат Unity — Texture2D sprite sheet.
  2. После этого меняем Data format на JSON (Array) и делаем экспорт еще раз.
  3. Теперь полученные три файла: Emoji.png, Emoji.json и Emoji.tpsheet загружаем в Unity.

Осталось с помощью TMP Sprite Importer сгенерировать атлас для Text Mesh Pro. В результате, на iOS и Android можно использовать эмоджи.

Бесконечный скроллинг в Unity

Когда делаешь игру под мобильную платформу, пусть Android или iOS, иногда хочется использовать стандартные для этой платформы компоненты, потому что они проверены, оптимизированы и быстро работают. Например, списки. Кто делал в Unity интерфейс с большим количеством прокручивающихся элементов в Scrollrect, наверняка замечали, что после определенного количества, вся эта конструкция начинает притормаживать. Наиболее оптимально использовать т. н. data driving подход, когда количество видимых элементов не меняется, а данные подгружаются динамически в нужную ячейку.

Для одного проекта понадобилось сделать списки друзей и лидеров. Количество пользователей сотни тысяч. Естественно, создавать такое количество ячеек было бы неверно. Быстрый поиск показал несколько рабочих вариантов, но после тестов на реальных устройствах, при быстрых прокрутках лаги все равно были заметны и еще по ряду причин не подошли. Последний найденый вариант лег в основу и после доработок, отлично функционирует.

Была сделана такая знакомая мобильным разработчикам функция, как pull-to-refresh, также, ячейки можно настраивать как угодно под ваш проект, нужное количество создается автоматически в зависимости от высоты экрана. Единственное ограничение — высота ячейки не динамическая.

Использовать очень легко — все делает один скрипт, вещаем его на Scrollview настраиваем параметры и коллбеки и все. Демо есть. Если будут какие-то вопросы и пожелания, не стесняйтесь писать. Удачи!

Ссылка на Github

Unity share плагин для iOS и Android

Каждый разработчик пытается автоматизировать процесс. В результате, создаются собственные движки, врапперы, надстройки, «велосипеды» и т. д... Так и получилось, нужно было сделать более-менее универсальный способ шаринга в Unity проекте для андроидов и айфонов, в результате написал эти два плагина. Теперь запросы типа: «как расшарить из Unity», «unity share plugin», «как отправить вконтакте из unity» должны вести сюда :)

Использовать очень просто. Есть небольшие заморочки с настройкой проекта под iOS, но это делается один раз и автоматически, с помощью обработки проекта после билда. Можно добавлять, изменять и использовать как угодно, все исходники и пример на Github.

В данный момент можно шарить в:

  • ВКонтакте
  • Facebook
  • Twitter
  • Одноклассники
  • WhatsApp
  • Viber
  • Telegram

Интерфейс в Unity выглядит так:

// vk, fb, ok, tw, wa, vb, tg
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

public class Sharing : MonoBehaviour {

	#if UNITY_IOS
		[DllImport ("__Internal")]
		private static extern void shareVia (string app, string message, string url, string param);
	#endif

	// Поделиться 
	public static void ShareVia (string app, string message, string param = "") {
		#if UNITY_ANDROID
			message = string.Format ("{0} {1}", message, "http://my.url.com"); // добавление ссылки
			using (var plugin = new AndroidJavaClass("com.mycompany.sharing.Plugin")) {
				plugin.CallStatic("shareVia", app, message);
			}
		#elif UNITY_IOS
			shareVia (app, message, "http://my.url.com", param);
		#endif
	}
		
	// Не удалось расшарить
	void OnShareError (string result) {
		switch (result) {
			case "NotInstall":
				// приложение не установлено
				break;
			case "NotAvailable":
				// шаринг не доступен
				break;
			case "AccessDenied":
				// нет доступа
				break;
			default:
				// не удалось расшарить текст
				break;
		}
	}
}

Например, чтобы расшарить в Facebook, достаточно вызвать:

Sharing.ShareVia ("fb", "Hi from Unity");

Для работы некоторых API, нужны ID приложений. Чтобы их получить, необходимо создать приложения для этих соц. сетей, как это сделать обычно написано в специальном разделе для разработчиков на сайте. Например, как для ВКонтакте.

Внутренности каждого плагина тут расписывать не буду, исходники все доступны, если будут вопросы пишите — почта внизу страницы.

Исходник на Github

2016   android   ios   plugin   unity

Настройка Sublime Text для Unity

Эта заметка больше для разработчиков использующих OS X (Mac OS уже). Потому как, разница между Visual Studio и MonoDevelop, просто разительна, как и скорость работы.

Но есть отличное решение — Sublime Text (ST). Установив несколько плагинов, и без того быстрый и многофункциональный ST, прекрасно подойдет для разработки на C# в Unity. После настройки, вы получите:

  • Автодополнение кода — autocompletion
  • Переход к функции — go to definition
  • Подсветка ошибок и синтаксиса
  • Сворачивание фрагментов кода — code folding
  • XML документирование

Установка Mono

Сначала нужно поставить Mono — open-source реализация .NET фреймворка, он нужен для компиляции и запуска C# скриптов на маке. Его можно установить, например, с помощью Brew:

brew install mono

Установка Package Control для Sublime

Если в вашем ST еще не установлен пакетный менеджер, то сделать это очень просто: нужно открыть консоль внутри ST, вставить кусок кода и нажать Enter.

Установка Omnisharp

Основной плагин который позволит ST работать с C#. Для установки: открывает пакетный менеджер (cmd+shift+p → Install Package), ищем Omnisharp и устанавливаем.

Настройка проекта

Теперь надо создать Sublime Project и связать его с Unity проектом. Для этого: открываем в ST корневую папку с проектом и идем в меню Project → Save Project As.... Сохраняем файл проекта рядом с *.sln файлом для удобства.

Открываем только что созданный файл MyProject.sublime-project в текстовом редакторе, можно в том же ST и редактируем его:

{
    "folders":
    [
        {
            "path": ".",
            "file_exclude_patterns": ["*.meta"],
        },
    ],
    "solution_file": "./MyProject.sln"
}

Тут немного объясню:

  • path — путь до папки со скриптами, в данном случае корневая папка, но вы можете написать например Scripts, или ту где у вас лежат скрипты
  • file_exclude_patterns — файлы с какими расширениями не показывать в проекте
  • solution_file — самое главное, тут должно быть название вашего SLN файла проекта

Устанавливаем плагины

Для документирования устанавливаем плагин — XmlDocs, для сворачивания кода — SyntaxFold. Устанавливаем также, через пакетный менеджер. Чтобы задокументировать код: вводим три слеша и жмем табуляцию — (/// + tab). А для настройки сворачивания кода, после установки плагина, нажмите shit+f5 и выберите C#.

Последние штрихи

Чтобы автодополнение кода работало не только по ctrl+space, а каждый раз когда вы ставите точку, в конфиг надо добавить правки. Убедитесь, что у вас открыт C# файл, и выбран синтаксис C# (в правом нижнем углу), далее идем в меню: Sublime Text → Preferences → Settings — More → Syntax Specific — User и вставляем этот код:

{
    "auto_complete": true,
    "auto_complete_selector": "source - comment",
    "auto_complete_triggers": [ {"selector": "source.cs", "characters": ".<"} ],
 }

Если вдруг автодополнение кода не заработало, перезагрузить ваш SLN файл: жмем cmd+shift+p, и выбираем OmniSharpSublime: Reload Solution.

Еще момент: это настройка ST для редактирования уже имеющего проекта. И мы не делаем его редактором по-умолчанию. Т. е. если вы откроете файл двойным кликом из Unity, то Omnisharp сервер не запустится. Для «корректного» открытия проекта, нужно в ST выбрать меню: Project → Open Project... и выбрать MyProject.sublime-project файл.

Готово

Теперь можно быстро и комфортно писать C# код для Unity на маке. Конечно, можно поставить еще плагинов, тут уже на ваше усмотрение. Например, есть плагин со сниппетами для C#, ищется в пакетном менеджере по словам C# Snippets.

Только для любителей Sublime Text :) Удачи!

Оптимизация 2D игр на Unity

Этот пост будет периодически дополняться и обновляться.
Обновлено 05.10.2017

На Youtube куча уроков по созданию простейших 2D игр на Unity. Реально, сделать неплохой платформер можно за день, при наличии опыта и готовых ассетов. Но многие начинающие игроделы сделав проект и протестировать его на ПК, с ужасом наблюдают как их творение тормозит на мобильном устройстве.

В мануалах, что встречаются в сети, большинство советов собрано к версии Unity 4.6+, кроме того, они почти все на английском, что для некоторых является преградой. В этом посте, я постарался собрать те моменты, которые помогли мне избавится от лагов на iOS и Android. Но важно понимать — не все можно решить лишь настройками, очень важна и архитектура приложения, и подготовленные текстуры, и знание более оптимальных алгоритмов.

Что нужно предпринять, чтобы повысить производительность, поднять FPS, снизить CPU?

1. Кешируем все!

Все, что будет использоваться больше одного раза лучше закешировать. Операции типа GameObject.Find() или GetComponent() достаточно ресурсозатратны, а если они вызываются где-нибудь в цикле или в Update (), то производительность может упасть.

void Awake () {
	_cachedTransform = transform;
}
 
void Update () { 
	_cachedTransform.localPosition = _newPosition;		
}

Не используйте Resources.Load () каждый раз когда нужно в рантайме загрузить что либо, это тоже дорогая операция. Лучше при старте закешировать и работать с кешем. Ну и конечно, объединяйте в префабы.

2. Настройки графики

В Unity есть 6 стандартных пресетов настройки качества графики. Но так как мы говорим про оптимизацию для 2D и для мобильных устройств, то все что выше Simple нет смысла ставить. Конечно, если у вас есть какие-то специфические моменты, частицы, и т. д., то с параметры можно поэкспериментировать найдя оптимальный баланс.

3. Используем FrameRate

По-умолчанию FrameRate равен 30. И зачастую этого бывает достаточно. Но например, при создании UI где есть прокручивающие списки и движущие элементы, может появится дрожание или шлейф. Я это заметил при тестировании на iPhone, поэтому вручную повысил FrameRate. А понижая FrameRate на сценах или игровых меню, где ничего не двигается, можно значительно снизить CPU, а следовательно продлить жизнь батарее устройства. Пользователи не любят когда игра сжирает аккумулятор за час.

public int _frameRate = 60;

void Start () {
	#if UNITY_IOS
		QualitySettings.vSyncCount = 0;
	#endif
}

void Update () {
	#if UNITY_IOS
		if (_frameRate != Application.targetFrameRate)
			Application.targetFrameRate = _frameRate;
	#endif
}

4. Атлас текстур

Определенно, нужно упаковывать все свои спрайты в атласы. Для этого есть как встроенный инструмент Sprite Packer, так и продвинутый TexturePacker. Для работы встроенного упаковщика, достаточно задавать теги в настройках импорта, объединяя текстуры и спрайты по смыслу и месту использования.

Таким образом уменьшается количество вызовов отрисовок ваших спрайтов. Проверить как идет отрисовка можно с помощью встроенного инструмента Frame Debugger.

5. Используем пул объектов

GameObject.Instantiate () — очень дорогая операция! Если есть возможность не использовать ее в процессе игры — не используйте. Для большого количества однотипных объектов надо использовать пул объектов. Один раз инициализировав определенное количество, например пуль, они будут использоваться снова и снова, вместо создания и уничтожения каждый раз. Урок по пулу объектов и готовый шаблон, для начала будет достаточно.

6. Меньше Update — больше событий

Метод Update () вызывается каждый кадр, FixedUpdate () в некоторых случаях еще чаще. Если у вас в этих местах происходит много различных проверок и действий, это можно сказаться на производительности. Я использую событийную модель: вместо проверки условия в Update (), когда происходит какое-либо действие, отправляется событие, а в нужном месте это событие «слушается» и обрабатывается в зависимости от переданных параметров. Для этого можно использовать менеджер событий о котором я писал ранее.

7. Выключаем неиспользуемые компоненты

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

8. Настройки билда

Билд под конкретную платформу, тоже можно оптимизировать. Например, если акселерометр не используется в игре, его лучше вообще отключить. Кроме того, я не использую автовыбор графического API, а задаю его сам убирая все остальные, опять же, если вы не используете какие-то специфические функции из OpenGLES 3.0, а так второй версии вполне хватает, она уже давно протестирована. Включаем статичный и динамический батчинг, а для Android многопоточный рендеринг. Для iOS включаем Script Call Optimization в Fast but no Exceptions, но тут момент — если будет какое-то исключение, то игра крашится.

9. Используем Profiler

Не стоит обделять вниманием профайлер. Обязательно протестируйте свою игру и посмотрите, в какие моменты идет максимальная нагрузка. Эти места нужно оптимизировать в первую очередь. Большинство ответов можно найти на stackoverflow.com или форуме Unity, просто загуглив название метода который тратит больше всего ресурсов.

10. Использование материала для UI Image и SpriteRenderer

Если у вас сложный интерфейс и много компонентов, особенно UI Image, то они существенно будут влиять на FPS. Чтобы повысить производительность, улучшить плавность анимаций, можно применить такой хак: там где вы не используете маски, у картинок нужно задать материал Sprites-Default. Если это сделать вместе с маской, то маска не сработает и получите примерно такой варнинг:

Material Sprites-Default doesn’t have _Stencil property

Чтобы убрать эту ошибку нужно немного изменить стандартный шейдер, сделать новый материал и применить его там где есть маска, тогда все сработает. Ссылка на измененный шейдер.
Цена плавности — повышение CPU :(

11. Уменьшаем размер текстур

Отличная утилита которая позволяет снизить потребления памяти для текстур до 3х раз. Как это работает и ссылка на Github, в статье на Хабре.

12. Практическое руководство по оптимизации Unity игр

Подойдет как для 2D, так и для 3D. Много полезной информации которую в документации вряд ли найдешь. Тут и про инструменты, и про опыт. Рассказывает специалист по эксплуатации из Unity Technologies — очень интересно. Узнал про memory profiler и то, что Camera.main не закеширована О_О. Обязательно смотреть всем.

13. Используем оптимизированный код

Снова хочется посоветовать набор оптимизированных скриптов от Leopotam. Коллекции, сериализация, векторы и многое другое. Настоятельное рекомендую, к изучению и использованию.

14. Используем одинаковые материалы

Если на объектах стоят разные материалы, они не будут батчится и будет больше вызовов отрисовки. Соответственно, нужно по возможности использовать как можно меньше разных шейдеров и материалов. Для понимания, как работает начальная оптимизация графики 2D, на Lynda.com есть небольшой курс.

15. Размеры атласов и спрайтов

Для применения сжатия спрайтов на мобильных устройствах нужно использовать атласы с размерами кратными степени 2, т. е. 1024х1024, 2048х2048.

16. Используйте UI Profiler

В Unity 2017 появился UI Profiler! Крутая штука, теперь можно выяснить почему в интерфейсе много вызовов отрисовки, увидеть какие материалы на объектах и всё это оптимизировать. Особенно актуально, если у вас сложные интерфейсы со множеством элементов, которые например, прокручиваются в ScrollRect.

Пока все. Тут не было про физику, потому что пока я ее не использовал. Возможно, в будущем добавлю. Пишите в комментариях ваши проверенные способы оптимизации для 2D игр под Unity.

Unity iOS plugin

Так как тема мобильной разработки на Unity для меня сейчас актуальна, то продолжу про создание плагинов. Как сделать свой плагин для iOS? Что можно делать с помощью нативных плагинов? Например, показать стандартный ActionSheet или отобразить HTML, или синхронизироваться с iCloud, или получить доступ к галереи картинок опять же... Чтобы сделать простой плагин и xCode не обязателен, на самом деле.

Для примера, сделаем плагин который отображает браузер и загружает какую-нибудь страницу внутри Unity приложения. Плагин будет состоять всего из одного файла, с расширением .mm. В документации есть список форматов и описание сборки под iOS.

Для начала, объявим главный контроллер Unity приложения:

extern UIViewController *UnityGetGLViewController();

Для примера будет достаточно трех функций: показа браузера, загрузки страницы, скрытия браузера.

- (void)showBrowser {
    CGRect screenRect = [[UIScreen mainScreen] bounds];
    webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, screenRect.size.width, screenRect.size.height)];
    [UnityGetGLViewController().view addSubview:webView];
}

- (void)loadUrl:(NSString *)url {
    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
}

- (void)close {
    [webView removeFromSuperview];
    webView = NULL;
}

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

Ну и последнее, интерфейс доступа на C:

static Browser *browser = NULL;

extern "C" {
    
    void browserLaunch () {
        if (browser == NULL)
            browser = [[Browser alloc] init];
        [browser showBrowser];
    }
    
    void browserLoadUrl (const char* url) {
        [browser loadUrl:[NSString stringWithUTF8String:url]];
    }
    
    void browserClose () {
        [browser close];
    }
    
}

Это все, плагин готов! Теперь осталось вызывать эти функции из Unity. По аналогии с Android плагином, делаем класс для браузера. Все функции подключаем через [DllImport («__Internal»)], должно получиться что-то такое:

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

public class Browser : MonoBehaviour {

	#if UNITY_IPHONE
		[DllImport ("__Internal")]
		private static extern void browserLaunch (int left, int right, int top, int bottom);

		[DllImport ("__Internal")]
		private static extern void browserLoadUrl (string url);

		[DllImport ("__Internal")]
		private static extern void browserClose ();
	#endif 

	// Показываем браузер
	public static void Launch (RectOffset offset) {
		#if UNITY_IPHONE
			browserLaunch (offset.left, offset.right, offset.top, offset.bottom);
		#endif
	}

	// Открыть страницу
	public static void LoadUrl (string url) {
		#if UNITY_IPHONE
			browserLoadUrl (url);
		#endif
	}

	// Убрать браузер
	public static void Close () {
		#if UNITY_IPHONE
			browserClose ();
		#endif
	}
}

Тут нужно уточнить, что браузер будет добавлен на главный экран, поверх всего. Поэтому, нужно правильно высчитать отступы, чтобы все выглядело красиво. В примере значения отступов грубо прописаны в коде, так делать не стоит :)

Исходники на GitHub

2016   ios   plugin   unity
Ранее Ctrl + ↓