Unity3d項目入門之虛擬搖桿

  Unity自己不提供搖桿的組件,開發者可使用牛逼的EasyTouch插件或者應用NGUI實現相關的需求,下面本文經過Unity自身的UGUI屬性,實現虛擬搖桿的功能。 主參考 《Unity:使用 UGUI 的 ScrollRect 製做虛擬搖桿》和鬆神的《UGUI研究院之遊戲搖桿》,分「搖桿UI的構建」和「搖桿事件鏈接」以及「搖桿表現強化」三方面總結制做過程當中的思路筆記。html

  一 搖桿UI的搭建ide

  核心是使用UGUI 的 ScrollRect Component( 常常用於 Scroll View 的製做),因能響應拖動回彈,同時又能計算對象的X軸和Y軸的偏移,因此選擇此組件開發。 建立兩Image組件,分別顯示搖桿背景和搖桿圓點,學習

  (overlay保證是場景攝像機看到的最上層)動畫

  (搖桿圓點以底座節點做parent,重置位置)ui

  給stickBg_添加ScrollRect組件(實質是一腳本),並在 Inspector 視窗將 Content 欄位拖動設置stick_爲其內容,this

  

  注意,Movement Type 項,將此欄位設置爲 Elastic,英文意思是回彈,則能自動將設置在 Content 的物件拉回置中,拖動原件,會有必定程度的緩衝力使物體不被拉遠,並在放開時自動彈回置中。spa

  運行遊戲,發現雖然能拖能回彈,可是隻能矩形拖動,並且矩形區域賊大,不符合實際項目的搖桿需求。 參考鬆神博文,直接繼承重寫ScrollRect組件,代碼以下:.net

using UnityEngine;
using System.Collections;
using UnityEngine.UI; //ScrollRect組件定義在UI的命名空間
using UnityEngine.EventSystems;

public class ScrollCircle : ScrollRect
{
    protected float mRadius;

    protected override void Start()
    {

        this.mRadius = (transform as RectTransform).sizeDelta.x * 0.5f;
    }

    public override void OnDrag(PointerEventData eventData)
    {

        base.OnDrag(eventData);

        Vector2 contentPostion = base.content.anchoredPosition;

        if (contentPostion.magnitude > this.mRadius)
        {

            contentPostion = contentPostion.normalized * this.mRadius;
        }

        base.content.anchoredPosition = contentPostion;
    }
}

  運行遊戲,發現拖動半徑生效。插件

  【補料:3d

     (直接上傳給你們湊合着用,親身找過資源,都要錢很差找哈哈)】

  還有一隱藏bug,就是當搖桿位置佈置得比較靠近屏幕邊緣時,拖動會出現拖不盡的問題,可參考 《Unity3D學習日記(一)使用UGUI製做虛擬搖桿

  二 搖桿事件鏈接

  在搖桿UI製做完畢後,接下來監聽和派發搖桿的觸摸事件,即監聽接收到stickBg_的移動,且將移動量傳遞出去給目標對象。這就運用到unity的事件系統的API,以下:

using UnityEngine;
//using System.Collections;       //可去,使用 Coroutine 才須要此聲明空間
using UnityEngine.Events;         //事件派發Interface
using UnityEngine.EventSystems;   //觸摸begin drag end事件監聽

  在操做搖桿時,主要監聽「開始控制( OnBeginDrag )」 和 「控制中( OnDrag )」 以及 「控制結束( OnEndDrag )」三個事件。 接口定義在Event空間的  IDragHandler , IEndDragHandler , IBeginDragHandler類聲明中,因此搖桿腳本類要繼承這三者類,詳細類分析參考官網的 IDragHandler ,

public class FixedJoystickHandler : MonoBehaviour, IDragHandler, IEndDragHandler, IBeginDragHandler
{
    public void OnBeginDrag(PointerEventData eventData) {}
    public void OnDrag(PointerEventData eventData) {}
    public void OnEndDrag(PointerEventData eventData) {}
}

  而後,定義 UnityEvent 事件執行處理對象,負責接收目前操做搖桿的結果,簡單說是當監聽到上述事件時要對哪一個對象執行什麼功能,有點相似於UGUI的 Button ,在 Inspector 窗口有Click 事件項,用以設置當發生Click 時會調用執行哪一個對象的哪一個接口,

  

  官方UnityEvent使用例程以下:

public class ExampleClass : MonoBehaviour
{
    UnityEvent m_MyEvent;

    void Start()
    {
        if (m_MyEvent == null)
            m_MyEvent = new UnityEvent();

        m_MyEvent.AddListener(Ping);
    }

    void Update()
    {
        if (Input.anyKeyDown && m_MyEvent != null)
        {
            m_MyEvent.Invoke();
        }
    }

    void Ping()
    {
        Debug.Log("Ping");
    }
}
    public Transform content;        //搖桿位移量「主人」
    public UnityEvent beginControl;  //開始控制器
    public UnityEvent endControl;    //結束控制器

  注意,stickBg_實時位移時須要將真實的位移量傳遞給UnityEvent類對象以計算移動距離和方向等,即須要傳遞參數,這就要運用到 UnityEvent擴展的模板類 UnityEvent<T0> ,官方例程以下:

using UnityEngine;
using UnityEngine.Events;

[System.Serializable] public class MyIntEvent : UnityEvent<int>
{
}

public class ExampleClass : MonoBehaviour
{
    public MyIntEvent m_MyEvent;

    void Start()
    {
        if (m_MyEvent == null)
            m_MyEvent = new MyIntEvent();

        m_MyEvent.AddListener(Ping);
    }

    void Update()
    {
        if (Input.anyKeyDown && m_MyEvent != null)
        {
            m_MyEvent.Invoke(5);
        }
    }

    void Ping(int i)
    {
        Debug.Log("Ping" + i);
    }
}

  可見T0表示傳遞的參數類型,且支持多個形參傳遞,如本節中傳遞的位移量是Vector3,

  

    [System.Serializable]  //這裏加Serializable目的是使其正確顯示在Inspector的事件屬性窗口,以下圖
    public class VirtualJoystickEvent : UnityEvent<Vector3> { }
   public VirtualJoystickEvent controlling;

  

  最後,就是實例化每一個事件監聽行爲,如剛開始被拖動時執行 beginControl 事件等,

    public void OnBeginDrag(PointerEventData eventData)
    {

        this.beginControl.Invoke();
    }
    public void OnEndDrag(PointerEventData eventData)
    {

        this.endControl.Invoke();
    }

  還有一點要注意的,本節 controlling 傳遞的結果是搖桿的偏移量和方向,stick_ 是 stickBg_的子控件,初始本地座標localPosition爲(0,0,0),移動時 x > 0 表示向右,< 0 是向左,而 y > 0 向上。可是隨着搖桿資源的尺寸大小不一樣,若使用實際的偏移量會因不一樣大小差別而變化,可能會形成影響。 故進行 歸一化 運算,使其x和y值老是介於-11之間,從而實現無論各類搖桿資源尺寸改變,被控制的物體接收到的值都是同樣的。

    public void OnDrag(PointerEventData eventData)
    {

        if (this.content)
        {
            this.controlling.Invoke(this.content.localPosition.normalized);
        }
    }

  到此事件監聽和派發實現完畢,附上完整腳本的代碼:

//FixedJoystickHandler.cs
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;

public class FixedJoystickHandler : MonoBehaviour, IDragHandler, IEndDragHandler, IBeginDragHandler
{

    [System.Serializable]
    public class VirtualJoystickEvent : UnityEvent<Vector3> { }

    public Transform content;
    public UnityEvent beginControl;
    public VirtualJoystickEvent controlling;
    public UnityEvent endControl;

    public void OnBeginDrag(PointerEventData eventData)
    {

        this.beginControl.Invoke();
    }

    public void OnDrag(PointerEventData eventData)
    {

        if (this.content)
        {
            this.controlling.Invoke(this.content.localPosition.normalized);
        }
    }

    public void OnEndDrag(PointerEventData eventData)
    {

        this.endControl.Invoke();
    }
}

  運行遊戲前,先添加事件處理器controller,對事件接收對象進行賦值,以下圖:

  

  

  本節將3d模型預製體對象p4賦值給各個control,響應的是Tank.cs中的對應接口方法。 固然控制目標也能夠爲人物角色,平時站在原地不移動的動畫播放狀態爲閒置中Idle,當操做搖桿時通知該角色 Animator 開始移動,讓 Animator 的動做狀態切換爲走路動畫。而後controling控制中的事件不斷通知角色更新當前最新的方向和位移;同理在結束控制後通知角色的 Animator 中止移動,從新切換回原先Idle的動畫播放。

  //Tank.cs
  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;

  public class Tank : MonoBehaviour {
    public List<AxleInfo> axleInfos;
    private float motor_ = 0;
    public float maxMotor;
    private float steer_ = 0;
    public float maxSteer;
    private float brake_ = 0;
    public float maxBrake = 100;
    private Transform wheelList;

    private void joyStickControl(float x, float y)
    {
        steer_ = x * maxSteer;
        motor_ = y * maxMotor;
    }

    private void playerCtrl()
    {
        //steer_ = Input.GetAxis("Horizontal") * maxSteer;
        //motor_ = Input.GetAxis("Vertical") * maxMotor;
        brake_ = 0;
        foreach (AxleInfo axleInfo in axleInfos)
        {
            if (axleInfo.leftWheel.rpm > 5 && motor_ < 0)  //正在前進時「down」
                brake_ = maxBrake;
            else if (axleInfo.leftWheel.rpm < -5 && motor_ > 0) //正在後退時「up」
                brake_ = maxBrake;
            continue;
        }
    }

    void Update () {
        playerCtrl();
        foreach(AxleInfo axleInfo in axleInfos)
        {
            if (axleInfo.motor)
            {
                axleInfo.leftWheel.motorTorque = motor_;
                axleInfo.rightWheel.motorTorque = motor_;
            }
            if(axleInfo.steering)
            {
                axleInfo.leftWheel.steerAngle = steer_;
                axleInfo.rightWheel.steerAngle = steer_;
            }
            axleInfo.leftWheel.brakeTorque = brake_;
            axleInfo.rightWheel.brakeTorque = brake_;
            if (axleInfos[1] != null && axleInfo == axleInfos[1])
            {
                WheelRotation(axleInfos[1].leftWheel);
            }
        }
    }

    private void WheelRotation(WheelCollider collider)
    {
        if (wheelList == null)
            return;
        Vector3 pos;
        Quaternion rot;
        collider.GetWorldPose(out pos, out rot);
        foreach(Transform w in wheelList)
        {
            w.rotation = rot;
        }
    }

    private void Start()
    {
        wheelList = transform.Find("WheelList");
    }

    public void beginMove()
    {
        Debug.Log("start begin move!!");
    }

    public void doMove(Vector3 drag)
    {
        joyStickControl(drag.x, drag.y);
    }

    public void endMove()
    {
        joyStickControl(0,0);
    }
}

  運行遊戲,可看到坦克能經過搖桿和按鍵盤同樣的操縱了~

  資料連接:

  《Unity3d使用UGUI開發原生虛擬搖桿

  《Unity3D遊戲開發之使用EasyTouch虛擬搖桿控制人物移動

  《教你一步步實現一個虛擬搖桿

  三 搖桿表現強化

  佔坑

  (搖桿半透明化,點擊拖動的時候全體化)

  (搖桿一開始隱藏,觸摸時才實際點位置顯示,即浮動式虛擬搖桿)

相關文章
相關標籤/搜索