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

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

Динамический джойстик

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

Чтобы было проще, вот скрипт из прошлой статьи:

using UnityEngine; using UnityEngine.EventSystems; #pragma warning disable 0649 public class Joystick : MonoBehaviour, IDragHandler, IEndDragHandler { [SerializeField] GameObject Handle; [SerializeField] float MoveRadius; static Joystick instance; public static Vector2 Position { get { return (instance.Handle.transform.position - instance.gameObject.transform.position).normalized; } } private void Start() { instance = this; } public void OnDrag(PointerEventData eventData) { Vector3 inputPosition = Camera.main.ScreenToWorldPoint(eventData.position); Vector3 offset = inputPosition - gameObject.transform.position; offset = new Vector3(offset.x, offset.y, 0); Handle.gameObject.transform.position = gameObject.transform.position + Vector3.ClampMagnitude(offset, MoveRadius); } public void OnEndDrag(PointerEventData eventData) { Handle.gameObject.transform.localPosition = Vector3.zero; } }

Итак, приступим.

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

public void OnDrag(PointerEventData eventData) { Vector3 clickPosition = Camera.main.ScreenToWorldPoint(eventData.position); clickPosition = new Vector3(clickPosition.x, clickPosition.y, 0); Vector3 offset = clickPosition - gameObject.transform.position; Handle.gameObject.transform.position = gameObject.transform.position + offset; }

Я запостил только метод OnDrag ибо изменения произошли только в нём, а копировать весь код будет неудобно как для меня, так и для вас.

Хорошо, теперь ручка двигается по всему экрану, но нам нужно отследить момент, когда она выходит за пределы зоны.

public void OnDrag(PointerEventData eventData) { Vector3 clickPosition = Camera.main.ScreenToWorldPoint(eventData.position); clickPosition = new Vector3(clickPosition.x, clickPosition.y, 0); Vector3 offset = clickPosition - gameObject.transform.position; if (offset.magnitude >= MoveRadius) { //ручка вышла за зону } Handle.gameObject.transform.position = gameObject.transform.position + offset; }

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

if (offset.magnitude >= MoveRadius) { //Если расстояние от ручки до зоны больше максимальной длины удаления, //то передвигаем зону к пальцу transform.position += (clickPosition - transform.position); }

Ручка теперь двигается к пальцу, но делает это рывками. Для решения данной проблемы, уменьшим расстояние от пальца до зоны, умножив его на какое-нибудь число от 0 до 1. У меня это 0,05. Вы же подберите его так, как вам нравится.

Вот финальный скрипт:

using UnityEngine; using UnityEngine.EventSystems; #pragma warning disable 0649 public class Joystick : MonoBehaviour, IDragHandler, IEndDragHandler { [SerializeField] GameObject Handle; [SerializeField] float MoveRadius; [SerializeField] float speed = 0.05f; static Joystick instance; public static Vector2 Position { get { return (instance.Handle.transform.position - instance.gameObject.transform.position).normalized; } } private void Start() { instance = this; } public void OnDrag(PointerEventData eventData) { Vector3 clickPosition = Camera.main.ScreenToWorldPoint(eventData.position); clickPosition = new Vector3(clickPosition.x, clickPosition.y, 0); Vector3 offset = clickPosition - gameObject.transform.position; if (offset.magnitude >= MoveRadius) { transform.position += (clickPosition - transform.position) * speed; } Handle.gameObject.transform.position = gameObject.transform.position + offset; } public void OnEndDrag(PointerEventData eventData) { Handle.gameObject.transform.localPosition = Vector3.zero; } }

Динамический джойстик готов.

Джойстик, пропадающий когда на него не нажимают.

Этот джойстик никак особо не отличается, но он немного удобнее в использовании.

Для скрытия джойстика во-первых нужно получить компоненты Image у зоны и ручки. Для ручки просто изменяем тип ссылки на ручку с GameObject на Image. А для зоны создаем отдельную ссылку и записываем в неё значение в Start-е при помощи GetComponent:

using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; //Не забываем добавить библиотеку для работы с пользовательским интерфейсом #pragma warning disable 0649 public class Joystick : MonoBehaviour, IDragHandler, IEndDragHandler { [SerializeField] Image Handle; Image Zone; [SerializeField] float MoveRadius; static Joystick instance; public static Vector2 Position { get { return (instance.Handle.transform.position - instance.gameObject.transform.position).normalized; } } private void Start() { instance = this; Zone = GetComponent<Image>(); }

Теперь, просто изменяем альфа канал у обоих компонентов на 1 при клике и на 0 при убирании пальца с джойстика.

using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; #pragma warning disable 0649 public class Joystick : MonoBehaviour, IDragHandler, IEndDragHandler, IPointerDownHandler { [SerializeField] Image Handle; Image Zone; [SerializeField] float MoveRadius; static Joystick instance; public static Vector2 Position { get { return (instance.Handle.transform.position - instance.gameObject.transform.position).normalized; } } private void Start() { instance = this; Zone = GetComponent<Image>(); SetTransparency(0); //Выключаем } public void OnPointerDown(PointerEventData eventData) { //При клике на джойстик SetTransparency(1); //Включаем } public void OnDrag(PointerEventData eventData) { Vector3 inputPosition = Camera.main.ScreenToWorldPoint(eventData.position); Vector3 offset = inputPosition - gameObject.transform.position; offset = new Vector3(offset.x, offset.y, 0); Handle.gameObject.transform.position = gameObject.transform.position + Vector3.ClampMagnitude(offset, MoveRadius); } public void OnEndDrag(PointerEventData eventData) { Handle.gameObject.transform.localPosition = Vector3.zero; SetTransparency(0); //Выключаем } void SetTransparency(float transparency) { Zone.color = new Color(Zone.color.r, Zone.color.g, Zone.color.b, transparency); Handle.color = new Color(Handle.color.r, Handle.color.g, Handle.color.b, transparency); } }

Джойстик с движением ручки по одной оси

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

Для этой модификации требуется только слегка изменить метод OnDrag.

Вот что нужно сделать:

public void OnDrag(PointerEventData eventData) { Vector3 clickPosition = Camera.main.ScreenToWorldPoint(eventData.position); float OffsetX = clickPosition.x - transform.position.x; Handle.transform.position = new Vector3(transform.position.x + Mathf.Clamp(OffsetX, -MoveRadius, MoveRadius), transform.position.y, 0); //Mathf.Clamp ограничивает число определенным диапазоном }

Выше представлен пример с движением ручки по горизонтали. Вот пример с движением по вертикали:

public void OnDrag(PointerEventData eventData) { Vector3 clickPosition = Camera.main.ScreenToWorldPoint(eventData.position); float OffsetY = clickPosition.y - transform.position.y; Handle.transform.position = new Vector3(transform.position.x,transform.position.y + Mathf.Clamp(OffsetY, -MoveRadius, MoveRadius), 0); }

На этом все. В этой статье я показал модификации джойстика, написанного в прошлой статье.

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

Всем пока!

1919 показов
358358 открытий
1 комментарий

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

Ответить