Unity实现安卓虚拟摇杆多点触控

代码示例在最下面
效果如图:
在这里插入图片描述

1. Unity对于触屏操作的支持

1.1. Touch结构体

Unity使用结构体Touch定义触屏设备的输入,每一个触控点(可以理解为每一个手指)对应一个Touch,其中主要的属性如下:

属性含义
fingerId触控点的编号,在该触控点的生命周期内(从点下到抬起)是不变的
phase触控点的状态,是一个Touch.Phase枚举,包括:
Began(手指按下),
Move(手指滑动),
Stationary(手指已经按下且保持不动),
Ended(手指抬起),
Canceled(系统停止跟踪该触控点)
position触控点在屏幕的坐标(屏幕坐标系)

其他的属性参考https://docs.unity3d.com/ScriptReference/Touch.html

1.2. fingerId分配

需要注意的是对于fingerId的分配,类似于下面的过程:
使用一个数组表示哪些触控点当前被激活,初始为:
[0][0][0][0][0]
其中0表示未激活,1表示已激活。每按下一个手指时,从头寻找第一个未激活的空位,把它的索引赋给fingerId,例如按下第一个手指后,数组变为:
[1][0][0][0][0]
手指①的fingerId=0,这个时候再按下第二个手指,数组变为:
[1][1][0][0][0]
手指①的fingerId=0,手指②的fingerId=1,这个时候松开第一个手指,数组变为:
[0][1][0][0][0]
手指②的fingerId不变,还是1,这时候再按下第三个手指,数组变为:
[1][1][0][0][0]
手指②的fingerId不变,还是1,把数组的0号为给手指③,因此手指③的fingerId=0

1.3. Input类

Untiy的Input类中与Touch有关的API如下:

函数含义
Input.TouchCountint, 触控点数量,即当前按下的手指数
Input.touchesTouch[],当前所有的Touch结构体数组
Input.GetTouch(int index)返回第index个Touch,index与fingerId无关
Input.multiTouchEnabled{get;set;}是否支持多点触控

2. 多点触控

当存在多个摇杆时,需要控制每个摇杆对应的fingerId,这里把摇杆类命名为JoyStick,另外定义一个控制类TouchEvent用于管理Touch与JoyStick的对应关系,TouchEvent在每次Update中读取所有的Touch:

  1. 如果是新按下的就判断是否处于某个摇杆的可触发区域,如果是就绑定,并让该摇杆读取该Touch;
  2. 如果是已经按下的手指就判断是否有绑定的摇杆,如果有就让该摇杆读取该Touch;
  3. 如果是抬起的手指,也先判断是否有绑定并读取,然后移除这个绑定

代码如下:
TouchEvent.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TouchEvent : MonoBehaviour
{
    private Dictionary<int, JoyStick> id2JoyDic = new Dictionary<int, JoyStick>();//fingerId与摇杆的映射
    private List<JoyStick> joyList = new List<JoyStick>();//UI上所有的摇杆

    public void AddJoy(JoyStick joy)
    {
        joyList.Add(joy);//添加摇杆,由JoyStick在开始时调用
    }

    // Update is called once per frame
    void Update()
    {
        foreach(var touch in Input.touches)//遍历所有的触控点
        {
            if(touch.phase == TouchPhase.Began)//有新的触控点
            {
                foreach(var joy in joyList)
                { 
                    if (joy.inTouchArea(touch.position) //判断是否在可触控范围内
                        && !id2JoyDic.ContainsValue(joy)) //如果对应的摇杆已经有触控点就忽略
                    {
                        id2JoyDic.Add(touch.fingerId, joy);//加入新的触控点,绑定摇杆
                    }
                }
            }

            if (id2JoyDic.ContainsKey(touch.fingerId))//如果该触控点已经绑定了摇杆
            {
                id2JoyDic[touch.fingerId].ReadTouch(touch);//读取触控操作
                if(touch.phase == TouchPhase.Canceled || touch.phase == TouchPhase.Ended)//已离开的触控点
                {
                    //移除触控点
                    id2JoyDic.Remove(touch.fingerId);
                }
            }
        }
    }
}

3. 摇杆实现思路

