Джойстик юнити для андроид

Как сделать джойстик в Unity 5

В этом уроке мы разберем, как сделать джойстик в Unity 5, работающий на всех платформах, включая Android и IOS. Для начала имеем: пустой проект и Canvas на нем. Если вы не знаете, что такое Canvas, или не разобрались в нем (например, не знаете как сделать масштабируемость интерфейса), то рекомендую посмотреть урок по созданию главного меню для того, чтоб лучше в этом разобраться.

Canvas мы переименуем как MobileInputCanvas и создадим на нем Image, который назовем JoystickHolder, выберем ему необходимый размер (изменив width и height свойства), а так же поставим его в нужное место. В Image мы выберем нужный спрайт, если у вас есть заготовки для спрайтов под джойстик и место, где от будет кататься, а если нет, то можете выбрать уже готовый круг. Для этого необходимо выбрать нужный нам объект Image и выбрать св-во Source Image. Далее в Unity уже есть картинка Knob, нужно выбрать именно её. Следующим мы создадим в JoystickHolder еще один Image, который назовем Joystick и проделаем с ним те же действия, но поменяем ему размер на более меньший. А так же нужно перекрасить наши круги в необходимые для вас цвета. В конце этих действий получиться что-то похожее на это:

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

Источник

H Делаем свой джойстик для Unity3D с батчингом и спрайтами в черновиках Tutorial

Некоторое время назад мне понадобился мобильный джойстик для управления персонажем. Посмотрев на стандартный джойстик из включенного в дистрибутивную версию Unity3D пакета я понял, что это не совсем то, что мне нужно.
Во-первых, там очень сильно закрученная и мудреная система вложенных друг в друга объектов.
Во-вторых, джойстик не «подскакивает» к пальцу при нажатии.
В-третьих, почему-то он ограничивается квадратом, а не кругом.
И в конце концов, у него нет красивой подложки «из коробки».

Чтобы не изобретать велосипед, решил поискать бесплатный джойстик в местном Asset Store. Меня очень удивило, если не сказать поразило, отсутствие бесплатных джойстиков. Из 40 найденных позиций были джойстики по 5-100 долларов, при этом, судя по рейтингам и комментариям, большинство из них работали очень криво. (Единственный бесплатный джойстик я нашел намного позже, но об этом подробнее дальше)

Я решил помочь себе и другим, сделав бесплатный джойстик без использования платных GUI библиотек вроде NGUI. Тем более у меня давно лежал пак экранных контроллеров от Kenny (изображение ниже) и нужно было срочно найти ему применение.

Какие спрайты и батчинг?

Unity3D версии 4.3 наделала много шума добавлением нативной поддержки разработки 2D игр. Одним из новых компонент являлся SpriteRenderer, который позволил с легкостью делать 2D игры без дополнительных библиотек. Однако основной его особенностью является то, что разные спрайты из одного атласа батчатся в 1 Draw Call независимо от относительного изменения размеров через Scale. В мобильной разработке принято экономить на Draw Call в любом месте, где это возможно и SpriteRenderer дает нам эту возможность — если упаковать все используемые контроллеры на экране в один атлас, то отрисовку всего интерфейса можно вместить в один Draw Call.

Проблема стандартного джойстика была еще и в том, что используемая для отрисовки GUITexture не батчится, на каждую GUITexture на экране тратится ровно 1 Draw Call.
Когда я таки нашел бесплатный джойстик в магазине, оказалось что он использует аналогичный подход. В общем я здорово загорелся идеей бесплатного джойстика из спрайтов с батчингом и принялся за дело.

Основная идея


Если разместить наш джойстик перед камерой на расстоянии, скажем, 0.5 юнитов, спрайты не будут «врезаться» в другие объекты сцены и все время будут на переднем плане. Вместе со SpriteRenderer пришла система Z-сортировки внутри определенных заранее слоев, но она распространяется только на сами спрайты и системы частиц (насколько я смог разобраться, поправьте если не прав).

Читайте также:  Удалил гугл сервис андроид

Этот же принцип, по-моему, используется в системах вроде NGUI (я не работал с ней, не могу сказать точно). В любом случае, картина получается следующая:

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

Окей, с этим понятно. Теперь к самому джойстику. Если вы играли в игры типа Dungeon Hunter 4, то замечали, что джойстик подпрыгивает в точку нажатия, и управление идет относительно этой точки. Причем есть «подложка» под джойстик и собственно сам джойстик.

Я собрал простой джойстик с «рабочей зоной». При нажатии на любую точку внутри зеленого коллайдера (простой Box Collider), джойстик должен прыгнуть туда и управляться относительно этой точки.


Объект состоит из трех элементов, главный компонент с Box Collider и два вложенных объекта со SpriteRenderer

Обычно проверка нажатия на каком-либо объекте в Unity3D производится с помощью Physics.Raycast(..). Из камеры пускается луч в точку нажатия, и проверяется, не попал ли этот луч в какой-либо объект — коллайдер. Плюсами такого подхода является то, что очень просто определить реальные (мировые) координаты точки нажатия, то есть перевести координаты экрана в координаты нашего сечения пирамиды. Однако я не хотел привязываться к этой системе потому-что:

  • Рейкастинг — это довольно дорогая операция
  • Не будет возможности найти координаты нажатия за пределами основного коллайдера

В итоге я решил использовать Physics.Raycast только для определения начального положения джойстика, а потом преобразовывать координаты касания на экране в координаты точки на сечении пирамиды и далее в локальные координаты джойстика. В целом логика ясна — проверяем, не нажали ли мы на BoxCollider с помощью Physics.Raycast, и если нажали, перемещаем весь джойстик в эту точку (подложку и сам джойстик, но не BoxCollider) и используя перевод экранных координат нажатия в координаты сечения пирамиды и локальные координаты джойстика находим конечное положение самого джойстика. Подложка при этом остается на своем месте. После того, как игрок отпускает палец от экрана, просто ставим джойстик в исходное положение. Вроде все просто.

Для превода экранных координат в координаты точки на сечении пирамиды я решил пойти простым путем — находим процентное отношение координат нажатия к размерам экрана, получаем два числа от 0 до 1, соответствующих координатам X и Y. Затем умножаем эти числа на ширину и высоту сечения пирамиды и получаем локальные координаты точки на этом сечении. После чего, если джойстик расположен не в центре, нужно вычесть из полученных координат относительный центр коллайдера. В итоге, без проверки выхода джойстика за рамки подложки, получается следующая картина. При этом мы уже можем найти относительное направление джойстика и нормировать его.

Используя разницу векторов центра подложки и центра джойстика можно найти длину вектора. Для проверки этого значения нам нужно знать радиус самой подложки. На помощь нам приходит свойство Pixels to Unit при импорте спрайтов. Фактически это свойство говорит о том, сколько пикселей исходного спрайта уместится в 1 юнит расстояния. Чем больше это значение, тем меньше выглядит спрайт. К сожалению, я не нашел адекватного способа определения этого свойства во время выполнения кода, поскольку требуется класс TextureImporter и его свойства, а он обычно доступен только для расширений редактора (скорее всего его физически можно использовать из рантайма, но по-моему это не совсем адекватный вариант). Пока решением остается ручное копирование значения Pixels to Unit в паблик свойство контроллера джойстика.
Посчитав фактическую величину подложки в юнитах, мы можем проверить, выходит ли джойстик за ее рамки или нет.
Для оптимизации проверки в данном случае нам здорово поможет свойство Vector3.sqrMagnitude, определяющее квадрат модуля вектора. Если сравнивать квадрат модуля с квадратом необходимого расстояния, можно избежать операции вычисления квадратного корня, что немного ускорит выполнения кода.
Общее условие выглядит следующим образом — если квадрат модуля вектора относительного направления меньше или равен квадрату радиуса подложки, джойстик находится под положением нажатия. В противном случае нормируем вектор относительного направления, умножаем на радиус подложки и ставим джойстик в точку с получившимися координатами.

Получается такая картина:

Читайте также:  Не меняется звук уведомлений андроид

Можно сразу сделать так, чтобы относительный вектор был нормирован от 0 до 1 для использования в дальнейшем. Тут заморачиваться не надо, используются простые формулы нормировки.

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

(На самом деле он ходит, просто неоднородная текстура, на которой видно передвижение, здорово увеличивает размер GIF файла)

Я уже начал думать что работа подходит к концу, но дальше все оказалось еще интересней.

Подводные камни

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

С этой проблемой я залип на очень длительное время, долго пытался понять мозгом, что и с чем нужно складывать и из чего вычитать. Оказалось, что все довольно сложно.
Прежде всего, как бы мы ни двигали BoxCollider, точка отсчета локальных координат в джойстике всегда будет в центре подложки (в обычном положении, без нажатия).
Оказалось что свойства collider.bounds считаются всегда в мировых координатах, поэтому для адекватного нахождения реальных размеров пришлось для начальных вычислений ставить объект джойстика в положение Reset, то есть в нулевую позицию с нулевым поворотом, а потом ставить обратно.
Общий вид систем отсчетов выглядел следующим образом:

Конечно, в расчет приходилось брать и размеры коллайдера. В общем я здорово заморочился, но все таки разобрался с этой системой.

Но это был не конец.

