解决移动端双摇杆冲突、摇杆与按钮冲突问题

发表于2017-09-30
评论1 4.8k浏览

最近项目需求是增加双摇杆,类似王者荣耀中的左摇杆控制角色移动,右摇杆控制技能方向。

项目中没有使用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下载地址:

https://github.com/654306663/TwoJoystick

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

标签: