1. 程式人生 > >Unity3d_UGUI虛擬搖桿(簡易)

Unity3d_UGUI虛擬搖桿(簡易)

       之前的專案中有用虛擬搖桿來操縱角色移動,但是之前使用的是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);
	}
}

二、虛擬搖桿的實現

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();
		}
	}
}
該指令碼的核心處就是Update裡面,我寫的效率不是很高,可以自行改寫。


三、具體操作方式

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的簡易虛擬搖桿,沒有什麼擴充套件功能,沒有進行什麼優化,如果有什麼更好的改進方法請私信我,謝謝!