Еще одним подводным камнем оказалась поддержка мультитача.
Представим следующий сценарий:
У нас есть два джойстика и два пальца, пусть это будет Д1, Д2, П1 и П2.
Пользователь нажимает П1 на Д1, после чего нажимает П2 на Д2. Индекс П1 в массиве нажатий равен 0, индекс П2 в массиве нажатий равен 1. Если после этого пользователь отпускает П2, индекс П1 все еще остается равен 0, все хорошо. Но если вместо П2 пользователь отпустит П1, то индекс П2 станет равен 0, и Д1 будет думать, что его П1 переместился в точку П2, и получится так, что один П2 управляет двумя джойстиками.
Стоит отметить, что до этого я мало работал с мультитачами, и следующее предложение может показаться кому-то смешным и несуразным. Я сразу ринулся проверять дельту перемещений. Но это был слишком кривой костыль, он не спасал от случая, когда мы сводим два пальца вместе. Оба джойстика прилипают к одному пальцу и снова та же самая картина.
Потом у меня хватило ума посмотреть документацию и прочитать про волшебное свойство Touch.fingerId. Оно хранит индекс нажатого пальца.
Я поменял привязку джойстика к индексу в массиве касаний на индекс пальца и стал проверять, присутствует ли еще в списке присутствующих касаний нужный нам палец. Все стало работать просто отлично.

Итог:
Я проверил производительность джойстика по профайлеру, оказалось что он здорово превосходит стандартный (простой одновременный твикинг, без применения перемещения на цель, проверял через Unity Remote):

  • Батчинг никуда не делся.
  • С подобным подходом не нужно переживать о размерах текстур относительно размера экрана, GUITexture приходилось вручную шкалировать под разные разрешения.
  • Но самое главное — своими руками и бесплатно. Не стоит недооценивать роль коммунизма в Open-Source разработке!

Пакет ждет одобрения в AssetStore, обновлю ссылку как только он там появится.
Ссылка на Repo: github.com/KumoKairo/CNJoystick

Читайте также:  2gis карты для андроида

Пока писал статью, понял как сделать джойстик без коллайдера и Physics.Raycast(..)
Я определенно буду продолжать работать над этим продуктом, планируется сделать простой тачпад и ABC кнопки.

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

Оригинальный пак экранных контроллеров можно найти ТУТ

Источник

Создание виртуального джойстика на юнити (основная статья)

Всем привет! В этой статье мы будем делать простой виртуальный джойстик на юнити.

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

Работа джойстика крайне проста — есть специальная «ручка» которую игрок двигает пальцем в определенной зоне. Когда нам нужно скажем, передвинуть персонажа, мы обращаемся к скрипту джойстика и он возвращает нам смещение ручки относительно одной или двух осей.

ВАЖНО! Смещение должно быть нормализованным. То есть максимальное значение по любой оси не должно быть больше единицы, а минимальное — минус единицы. Иначе будет трудно настроить ту же скорость движения персонажа. Например, нам нужно чтобы персонаж двигался с максимальной скоростью 3 метра в секунду. Если смещение будет нормализованным, то максимальная скорость персонажа будет равна заданной, то есть 3 м/с, если же оно не будет нормализованным, то скорость может быть равна, например 8 или 0,2 или 114 м/с.

Итак, приступим. Для начала нам нужно пару кругляшков для обозначения «зоны» и «ручки». Нарисовать их можно в фотошопе или где-нибудь ещё и экспортировать в юнити.

На сцене создаем канвас, создаем в нем объект с компонентом Image, кидаем в свойтво source image наш спрайт зоны и ставим в нужное место на экране. Готово! Это наш джойстик. Затем создаём скрипт управления джойстиком и вешаем на него. Далее создаем джойстику дочерний объект с тем же имаджем (не бейте пж за мой английский) и кидаем на него спрайт ручки. Это наша ручка (как ни странно).

Самая скучная работа закончена и можно начинать кодить.

Итак, прежде всего нам нужно отслеживать взаимодействие пользователя с джойстиком. Для этого будем использовать интерфейсы IDragHandler (для отслеживания «перетаскивания» джойстика пальцем) и IEndDragHandler (для того, чтобы знать когда пользователь убрал палец с джойстика). Объявляем класс наследником этих интерфейсов и реализуем их. Наш код должен выглядеть примерно так:

ВАЖНО! Обязательно добавьте CircleCollider2D на джойстик для регистрации нажатий по нему и не забудьте сделать его триггером. На основную камеру добавьте Physics 2D Raycaster для того чтобы узнавать о клике пальцем на джойстик.

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

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

Это Vector3.ClampMagnitude. Этот метод возвращает обрезанную до определенной длины копию вектора. То что нам и нужно. Модифицируем код.

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

ВАЖНО! В коде ниже будут использоваться свойства. Почитайте, что это такое (если вы не знаете) чтобы понимать, что там происходит.

Это финальный скрипт. Больше мы не будем его дорабатывать.

P.S. Выше я использовал свойство, только затем, чтобы сэкономить пару операций и не изменять значение каждый кадр. А static я использовал, чтобы получать значение проще. Вот пример:

Так как статья получается довольно длинной, то различные модификации джойстика я не буду публиковать здесь. Для этого я создам отдельную статью, которая выйдет сегодня или (если мне будет лень) завтра.

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

Источник

Оцените статью