摇杆就是一个大圈+一个小圈组成,并且有一个可以触发的区域,如图:
在这里插入图片描述
矩形白色区域代表可以开始触控的区域,当手指按下时,把大圈的位置设为Touch的position,当手指滑动时,让小圈跟着动,并且限制它相对大圈的最大距离。
代码如下:
JoyStick.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

public class JoyStick : MonoBehaviour
{
    public TouchEvent touchEvent;//多点触控管理类
    public bool isFixedArea = false;//摇杆位置是否固定
    private RectTransform touchArea;//可以触控的范围
    public RectTransform circle;//摇杆的大圈
    public RectTransform point;//摇杆的小圈
    private float maxOffset;//摇杆离中心的最大距离
    //public float xAxis = 0;
    //public float yAxis = 0;
    public UnityEvent onDown,onDrag,onUp;//按下、滑动、抬起的事件
    // Start is called before the first frame update
    void Start()
    {
        if (isFixedArea)
        {
            touchArea = circle;//如果固定位置,则把可触控区域设为大圈的区域
        }
        else
        {
            touchArea = this.GetComponent<RectTransform>();
        }
        maxOffset = Mathf.Min(circle.rect.width, circle.rect.height)/2;//计算大圈的最小半径作为小圈的最大移动距离
        touchEvent.AddJoy(this);//注册摇杆
    }

    /// <summary>
    /// 读取触控操作,由TouchEvent调用
    /// </summary>
    /// <param name="touch"></param>
    public void ReadTouch(Touch touch)
    {
        if(touch.phase == TouchPhase.Began)
        {
            //如果手指按下
            circle.position = touch.position;//把大圈移动到手指按下的位置
            onDown.Invoke();//触发按下事件
        }
        else if(touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
        {
            //如果手指已经按下
            Vector3 prePointPos = point.position;//记录之前的位置
            Vector3 offset = new Vector3(touch.position.x, touch.position.y, 0) - prePointPos;//两帧的手指偏移量
            point.position += offset;//让小圈跟着动
            if ((point.position - circle.position).magnitude > maxOffset)
            {
                point.position = circle.position + (point.position - circle.position).normalized * maxOffset;//限制摇杆的移动
            }
            //xAxis = offset.x / maxOffset;//计算归一化偏移量
            //yAxis = offset.y / maxOffset;
            //print(string.Format("xAxis: {0} -- yAxis: {1}", xAxis, yAxis));

            onDrag.Invoke();//触发滑动事件
        }
        else if(touch.phase == TouchPhase.Ended)
        {
            //如果手指抬起
            point.position = circle.position;//重置小圈的位置到大圈的中心
            onUp.Invoke();//触发抬起事件
        }
    }

    /// <summary>
    /// 判断某一点是否在可触控范围(矩形)内,由TouchEvent调用
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    public bool inTouchArea(Vector2 pos)
    {
        Rect rect = touchArea.rect;
        rect.x += touchArea.position.x;
        rect.y += touchArea.position.y;
        return rect.Contains(pos);
        
    }

    public void OnDownTest()
    {
        print("down");
    }

    public void OnDragTest()
    {
        print("drag");
    }

    public void OnUpTest()
    {
        print("up");
    }
}

4. 代码使用

TouchEven.cst挂在任意物体上,我这里挂在了EventSystem上
每一个摇杆如图中红框所示,

  1. Joy1是一个panel,它在屏幕上的大小就代表了可触发的区域
  2. circle是image,表示大圈
  3. joy是image,表示小圈
    在这里插入图片描述
    三个摇杆场景如图:
    在这里插入图片描述
    把JoyStick.cs挂在Joy1上,把circle赋给circle,joy赋给point,把TouchEvent也赋好值,如图:
    在这里插入图片描述

5. 在手机上实时调试

在手机上安装Unity Remote app,下载链接:
链接:https://pan.baidu.com/s/1moiz-4It5euHtpd86cLwgQ
提取码:z0fy
在Unity中点击edit->project settings,设置Editor,如图:
在这里插入图片描述
手机打开USB调试,连接电脑,然后运行,记得调整好分辨率
在这里插入图片描述

  • 9
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值