1. 程式人生 > >Unity 搖桿功能的 UGUI 實現

Unity 搖桿功能的 UGUI 實現

本文目標

在 Unity 中使用 UGUI 實現搖桿功能,並將此搖桿模組化以提高其通用性。

軟體版本

  • Unity 2018.1.5f1 Personal (64bit)
  • Visual Studio 2017

思路

基於 UnityEngine.UI.ScrollRect 來實現搖塊。
ScrollRect 是矩形的,要實現圓形的搖動區域,可通過程式碼設定搖塊可移動半徑來實現。當搖塊當前位置超出了半徑後,不再讓搖塊向外面的區域移動即可。搖塊相對於中心點的位置向量正則化之後可以作為輸出的方向向量。

介面

  • 輸入:由Unity提供的手勢互動事件及相關資料。
  • 輸出:方向向量,ForceVector。這是歸一化的移動向量,可以按需要使用。

元件

元件結構:

Canvas
└─JoyStick(Empty)
    ├─ScrollCircle(Empty, +本文建立的ScrollCircle元件)
    │  └─StickImage(UI/Image)
    └─StickBackground(UI/Image)

ScrollCircle 指令碼,繼承於 UnityEngine.UI.ScrollRect. 掛載到 ScrollCircle 物體上。

邏輯

在 Start 時計算出搖桿區域半徑或者直接設定可拖動半徑。
響應拖動 OnDrag、結束拖動 OnEndDrag 事件(過載父類的方法)。
先根據滑塊大小計算半徑,然後在拖動時位置由 ScrollRect 的 OnDrag 函式處理,只在位置超出半徑時作特殊處理以限制滑塊位置。以 anchoredPosition 正則化後的結果作為輸出的方向變數。

OnDrag

在呼叫基類方法後,先獲取搖塊相對於 anchor 的當前位置 v
如果 |v| 大於指定的半徑 R,則將搖塊位置設定為以半徑為模,以 v 的方向為方向的位置向量所在的位置 v|v|R
整個搖桿元件輸出的方向向量則為二維的向量,正則化之後提供給其他元件使用。

補充說明

Anchor 和 pivot的概念:錨點(Anchor)、軸心(Pivot)都是 Rect Transform 的引數。這兩個都是取值在[0,1]之間,是一個比例值。這個比例值,是根據 Rect Transform 所在 GameObject 的 width 和 height 來進行比例計算的。

  • Anchor: Anchor(錨點)以四個小三角形組成的風扇狀圖標表示,是相對於父節點的位置。比如 ScrollCircle 的父元素是 Canvas。
  • Pivot: Pivot(軸心)是旋轉縮放的中心。(Rotations, size, and scale modifications occur around the pivot)
  • Anchored Postion:相對於 anchor 來設定的 Rect Transform 的位置。

舉例解釋:

Anchor 與 Pivot 示例

此處 Pivot 是 (0.5, 0.5),則是 x、y 位置的一半,即整個元素的中心點。

OnEndDrag

呼叫基類方法,使搖塊歸位。並將方向向量設定為零向量。

程式碼

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class ScrollCircle : ScrollRect
{
    // 搖桿輸出的方向向量
    public Vector2 ForceVector {
        get {
            return m_ForceVector;
        }
        private set { }
    }


    protected override void Start()
    {
        base.Start();
        m_ForceVector = Vector2.zero;
        // mRadius = (transform as RectTransform).sizeDelta.x * 0.5f; // 計算搖桿塊的半徑
        m_Radius = 50f;
    }

    public override void OnDrag(UnityEngine.EventSystems.PointerEventData eventData)
    {

        base.OnDrag(eventData);
        var contentPostion = this.content.anchoredPosition;
        if (contentPostion.magnitude > m_Radius)
        {
            contentPostion = contentPostion.normalized * m_Radius;
            SetContentAnchoredPosition(contentPostion);
        }
        m_ForceVector = contentPostion.normalized;
        Debug.Log("方向向量:" + m_ForceVector);
    }

    public override void OnEndDrag(PointerEventData eventData)
    {
        base.OnEndDrag(eventData);
        m_ForceVector = Vector2.zero;
    }

    private Vector2 m_ForceVector;
    protected float m_Radius;
}

註解

RectTransform.sizeDelta 用來取 width 的一半作為半徑。這段程式碼如果把 anchor 打散,就失效,因為此時給的值是一個[0, 1]相對值而不是 RectTransform 的大小了。

RectTransform.sizeDelta

The size of this RectTransform relative to the distances between the anchors.

If the anchors are together, sizeDelta is the same as size. If the anchors are in each of the four corners of the parent, the sizeDelta is how much bigger or smaller the rectangle is compared to its parent.

使用注意

控制角色行走時一般是 x, z 平面。使用時注意將搖桿輸出的方向向量與場景中模型的方向配置正確。一般把模型朝向 -z 方向,再合理利用方向數值(注意正負號)即可。

示例工程

參考資料