Προσαρμογή του στοιχείου ελέγχου στην οθόνη για την υλοποίηση του πληκτρολογίου κατεύθυνσης

Σελίδα ενημέρωση :
Ημερομηνία δημιουργίας σελίδας :

Περιβάλλον επαλήθευσης

παράθυρα
  • Παράθυρα 11
Επεξεργαστής ενότητας
  • 2020.3.25στ1
Πακέτο συστήματος εισόδου
  • 1.2.0

Προϋποθέσεις για αυτήν τη συμβουλή

Οι ακόλουθες ρυθμίσεις έχουν γίνει εκ των προτέρων ως προϋπόθεση για την περιγραφή αυτής της συμβουλής.

Θα πρέπει επίσης να είστε εξοικειωμένοι με τις παρακάτω συμβουλές:

Το On-Screen Control Stick είναι η τυπική λειτουργία προσανατολισμού

Το On-Screen Stick υλοποιείται ως εικονικό stick, αναπαράγοντας τη λειτουργία ενός stick παρόμοια με αυτή που βρίσκεται σε έναν φυσικό ελεγκτή. Για παράδειγμα, εάν θέλετε να το μετακινήσετε προς τα δεξιά, μπορείτε πρώτα να αγγίξετε την οθόνη για να αγγίξετε το ραβδί και, στη συνέχεια, να το σύρετε προς τα δεξιά για να χτυπήσετε το ραβδί προς τα κάτω.

Είναι εύκολο να φανταστεί κανείς ως λειτουργία ραβδιού, αλλά αντίθετα, όταν θέλετε να το μετακινήσετε αμέσως προς τα δεξιά, (1) αγγίξτε (2) σύρετε προς τα δεξιά, Δεδομένου ότι απαιτεί λειτουργία δύο σταδίων, η απάντηση αναπόφευκτα θα καθυστερήσει λίγο. Αυτό ισχύει ιδιαίτερα όταν πρέπει να κάνετε μια γρήγορη αλλαγή κατεύθυνσης στη σειρά.

Πληκτρολόγιο κατεύθυνσης που καθορίζει την κατεύθυνση τη στιγμή της αφής (μη τυπικό)

Η καλύτερη μέθοδος εισαγωγής για γρήγορη εισαγωγή κατεύθυνσης είναι αυτή που μπορεί να καθορίσει την κατεύθυνση τη στιγμή της αφής τοποθετώντας κάτι σαν πληκτρολόγιο κατεύθυνσης ή πλήκτρο βέλους. Δεν είναι φτιαγμένο από την Unity, αλλά στο παιχνίδι μου Little Saber, τοποθετώ τα πλήκτρα βέλους έτσι ώστε να μπορείτε να μετακινηθείτε γρήγορα προς την κατεύθυνση που αγγίζετε. Φυσικά, μπορείτε επίσης να σύρετε προς τα πάνω ή προς τα αριστερά ενώ αγγίζετε για να αλλάξετε κινήσεις.

Ωστόσο, το πρότυπο ελέγχου στην οθόνη δεν είναι εύκολο να τοποθετηθεί και να εφαρμοστεί, επειδή υπάρχουν μόνο μοχλοί οθόνης και κουμπιά στην οθόνη.

Μπορείτε επίσης να δημιουργήσετε ένα ψευδο-D-pad τακτοποιώντας τέσσερα κουμπιά όπως φαίνεται στο παρακάτω σχήμα, αλλά είναι άβολο επειδή δεν μπορείτε να εισαγάγετε διαγώνια. Εάν τοποθετήσετε 8 κουμπιά, είναι δυνατή η διαγώνια λειτουργία, αλλά η λειτουργία κατεύθυνσης ροής όπως "← ↙ ↓" εξακολουθεί να μην είναι δυνατή.

Δημιουργήστε ένα πληκτρολόγιο κατεύθυνσης με προσαρμογή του Ελέγχου στην οθόνη

Όπως μπορείτε να δείτε, ο έλεγχος στην οθόνη διατίθεται στάνταρ μόνο με το Stick and Button, αλλά μπορείτε να προσαρμόσετε τις λειτουργίες που λείπουν με τα δικά σας σενάρια. Εδώ λοιπόν θα ήθελα να δημιουργήσω ένα κατευθυντικό πληκτρολόγιο με προσαρμογή ελέγχου στην οθόνη.

Χάρτης δράσης

Θα χρησιμοποιήσουμε τον χάρτη δράσης, αλλά θα χρησιμοποιήσουμε αυτόν που δημιουργήθηκε στις προηγούμενες συμβουλές ως έχει, οπότε η διαδικασία παραλείπεται.

Έχετε γράψει επίσης κάποιο κώδικα.

Τοποθέτηση αντικειμένων

Τοποθετήστε μια περιοχή κειμένου για να εμφανίσετε τα δεδομένα εισόδου σας και ένα κουμπί που αντικαθιστά το πληκτρολόγιο κατεύθυνσης. Σε αυτήν την περίπτωση, τα κουμπιά είναι διατεταγμένα, αλλά μπορείτε να τα αντικαταστήσετε με μια εικόνα που είναι πιο κατανοητή.

Δέσμη ενεργειών για την εμφάνιση εισόδου

Επειδή ο Έλεγχος στην οθόνη αντικαθιστά την επαφή με την αλληλεπίδραση του φυσικού ελεγκτή, Δημιουργήστε μια δέσμη ενεργειών που εμφανίζει τα δεδομένα εισόδου σας σε κείμενο καθώς λειτουργεί ο χάρτης ενεργειών.

Το περιεχόμενο είναι το ίδιο με πριν, οπότε θα παραλείψω την εξήγηση.

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}";
  }
}

Αφού το ρυθμίσετε, ελέγξτε πρώτα αν λειτουργεί με το πληκτρολόγιο ή το gamepad σας.

Προσαρμογή στοιχείων ελέγχου στην οθόνη

Αυτό μας φέρνει στο κύριο θέμα αυτών των συμβουλών. Η προσαρμογή του στοιχείου ελέγχου στην οθόνη είναι μια δέσμη ενεργειών, επομένως δημιουργήστε πρώτα μια δέσμη ενεργειών. Το όνομα είναι αυθαίρετο, αλλά στην περίπτωση 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 κλάση προσαρμογής ελέγχου στην οθόνη, μεταβιβάζοντας από την κλάση προσαρμογής. Επιπλέον, για να λάβετε διάφορα συμβάντα αφής, κληρονομήστε τη διεπαφή προορισμού. Εδώ, γίνεται επεξεργασία "όταν αγγίζετε", "όταν μετακινείστε ενώ αγγίζετε", "όταν απελευθερώνεται η αφή" και "πριν σύρετε", επομένως περιγράφονται αντίστοιχα οι ακόλουθες διεπαφές.

public class OnScreenDpad
  : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler
Περιεχόμενα διεπαφής
IPointerDownHandler Όταν αγγίζετε
IPointerUpHandler Όταν αφήνετε το άγγιγμα
IDragHandler Όταν κινείστε ενώ αγγίζετε
IInitializePotentialDragHandler Πριν ξεκινήσετε τη μεταφορά

Δηλώνονται τα ακόλουθα πεδία:

controlPathInternalOnScreenControl απαιτείται εάν κληρονομείτε από μια τάξη. Περιέχει μια συμβολοσειρά από το ποιο κουμπί της συσκευής εισόδου θα αντιστοιχιστεί, αλλά βασικά μπορείτε να το γράψετε όπως είναι. 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);
}

Επισυνάψτε τη δέσμη ενεργειών που δημιουργήσατε στο κουμπί. Αυτή η ενέργεια έχει οριστεί να αντιμετωπίζεται ως η λειτουργία του αριστερού μοχλού του Gamepad.

Δοκιμάστε να μετακινήσετε το παιχνίδι και αγγίξτε τα κουμπιά. Νομίζω ότι μπορείτε να δείτε ότι η τιμή που λαμβάνεται αλλάζει ανάλογα με τη θέση αφής. Επίσης, μπορείτε να δείτε ότι ακόμα κι αν σύρετε ενώ αγγίζετε, η τιμή αλλάζει ανάλογα με τη θέση οπισθέλκουσας.