解决移动端双摇杆冲突、摇杆与按钮冲突问题
发表于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下载地址:

