Unity3d_UGUI虛擬搖桿(簡易)
阿新 • • 發佈:2019-01-24
之前的專案中有用虛擬搖桿來操縱角色移動,但是之前使用的是EasyTouch,屬於NGUI下的一個外掛,但是本身專案是基於UGUI的,覺得這樣摻雜在一起有些不倫不類,就自己用UGUI做了一個簡易的虛擬搖桿(可以實現給角色移動指令碼發出搖桿的偏移引數類似於EasyTouch),若要實現更加複雜的功能,可以參照EasyTouch自行編寫。
不多說下面開始實現步驟:(寫這篇文章的時候離製作有些久了,有些地方可能有遺忘或者錯誤請見諒)
一、UGUI上接受拖拽事件。
我們專案基於Android平臺,所以要考慮一些點選,拖動什麼的能不能得到相應,然後在網上搜到了這篇帖子:
這是監聽事件的指令碼:EventTriggerListener.cs
using UnityEngine; using System.Collections; using UnityEngine.EventSystems; public class EventTriggerListener : UnityEngine.EventSystems.EventTrigger { public delegate void VoidDelegate (GameObject go); public VoidDelegate onClick; public VoidDelegate onDown; public VoidDelegate onEnter; public VoidDelegate onExit; public VoidDelegate onUp; public VoidDelegate onSelect; public VoidDelegate onUpdateSelect; static public EventTriggerListener Get (GameObject go) { EventTriggerListener listener = go.GetComponent<EventTriggerListener>(); if (listener == null) listener = go.AddComponent<EventTriggerListener>(); return listener; } public override void OnPointerClick(PointerEventData eventData) { if(onClick != null) onClick(gameObject); } public override void OnPointerDown (PointerEventData eventData) { if(onDown != null) onDown(gameObject); } public override void OnPointerEnter (PointerEventData eventData) { if(onEnter != null) onEnter(gameObject); } public override void OnPointerExit (PointerEventData eventData) { if(onExit != null) onExit(gameObject); } public override void OnPointerUp (PointerEventData eventData) { if(onUp != null) onUp(gameObject); } public override void OnSelect (BaseEventData eventData) { if(onSelect != null) onSelect(gameObject); } public override void OnUpdateSelected (BaseEventData eventData) { if(onUpdateSelect != null) onUpdateSelect(gameObject); } }
二、虛擬搖桿的實現
該指令碼的核心處就是Update裡面,我寫的效率不是很高,可以自行改寫。using UnityEngine; using System.Collections; using UnityEngine.UI; using UnityEngine.EventSystems; using UnityEngine.Events; public class JoyStick : MonoBehaviour { public float fJoyTouchRadius; public float fJoyAreaRadius; private Image joyTouchImage; private Image jouAreaImage; private bool bIsActived;//是否觸發 public bool BIsActived { get{ return bIsActived; } } private bool bIsLocked = false;//是否上鎖 public bool BIsLocked { get{ return bIsLocked; } set { bIsLocked = value; if(bIsLocked) { if(joyTouchImage) { joyTouchImage.transform.position = initPos; //將偏移量歸零 JoyStickAxis = Vector2.zero; //角色移動函式 if(joyStickMoveEnd != null) joyStickMoveEnd(); } } } } private float fDragRadius;//可拖動的半徑 private Vector3 initPos;//初始位置 private float fScreenRate;//螢幕比率 private Vector2 JoyStickAxis;//用來傳值給角色控制器 //角色移動代理函式 public delegate void JoyStickMove(Vector2 joyStickOffset); public static event JoyStickMove joyStickMove; //角色停止移動代理函式 public delegate void JoyStickMoveEnd(); public static event JoyStickMoveEnd joyStickMoveEnd; void Start () { //我這邊自己做的自適應,後面的GlobalManager.iScreenWitdh是你預設的螢幕寬度,其實可以用UGUI裡面的 //Horizontal Layout Group來控制自適應 fScreenRate = (float)Screen.width / GlobalManager.iScreenWitdh; joyTouchImage = transform.FindChild("JoyTouch").GetComponent<Image>(); jouAreaImage = transform.FindChild("JoyArea").GetComponent<Image>(); //設定joystick的大小 joyTouchImage.rectTransform.localScale = new Vector2 (fJoyTouchRadius, fJoyTouchRadius); jouAreaImage.rectTransform.localScale = new Vector2 (fJoyAreaRadius, fJoyAreaRadius); //可拖拽的半徑 fDragRadius = jouAreaImage.rectTransform.rect.width * fJoyAreaRadius * fScreenRate * 0.5f; initPos = new Vector3 (1, 1, 0) * fDragRadius * 1.2f;//1.2距離側邊的距離放大1.2倍,這邊可以自己後面更改 joyTouchImage.transform.position = initPos; jouAreaImage.transform.position = initPos; //給joytouch註冊事件 EventTriggerListener.Get (joyTouchImage.gameObject).onDown = OnJoyTouchDown; EventTriggerListener.Get (joyTouchImage.gameObject).onUp = OnJoyTouchUp; //給joyarea註冊事件 EventTriggerListener.Get (jouAreaImage.gameObject).onDown = OnJoyTouchDown; EventTriggerListener.Get (jouAreaImage.gameObject).onUp = OnJoyTouchUp; } void Update() { #if UNITY_ANDROID // if (Input.touchCount > 1) // return; //這邊會有一個bug就是當你在拖拽搖桿的時候,點選螢幕別的地方,搖桿會向點選處偏移 //我沒有找到很好的解決方法,暫時是用多點觸控遮蔽的方法 #endif if(bIsActived && !bIsLocked) { //joy世界座標座標 Vector3 wJoyTouchPos = joyTouchImage.transform.position; //滑鼠螢幕座標 Vector3 mScreenPosition = new Vector3 (Input.mousePosition.x, Input.mousePosition.y, wJoyTouchPos.z); //獲得滑鼠和物件之間的偏移量,拖拽時相機應該保持不動 Vector3 offset = wJoyTouchPos - mScreenPosition; //物件新座標 joyTouchImage.transform.position -= offset; //判斷與初始位置的距離,之所以不用世界座標是因為本身半徑要轉化為世界座標的半徑,麻煩 float fDis = Vector3.Distance(joyTouchImage.transform.position, initPos); //當相對位置大於可拖動的半徑的時候,按比例縮減 if(fDis > fDragRadius) { joyTouchImage.transform.position = initPos + (joyTouchImage.transform.position - initPos) * fDragRadius / fDis; } //虛擬搖桿偏移量 JoyStickAxis.x = (joyTouchImage.transform.position.x - initPos.x) / fDragRadius; JoyStickAxis.y = (joyTouchImage.transform.position.y - initPos.y) / fDragRadius; //角色移動函式 if(joyStickMove != null) joyStickMove(JoyStickAxis); } } //當觸碰虛擬搖桿的進入 private void OnJoyTouchDown(GameObject go) { if(go == joyTouchImage.gameObject || go == jouAreaImage.gameObject) { bIsActived = true; } } //當觸碰虛擬搖桿的退出 private void OnJoyTouchUp(GameObject go) { if(go == joyTouchImage.gameObject || go == jouAreaImage.gameObject) { bIsActived = false; //回到原位 joyTouchImage.transform.position = initPos; //將偏移量歸零 JoyStickAxis = Vector2.zero; //角色停止移動函式 if(joyStickMoveEnd != null) joyStickMoveEnd(); } } }
三、具體操作方式
1.在canvas下建立一個空的JoyStick,該joyStick放在canvas的最下面,否則會被其他控制元件覆蓋掉(UGUI就是這點很煩不能更改控制元件的Depth)。
2.在joystick下建立2個sprite,給兩張轉為2dSperite的圖片(joyTouch在joyArea的下面)輸入縮放大小,本來我這邊還有引數設定初始位置,但是後面做自適應找到一個比較適中的值就去掉了,需要的可以自行編寫。
3.在角色控制移動指令碼處
加入:
void OnEnable()
{
JoyStick.joyStickMove += JoystickMove;
JoyStick.joyStickMoveEnd += JoystickMoveEnd;
}
void OnDisable()
{
JoyStick.joyStickMove -= JoystickMove;
JoyStick.joyStickMoveEnd -= JoystickMoveEnd;
}
還有就是:
public void JoystickMoveEnd()
{
//搖桿停止移動響應函式
}
public void JoystickMove(Vector2 joyPos)
{
//搖桿開始移動響應函式,這邊的joyPos就是搖桿偏移量
}
差不多就做好了一個UGUI的簡易虛擬搖桿,沒有什麼擴充套件功能,沒有進行什麼優化,如果有什麼更好的改進方法請私信我,謝謝!