解决移动端双摇杆冲突、摇杆与按钮冲突问题
发表于2017-09-30
最近项目需求是增加双摇杆,类似王者荣耀中的左摇杆控制角色移动,右摇杆控制技能方向。
项目中没有使用EasyTouch,FingerGestures等插件,是纯代码实现的双摇杆
往常我们在PC中获取点击位置是用Input.mousePosition;而在移动端要获取多点触控,需用到Input.GetTouch(),如果对该方法不了解的,请自行查询。
双摇杆中用到的点击位置,就可以使用
GetPosition(TouchDirType.Left);
GetPosition(TouchDirType.Right);
下面脚本是解决双摇杆冲突的:
using UnityEngine; using System.Collections; using System.Collections.Generic; using System; public enum TouchDirType { Empty, Left, Right } public class TouchManager : MonoBehaviour { private static TouchManager instance; public static TouchManager Instance { get { return instance; } } float halfWith; static Dictionary<TouchDirType, Vector2> dirTypeTouch; Dictionary<int, TouchDirType> touchIndexType; public Action<TouchDirType> onTouchBegan; // 手指按下时 事件 public Action<TouchDirType> onTouchMoved; // 手指按住时 事件 public Action<TouchDirType> onTouchEnded; // 手指松开时 事件 void Awake() { if (instance == null) instance = this; else Destroy(this); } // Use this for initialization void Start() { halfWith = Screen.width * 0.5f; dirTypeTouch = new Dictionary<TouchDirType, Vector2>(); dirTypeTouch.Add(TouchDirType.Left, Vector2.zero); dirTypeTouch.Add(TouchDirType.Right, Vector2.zero); touchIndexType = new Dictionary<int, TouchDirType>(); } void LateUpdate() { for (int i = 0, length = Input.touchCount > 2 ? 2 : Input.touchCount; i < length; i ) { Touch touch = Input.GetTouch(i); if (touch.phase == TouchPhase.Began) { if (touch.position.x < halfWith) { if (touchIndexType.ContainsKey(touch.fingerId)) { touchIndexType[touch.fingerId] = TouchDirType.Left; } else { touchIndexType.Add(touch.fingerId, TouchDirType.Left); } } else { if (touchIndexType.ContainsKey(touch.fingerId)) { touchIndexType[touch.fingerId] = TouchDirType.Right; } else { touchIndexType.Add(touch.fingerId, TouchDirType.Right); } } dirTypeTouch[touchIndexType[touch.fingerId]] = touch.position; if (onTouchBegan != null) onTouchBegan(touchIndexType[touch.fingerId]); } else if (touch.phase == TouchPhase.Moved) { if (touchIndexType.ContainsKey(touch.fingerId)) { dirTypeTouch[touchIndexType[touch.fingerId]] = touch.position; if (onTouchMoved != null) onTouchMoved(touchIndexType[touch.fingerId]); } } else if (touch.phase == TouchPhase.Ended) { if (touchIndexType.ContainsKey(touch.fingerId)) { if (onTouchEnded != null) onTouchEnded(touchIndexType[touch.fingerId]); dirTypeTouch[touchIndexType[touch.fingerId]] = Vector2.zero; touchIndexType.Remove(touch.fingerId); } } } } /// <summary> /// 通过方向获取输入的坐标 /// </summary> /// <param name="dirType"></param> /// <returns></returns> public static Vector2 GetPosition(TouchDirType dirType) { #if UNITY_EDITOR return Input.mousePosition; #elif UNITY_ANDROID || UNITY_IPHONE return dirTypeTouch[dirType]; #endif } public static string leftOffset; public static string rightOffset; void OnGUI() { GUIStyle bb = new GUIStyle(); bb.normal.background = null; bb.normal.textColor = new Color(1, 0, 0); bb.fontSize = 40; GUI.Label(new Rect(0, 0, 500, 50), "LeftPosition: " GetPosition(TouchDirType.Left), bb); GUI.Label(new Rect(0, 80, 500, 50), "RightPosition: " GetPosition(TouchDirType.Right), bb); GUI.Label(new Rect(0, 160, 500, 50), "LeftOffset: " leftOffset, bb); GUI.Label(new Rect(0, 240, 500, 50), "RightOffset: " rightOffset, bb); } }
下面脚本是实现双摇杆的
using UnityEngine; using System.Collections; public class Joystick : MonoBehaviour { public TouchDirType touchDirType; bool isPress = false; bool touchActionActive = false; float h, v; public float bigCircleRadius = 100; Transform bigCircleTrans; Transform smallCircleTrans; Vector2 bigCircleStartWorldPos = Vector2.zero; Vector2 smallCircleStartLocalPos = Vector2.zero; Vector2 startPressPos; // 技能摇杆按下时的初始位置 即第一帧位置 private Vector2 offset; // 摇杆的偏移量 -1到1 public Vector2 GetOffset() { return new Vector2(h, v); } void Start() { #if UNITY_ANDROID || UNITY_IPHONE TouchManager.Instance.onTouchBegan = OnTouchBegan; #endif bigCircleTrans = transform; smallCircleTrans = transform.GetChild(0); smallCircleStartLocalPos = smallCircleTrans.localPosition; } void Destroy() { #if UNITY_ANDROID || UNITY_IPHONE TouchManager.Instance.onTouchBegan -= OnTouchBegan; #endif } void Update() { if (isPress) { PressIsTrue(); } switch (touchDirType) { case TouchDirType.Empty: break; case TouchDirType.Left: TouchManager.leftOffset = GetOffset().ToString(); break; case TouchDirType.Right: TouchManager.rightOffset = GetOffset().ToString(); break; default: break; } } public void OnPointDown() { #if UNITY_EDITOR this.isPress = true; startPressPos = TouchManager.GetPosition(touchDirType); #endif touchActionActive = true; } public void OnPointUp() { this.isPress = false; smallCircleTrans.localPosition = smallCircleStartLocalPos; touchActionActive = false; // 鼠标抬起时 将 h,v归零 h = 0; v = 0; } #region 手机端使用 public void OnTouchBegan(TouchDirType dirType) { if (dirType != touchDirType || touchActionActive == false ) return; isPress = true; startPressPos = TouchManager.GetPosition(touchDirType); } #endregion // 按下时 触发此方法 void PressIsTrue() { // UICamera.lastTouchPosition 为当前鼠标按下时的坐标(Vector2类型) if (bigCircleStartWorldPos == Vector2.zero) { bigCircleStartWorldPos = Camera.main.WorldToScreenPoint(bigCircleTrans.position); } Vector2 touchPos = TouchManager.GetPosition(touchDirType) - bigCircleStartWorldPos; // 当鼠标拖动的位置与中心位置大于bigCircleRadius时,则固定按钮位置不会超过bigCircleRadius。 bigCircleRadius为背景图片半径长度 if (Vector2.Distance(touchPos, Vector2.zero) > bigCircleRadius) { // 按钮位置为 鼠标方向单位向量 * bigCircleRadius smallCircleTrans.localPosition = touchPos.normalized * bigCircleRadius; } else { // 按钮位置为鼠标位置 smallCircleTrans.localPosition = touchPos; } // 按钮位置x轴 / 半径 的值为0-1的横向偏移量 h = smallCircleTrans.localPosition.x / bigCircleRadius; // 按钮位置y轴 / 半径 的值为0-1的纵向偏移量 v = smallCircleTrans.localPosition.y / bigCircleRadius; } }
项目使用版本:Unity5.3.4 GitHub下载地址: