Настроювання екранного елемента керування для реалізації D-pad
Середовище перевірки
- Вікна
-
- вікна 11
- Редактор єдності
-
- 2020.3.25f1
- Пакет системи введення
-
- 1.2.0
Передумови для цієї поради
Наступні настройки були зроблені заздалегідь як передумова для опису цієї поради.
Також слід ознайомитися з наступними порадами:
- Використання карт дій для призначення кнопок поведінці гри
- Використання елементів керування, оптимізованих для сенсорного вводу, із екранним керуванням
Екранний Control Stick - це стандартна операція орієнтації
On-Screen Stick реалізований як віртуальний флеш-накопичувач, повторюючи роботу флешки, подібну до тієї, що знаходиться на фізичному контролері. Наприклад, якщо ви хочете перемістити його вправо, ви можете спочатку доторкнутися до екрана, щоб доторкнутися до палиці, а потім посуньте її вправо, щоб збити палицю.
Її легко уявити як операцію палицею, але навпаки, коли ви хочете негайно перемістити її вправо, (1) торкнутися, (2) посуньте вправо, Оскільки для цього потрібна двоетапна операція, відповідь неминуче буде трохи відстрочена. Особливо це актуально, коли потрібно зробити швидку зміну напрямку поспіль.
D-pad, що визначає напрямок в момент дотику (нестандартний)
Найкращим методом введення для швидкого введення напрямку є той, який може визначити напрямок у момент дотику, розмістивши щось на зразок D-pad або клавіші зі стрілкою. Це зроблено не Unity, але в моїй грі Little Saber я розміщую клавіші зі стрілками, щоб можна було швидко рухатися в напрямку, до якого торкаєшся. Звичайно, ви також можете ковзати вгору або вліво, торкаючись, щоб перемикати рухи.
Однак стандарт екранного керування нелегко розмістити та впровадити, оскільки є лише екранні палички та екранні кнопки.
Також можна створити псевдо-D-pad, розташувавши чотири кнопки, як показано на малюнку нижче, але це незручно, оскільки неможливо ввести по діагоналі. Якщо розмістити 8 кнопок, діагональ можлива, але операція напрямку потоку, така як «← ↙ ↓», все ще неможлива.
Створення панелі напрямків із налаштуванням екранного керування
Як бачите, екранне керування входить до стандартної комплектації лише з Stick і Button, але ви можете налаштувати відсутні функції за допомогою власних сценаріїв. Тож тут я хотів би створити панель напрямків із налаштуванням екранного керування.
Карта дій
Ми будемо використовувати карту дій, але ми будемо використовувати ту, яка створена в попередніх порадах як є, тому процедура опущена.
Ви також написали якийсь код.
Позиціонування об'єктів
Розмістіть текстову область для відображення введених даних і кнопку, яка замінює D-pad. В цьому випадку кнопки розташовуються, але можна замінити їх на зображення, яке легше зрозуміти.
Сценарій для відображення вхідних даних
Оскільки екранне керування замінює дотик взаємодією з фізичним контролером, Створіть сценарій, який відображатиме введені дані в тексті під час роботи мапи дій.
Зміст такий же, як і раніше, тому пояснення опустю.
- Використання карт дій для призначення кнопок поведінці гри
- Використання елементів керування, оптимізованих для сенсорного вводу, із екранним керуванням
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
public class InputActionScript : MonoBehaviour
{
<summary>情報を表示させるテキストオブジェクト。</summary>
[SerializeField] private Text TextObject;
<summary>アクションマップから自動生成されたクラス。</summary>
private InputActionSample _actionMap;
private void Awake()
{
// 各操作を行ったときに呼ばれるイベントを設定する
_actionMap = new InputActionSample();
_actionMap.Action2D.Move.performed += context => OnMove(context);
_actionMap.Action2D.Attack.performed += context => OnAttack(context);
_actionMap.Action2D.Move.canceled += context => OnMove(context);
_actionMap.Action2D.Attack.canceled += context => OnAttack(context);
}
private void OnEnable()
{
// このオブジェクトが有効になったときにアクションマップを有効にする
_actionMap.Enable();
}
private void OnDisable()
{
// このオブジェクトが無効になったときにアクションマップが余計な動作を起こさないように無効にする
_actionMap.Disable();
}
<summary>
Move 操作をした時に呼ばれるメソッドです。
</summary>
<param name="context">コールバックパラメータ。</param>
public void OnMove(InputAction.CallbackContext context)
{
// Move の入力量を取得
var vec = context.ReadValue<Vector2>();
TextObject.text = $"Move:({vec.x:f2}, {vec.y:f2})\n{TextObject.text}";
}
<summary>
Attack 操作をした時に呼ばれるメソッドです。
</summary>
<param name="context">コールバックパラメータ。</param>
public void OnAttack(InputAction.CallbackContext context)
{
// Attack ボタンの状態を取得
var value = context.ReadValueAsButton();
TextObject.text = $"Attack:{value}\n{TextObject.text}";
}
}
Після його налаштування спочатку перевірте, чи працює він з вашою клавіатурою або геймпадом.
Налаштування екранних елементів керування
Це підводить нас до основної теми цих порад.
Настроювання екранного елемента керування – це сценарій, тому спочатку створіть сценарій.
Назва довільна, але в даному випадку OnScreenDpad
це .
Сценарій виглядає так:
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.OnScreen;
public class OnScreenDpad
: OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
{
[InputControl(layout = "Vector2")]
[SerializeField]
private string _controlPath;
<summary><see cref="OnScreenControl"/> で定義された値。</summary>
protected override string controlPathInternal { get => _controlPath; set => _controlPath = value; }
<summary>オブジェクトの位置。</summary>
private Vector2 _objectPosition;
<summary>オブジェクトのサイズの半分 (スケールも含む)。</summary>
private Vector2 _objectSizeHalf;
<summary>
オブジェクトが動作する最初のタイミングで1回だけ呼ばれます。
</summary>
private void Start()
{
var rectTransform = (RectTransform)base.transform;
// オブジェクトの位置を取得
_objectPosition = rectTransform.anchoredPosition;
// オブジェクトのサイズの半分を取得 (スケールサイズも考慮)
_objectSizeHalf = rectTransform.sizeDelta * rectTransform.localScale / 2f;
}
<summary>ドラッグの初期化処理として呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnInitializePotentialDrag(PointerEventData eventData)
{
// タッチのスライド操作を即座に発生させたいのでドラッグ開始までの閾値を無効にします
eventData.useDragThreshold = false;
}
<summary>タッチしたタイミングで呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnPointerDown(PointerEventData eventData)
{
Operate(eventData);
}
<summary>タッチした後ドラッグするたびに呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnDrag(PointerEventData eventData)
{
Operate(eventData);
}
<summary>タッチを離したときに呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnPointerUp(PointerEventData eventData)
{
// 入力をやめた扱いにしたいので zero を渡します
SendValueToControl(Vector2.zero);
}
<summary>
方向パッドの入力処理を行います。
</summary>
<param name="eventData">タッチ情報。</param>
private void Operate(PointerEventData eventData)
{
// タッチ位置を Canvas 上の位置に変換します
RectTransformUtility.ScreenPointToLocalPointInRectangle(
transform.parent.GetComponentInParent<RectTransform>(),
eventData.position,
eventData.pressEventCamera,
out Vector2 localPoint);
// Dpad の中心を原点としたタッチ位置
Vector2 positionInDpad = localPoint - _objectPosition;
// タッチ位置をオブジェクトサイズの半分で割り 0~1 の範囲に収めます
Vector2 positionRate = Vector2.ClampMagnitude(positionInDpad / _objectSizeHalf, 1);
// 入力値を OnScreenControl に渡してその後の処理を任せます。
SendValueToControl(positionRate);
}
}
Ви створюєте OnScreenControl
клас настроювання екранного елемента керування, успадковуючи його від класу Customize.
Крім того, щоб отримувати різні сенсорні події, успадкуйте цільовий інтерфейс.
Тут обробляються «при торканні», «при русі під час торкання», «коли дотик відпущений» і «перед перетягуванням», тому відповідно описані і наступні інтерфейси.
public class OnScreenDpad
: OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Зміст інтерфейсу | |
---|---|
IPointerDownHandler | При дотику |
IPointerUpHandler | Коли ви відпускаєте дотик |
IDragHandler | Коли ви рухаєтеся під час дотику |
IInitializePotentialDragHandler | Перед початком перетягування |
Оголошуються такі поля:
controlPathInternal
OnScreenControl
є обов'язковим, якщо ви успадковуєте від класу.
Він містить рядок того, з якою кнопкою пристрою введення зіставити, але в основному ви можете записати його як є.
InputControl
Однак атрибут повинен містити значення, яке потрібно зберегти (в даному випадку Vector2).
_objectPosition
_objectSizeHalf
А заздалегідь утримуйте половину положення і розміру кнопки і використовуйте її пізніше для розрахунків.
[InputControl(layout = "Vector2")]
[SerializeField]
private string _controlPath;
<summary><see cref="OnScreenControl"/> で定義された値。</summary>
protected override string controlPathInternal { get => _controlPath; set => _controlPath = value; }
<summary>オブジェクトの位置。</summary>
private Vector2 _objectPosition;
<summary>オブジェクトのサイズの半分 (スケールも含む)。</summary>
private Vector2 _objectSizeHalf;
Метод, викликаний Start
після ініціалізації об'єкта, отримує половину положення і розміру кнопки на полотні.
Розмір також враховує масштаб.
Start
Він обробляється методом, але якщо його можна правильно використовувати при розрахунку в результаті, то терміни придбання можуть бути де завгодно.
// <summary>
// オブジェクトが動作する最初のタイミングで1回だけ呼ばれます。
// </summary>
private void Start()
{
var rectTransform = (RectTransform)base.transform;
// オブジェクトの位置を取得
_objectPosition = rectTransform.anchoredPosition;
// オブジェクトのサイズの半分を取得 (スケールサイズも考慮)
_objectSizeHalf = rectTransform.sizeDelta * rectTransform.localScale / 2f;
}
OnInitializePotentialDrag
викликається, коли потрібно почати перетягування після дотику.
Під час перетягування OnDrag
називається метод.
Поріг встановлений для запобігання судження про перетягування певною мірою, щоб палець трохи не рухався і судження про перетягування не відбувалося через операцію простого дотику.
Цього разу, оскільки це вхід, який передбачає, що ви хочете виконати операцію тонкої настройки, вимкніть це налаштування порогу та негайно зробіть судження про перетягування.
eventData.useDragThreshold
false
Поріг можна перевизначити, встановивши значення .
<summary>ドラッグの初期化処理として呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnInitializePotentialDrag(PointerEventData eventData)
{
// タッチのスライド操作を即座に発生させたいのでドラッグ開始までの閾値を無効にします
eventData.useDragThreshold = false;
}
Нижче наведено події, які називаються під час дотику, перетягування та відпускання відповідно.
OnPointerDown
OnDrag
Оскільки і , кожен виконує Operate
однакову вхідну обробку, тому створюємо окремий метод і викликаємо його.
OnPointerUp
Тепер передайте його методу, щоб SendValueToControl
Vector2.zero
повідомити елемент керування про припинення введення.
<summary>タッチしたタイミングで呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnPointerDown(PointerEventData eventData)
{
Operate(eventData);
}
<summary>タッチした後ドラッグするたびに呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnDrag(PointerEventData eventData)
{
Operate(eventData);
}
<summary>タッチを離したときに呼ばれます。</summary>
<param name="eventData">タッチ情報。</param>
public void OnPointerUp(PointerEventData eventData)
{
// 入力をやめた扱いにしたいので zero を渡します
SendValueToControl(Vector2.zero);
}
Operate
Метод являє собою сердечну операцію введення D-pad.
Перш за все, положення дотику можна отримати за допомогою , але так як ця координата є координатою ігрового екрану eventData.position
,
RectTransformUtility.ScreenPointToLocalPointInRectangle
метод перетворення в координати полотна.
Значення, які потрібно передати, - це "полотно", "положення дотику", "камера" та " RectTransform
приймальна змінна".
Отримавши «положення дотику до полотна», перетворіть його в положення кнопки як початок координат.
Просто відніміть положення ()_objectPosition
об'єкта.
Далі, оскільки положення дотику все ще є числом на полотні, перетворіть це значення у співвідношення 0 ~ 1.
Якщо розділити на половину розміру кнопки, сенсорне положення в межах діапазону кнопки становитиме 0~1.
Якщо ви перетягуєте дотиком, значення буде 1 або більше, оскільки він приймає операції навіть поза кнопкою.
Vector2.ClampMagnitude
Роблять це в методі так, щоб вона не перевищувала 1.
Нарешті, передайте методу вхідне значення SendValueToControl
, перетворене на 0 ~ 1.
Екранний елемент керування зробить все інше за вас.
<summary>
方向パッドの入力処理を行います。
</summary>
<param name="eventData">タッチ情報。</param>
private void Operate(PointerEventData eventData)
{
// タッチ位置を Canvas 上の位置に変換します
RectTransformUtility.ScreenPointToLocalPointInRectangle(
transform.parent.GetComponentInParent<RectTransform>(),
eventData.position,
eventData.pressEventCamera,
out Vector2 localPoint);
// Dpad の中心を原点としたタッチ位置
Vector2 positionInDpad = localPoint - _objectPosition;
// タッチ位置をオブジェクトサイズの半分で割り 0~1 の範囲に収めます
Vector2 positionRate = Vector2.ClampMagnitude(positionInDpad / _objectSizeHalf, 1);
// 入力値を OnScreenControl に渡してその後の処理を任せます。
SendValueToControl(positionRate);
}
Прикріпіть до кнопки створений вами сценарій. Ця дія розглядається як операція лівої палички геймпада.
Спробуйте перемістити гру і торкнутися кнопок. Думаю, ви бачите, що отримане значення змінюється в залежності від положення дотику. Крім того, ви можете бачити, що навіть якщо ви перетягуєте під час дотику, значення змінюється залежно від позиції перетягування.