Unity之Touch触摸屏单指、多指触碰

一、效果展示

二、前言

我们制作多指触碰主要用到Unity中已经封装好的Touch类来制作,首先来看看unity官方对于Touch的描述:地址

在移动设备上,Input 类提供对触摸屏、加速度计和地理/位置输入的访问。

通过 iOS 键盘可以访问移动设备上的键盘。

iPhone、iPad 和 iPod Touch 设备最多可跟踪五根手指同时触摸屏幕。可通过访问 Input.touches 属性数组来获取在最后一帧期间触摸屏幕的每根手指的状态。

Android 设备对其跟踪的手指数量没有统一限制。相反,此限制因设备而异,可能是旧设备上的双手指触摸到某些新设备上的五指触摸。

通过上边的描述我们可以知道几点重要信息

1.iPhone、iPad 和 iPod Touch 设备最多可跟踪五根手指同时触摸屏幕

2.Android 设备跟踪的手指数量没有统一限制,这个限制是由设备决定的

3.所有的touch操作是在最后一帧来处理。

 Input类中要使用到的函数:

1.Input.touches :这是一个Touch[]数组,里面存储了所有手指在屏幕的触摸(Touch类)

2.Input. touchCount: 获取屏幕中触摸的数量

3.Input.GetTouch(int i):获取Input.touches数组中的touch类,这里要注意,当触摸在屏幕抬起时,Touch在Input.touches数组下标会变更。

举例:当前有个三个手指触摸 A、B、C,在Input.touches 数组中分别对应其下标为0,1,2.即:A(下标 0)、B(下标 1)、C(下标 2),当我们抬起手指A,然后在按下,此时他们的下标就变成,B(下标 0)、C(下标 1)、A(下标 2)。

所以我们在做多指时不能通过索引来获取唯一的touch类,因为当一个touch抬起时,后面的touch索引会一次往前进一位。为了避免这个不唯一问题可以用到Touch类的fingerld。

 好的现在我们再来看一下Touch类的一些主要函数:

1.fingerld: 触摸的唯一索引,上面说过touch的索引在生命周期为结束时会进行更改,不能使用索引代表其唯一性,fingerld在生命周期结束前是不会更改的。

2.position:   触摸屏幕的位置

3.deltatime: 从最后状态到目前状态所经过的时间

4.tapCount: 点击数。Andorid设备不对点击计数,这个方法总是返回1

5.deltaPosition: 自最后一帧所改变的屏幕位置

6.phase相位,也即屏幕操作状态,其中phase(状态)有以下这几种:
    (1) Began 手指刚刚触摸屏幕

    (2) Moved 手指在屏幕上移动

    (3) Stationary 手指触摸屏幕,但自最后一帧没有移动

    (4) Ended 手指离开屏幕

    (5) Canceled系统取消触控跟踪,原因如把设备放在脸上或同时超过5个触摸点

三、案例实现

1.单指触碰到指定区域时控制物体旋转,双指触碰到指定区域控制物体放大缩小,并且不受其他指头触摸影响

思路:因为Touch的属性是在最后一帧来处理,所以在unity生命周期中我们要在Update中执行,我们通过for循环来遍历Input.touches中所有的touch,然后让相机向Touch的position位置发射一条射线,如果检测碰撞(通过层级判断,如:item层)到可以控制的物体身上,就获取这个物体身上的item类来执行对应的方法(单指或双指方法)。这里每个可以操控的物体都有一个Item脚本,这个脚本中存在单指和双指的执行方法

具体实现:

DoubleTouchManager类,负责遍历touch射线检测是否碰撞到物体

PS:这里面我加了一个鼠标左键控制旋转,中间滑轮控制放大缩小的功能。脚本中有详细注释

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

/// <summary>
/// 多指操作管理器
/// </summary>
public class DoubleTouchManager : MonoBehaviour
{
    public LayerMask layer;

    static DoubleTouchManager intance;
    public static DoubleTouchManager GetIntance()
    {
        return intance;
    }

    private void Awake()
    {
        intance = this;
    }
    void Start()
    {
    }

    TouchItem _mouseObj;//射线检测获取item

    //封装 用于item判断是否10秒后位置复原
    public TouchItem MouseObj { get => _mouseObj; set => _mouseObj = value; }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Debug.Log("按下ESC退出");
            Application.Quit();
        }

        #region 鼠标控制旋转缩放
        //没有手指触摸时才可执行鼠标
        if (Input.touches.Length <= 0)
        {
            //鼠标左键按下 记录鼠标位置
            if (Input.GetMouseButtonDown(0))
            {
                MouseObj = RayDetection(Input.mousePosition);
                Debug.Log("1: "+MouseObj);

                //如果没有检测到3D物体,则检测ui
                if (MouseObj == null)
                    MouseObj = GetFirstPickGameObject(Input.mousePosition);

                Debug.Log(MouseObj);

                //item为空不执行
                if (MouseObj != null)
                    MouseObj.OnMouseDownFountion();//修改item的oldPos值;
            }

            //鼠标拖动旋转物体碰撞的物体不为空
            if (Input.GetMouseButton(0) && MouseObj != null)
                MouseObj.OnMouseFountion();


            //鼠标滚轮控制放大缩小
            float value = Input.GetAxis("Mouse ScrollWheel");
            if (value != 0)
            {
                //滑动发出射线获取碰撞到的item
                TouchItem _obj = RayDetection(Input.mousePosition);

                //如果没有检测到3D物体,则检测ui
                if (MouseObj == null)
                    MouseObj = GetFirstPickGameObject(Input.mousePosition);

                //item为空不执行
                if (_obj != null)
                    _obj.OnMouseWheelFountion(value);
            }

            //鼠标拖动旋转物体碰撞的物体不为空
            if (Input.GetMouseButtonUp(0))
                MouseObj = null;

            return;
        }
        #endregion

        #region 多指触摸
        for (int i = 0; i < Input.touches.Length; i++)
        {
            //射线检测获取Item
            TouchItem touchObj = RayDetection(Input.GetTouch(i).position);
            //如果没有检测到3D物体,则检测ui
            if (touchObj == null)
                touchObj = GetFirstPickGameObject(Input.GetTouch(i).position);

            if (touchObj != null)
            {
                if (!touchObj.istouch1)
                {
                    //手指1不存在时,执行
                    touchObj.Touch1(i);
                }
                else if (touchObj.istouch1 && touchObj.touch1ID == Input.GetTouch(i).fingerId)
                {
                    //手指1存在 并且这个手指id相同时
                    touchObj.Touch1(i);
                }
                else
                {
                    //手指1存在 手指不相同
                    touchObj.Touch2(i);
                }

                Debug.Log("检测到物体");
            }
        }
        #endregion


    }

    #region 3D物体射线检测
    TouchItem RayDetection(Vector2 pos)
    {
        Ray ray = Camera.main.ScreenPointToRay(pos);
        RaycastHit hit;

        if (Physics.Raycast(ray,out hit,int.MaxValue,layer))
            return hit.transform.GetComponent<TouchItem>();

        return null;

    }

    #endregion

    #region UI射线检测
    /// <summary>
    /// 获取点击的UI类型Item
    /// </summary>
    /// <param name="position">点击屏幕坐标</param>
    /// <returns></returns>
    public TouchItem GetFirstPickGameObject(Vector2 position)
    {
        EventSystem eventSystem = EventSystem.current;
        PointerEventData pointerEventData = new PointerEventData(eventSystem);
        pointerEventData.position = position;
        //射线检测ui
        List<RaycastResult> uiRaycastResultCache = new List<RaycastResult>();
        eventSystem.RaycastAll(pointerEventData,uiRaycastResultCache);
        if (uiRaycastResultCache.Count > 0 && uiRaycastResultCache[0].gameObject.GetComponent<TouchItem>() != null)
            return uiRaycastResultCache[0].gameObject.GetComponent<TouchItem>();
        return null;
    }
    #endregion

}

Item类:

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

/// <summary>
/// 触摸item 基类
/// </summary>
public class TouchItem : MonoBehaviour
{
    //单双指位置
    protected Vector2 oldPos1;
    protected Vector2 oldPos2;

    [SerializeField] string layer = "touchItem";  //层级

    [Range(0,10f)]
    [SerializeField] protected float rotSpeed = 10;  //旋转速度
    protected Vector3 originRot;//原旋转位置
    protected Vector3 originScale;//原大小
    public bool istouch1; //触摸1是否存在
    public bool istouch2; //触摸2是否存在

    public float minSize = 0.9f;
    public float maxSize = 1.3f;
    public Transform moveobj;

    void Start()
    {
        InitData();//参数初始化
    }

    void Update()
    {
        Recover();//无人操作复原
    }

    #region 初始化参数
    /// <summary>
    /// 初始化参数
    /// </summary>
    protected virtual void InitData()
    {
        istouch1 = false;
        istouch2 = false;
        timer = Time.realtimeSinceStartup;

        //3D移动物体原旋转和大小
        if (moveobj != null)
        {
            originRot = moveobj.localEulerAngles;
            originScale = moveobj.localScale;
        }


        //初始化layer
        gameObject.layer = LayerMask.NameToLayer(layer);
    }
    #endregion

    #region Update无人操作复原
    protected float timer = 0;
    /// <summary>
    /// Update无人操作复原
    /// </summary>
    protected virtual void Recover()
    {
        //当没有触摸时并且鼠标选中的item不是自己时,每过10秒执行位置大小复原
        if (touch1ID == -1 && DoubleTouchManager.GetIntance().MouseObj != this)
        {
            if (Time.realtimeSinceStartup - timer >= 10f)
            {
                if (moveobj != null)
                {
                    moveobj.eulerAngles = originRot;
                    moveobj.localScale = originScale;
                }

                timer = Time.realtimeSinceStartup;
            }
        }
        else
            timer = Time.realtimeSinceStartup;
    }
    #endregion

    #region 单指操作
    public int touch1ID = -1;
    protected int touch1Index = -1;
    public virtual void Touch1(int i)
    {
        //当有双指时 不执行旋转
        if (istouch2)
            return;

        touch1Index = i;
        istouch1 = true;

        //防止多次触碰出现问题
        if (touch1Index >= Input.touchCount)
        {
            //Debug.Log("单指 防止多次触碰出现问题");
            touch1ID = -1;
            touch1Index = -1;
            istouch1 = false;
            return;
        }

        Touch touch = Input.GetTouch(touch1Index);

        //判断id是否相同
        if (touch1ID == -1)
            touch1ID = touch.fingerId;
        else
            if (touch1ID != touch.fingerId)
        {
            touch1ID = -1;
            touch1Index = -1;
            istouch1 = false;
            //id不同 不执行
            return;
        }

        //单指操作 
        if (touch.phase == TouchPhase.Began)
            SingleBeganOperation(touch.position); //单指操作

        if (touch.phase == TouchPhase.Moved)
        {
            SingleMovedOperation(touch.position);
            //Debug.Log("旋转");
        }

        //手指抬起 或者系统取消对触摸的跟踪
        if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
        {
            SingleEndOperation(); //抬起
            //Debug.Log("手指抬起 或者系统取消对触摸的跟踪");
        }

    }
    #endregion

    #region 双指操作
    protected int touch2ID = -1;

    
    public virtual void Touch2(int i)
    {
        istouch2 = true;

        //防止多次触碰出现问题
        if (touch1Index >= Input.touchCount || i >= Input.touchCount)
        {
            //Debug.Log("多指: 防止多次触碰出现问题");
            touch1ID = -1;
            touch2ID = -1;
            istouch1 = false;
            istouch2 = false;
            return;
        }

        Touch touch1 = Input.GetTouch(touch1Index);
        Touch touch2 = Input.GetTouch(i);

        //判断id是否相同
        if (touch1ID == -1 || touch2ID == -1)
        {
            touch1ID = touch1.fingerId;
            touch2ID = touch2.fingerId;
        }
        else
        {
            if (touch1ID != touch1.fingerId || touch2ID != touch1.fingerId)
            {
                //id不同 不执行 
                touch1ID = -1;
                touch2ID = -1;
                istouch1 = false;
                istouch2 = false;
                return;
            }
        }


        //双指操作
        if (touch2.phase == TouchPhase.Began)
        {
            Debug.Log("多指************1");

            oldPos1 = touch1.position;
            oldPos2 = touch2.position;
            return;
        }

        if (touch1.phase == TouchPhase.Moved || touch2.phase == TouchPhase.Moved)
        {
            Debug.Log("多指************2");

            float oldDistance = Vector2.Distance(oldPos1,oldPos2);  //计算原先两指的距离
            float newDistance = Vector2.Distance(touch1.position,touch2.position);  //当前移动后两指的距离

            //(新距离)减去(旧距离)得出的差如果是负数的话缩小,正数就放大
            float offset = newDistance - oldDistance;
            //Debug.Log("3: 判断两指有一个在运动时: " + offset);

            //放大因子, 一个像素按 0.01倍来算(100可调整)
            float scaleFactor = offset / 100;
            //计算物体scale要放大的值
            Vector3 localScale = moveobj.localScale + (Vector3.one * scaleFactor);

            //设置放大缩小的范围值
            Vector3 scale = new Vector3(Mathf.Clamp(localScale.x,minSize,maxSize),Mathf.Clamp(localScale.y,Mathf.Clamp(localScale.z,maxSize));

            moveobj.localScale = scale;//赋值
            Debug.Log("大小: " + scale);

            //记住最新的触摸点位置,下次使用  
            oldPos1 = touch1.position;
            oldPos2 = touch2.position;
        }

        if (touch1.phase == TouchPhase.Ended || touch2.phase == TouchPhase.Ended)
        {
            touch1ID = -1;
            touch2ID = -1;
            istouch1 = false;
            istouch2 = false;
            //Debug.Log("手指抬起2");
        }

        if (touch1.phase == TouchPhase.Canceled || touch2.phase == TouchPhase.Canceled)
        {
            touch1ID = -1;
            touch2ID = -1;
            istouch1 = false;
            istouch2 = false;
            //Debug.Log("系统取消对触摸的跟踪2");
        }

    }
    #endregion

    #region 鼠标控制旋转和放大缩小

    public virtual void OnMouseDownFountion()
    {
        //Debug.Log("按下");

        //开始按下操作
        SingleBeganOperation(Input.mousePosition);
    }

    //左键控制旋转
    public virtual void OnMouseFountion()
    {
        //Debug.Log("持续");
        SingleMovedOperation(Input.mousePosition);
    }

    //左键抬起
    public virtual void OnMouseUpFountion()
    {
        //Debug.Log("抬起");
        SingleEndOperation();
    }

    //滑轮控制放大
    public virtual void OnMouseWheelFountion(float value)
    {
        Debug.Log("滑轮");

        //计算物体scale要放大的值
        Vector3 localScale = moveobj.localScale + Vector3.one * value;

        //设置放大缩小的范围值
        Vector3 scale = new Vector3(Mathf.Clamp(localScale.x,maxSize));

        moveobj.localScale = scale;//赋值
        //Debug.Log("大小: " + scale);

    }
    #endregion

    #region 单指 (按下、持续按下、抬起操作)
    /// <summary>
    /// 开始操作
    /// </summary>
    /// <param name="pos">位置</param>
    protected virtual void SingleBeganOperation(Vector3 pos)
    {
        //Debug.Log("按下");
        oldPos1 = pos;
    }

    /// <summary>
    /// 持续操作
    /// </summary>
    /// <param name="pos">位置</param>
    protected virtual void SingleMovedOperation(Vector3 pos)
    {
        //Debug.Log("持续");
        Vector3 newRot = new Vector3((pos.y - oldPos1.y),-(pos.x - oldPos1.x),0) * Time.deltaTime * rotSpeed;
        var rotation = Quaternion.Euler(newRot);
        moveobj.rotation *= rotation;
        //moveobj.eulerAngles = new Vector3(moveobj.eulerAngles.x,moveobj.eulerAngles.y,0);
        moveobj.eulerAngles = new Vector3(0,0); //只让Y轴旋转
        oldPos1 = pos;
    }

    /// <summary>
    /// 结束操作
    /// </summary>
    /// <param name="pos">=位置</param>
    protected virtual void SingleEndOperation()
    {
        touch1ID = -1;
        touch1Index = -1;
        istouch1 = false;
    }

    #endregion

}

场景布局:

 然后打包即可,注意在触摸屏中Input.GetMouse()这种获取鼠标单机事件的函数可以触发手指触摸,但是touch在没有触摸功能的设备上使用鼠标是无法检测的,所以要测试这个效果需要打包到有触摸功能的设备上才可以执行。

四、项目包

https://pan.baidu.com/s/1Im9JCz83WCdv-cK8f6L8Qg 
提取码:syq1 

原文地址:https://blog.csdn.net/qq_42345116

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


当我们远离最新的 iOS 16 更新版本时,我们听到了困扰 Apple 最新软件的错误和性能问题。
欧版/美版 特别说一下,美版选错了 可能会永久丧失4G,不过只有5%的概率会遇到选择运营商界面且部分必须连接到iTunes才可以激活
一般在接外包的时候, 通常第三方需要安装你的app进行测试(这时候你的app肯定是还没传到app store之前)。
前言为了让更多的人永远记住12月13日,各大厂都在这一天将应用变灰了。那么接下来我们看一下Flutter是如何实现的。Flutter中实现整个App变为灰色在Flutter中实现整个App变为灰色是非常简单的,只需要在最外层的控件上包裹ColorFiltered,用法如下:ColorFiltered(颜色过滤器)看名字就知道是增加颜色滤镜效果的,ColorFiltered( colorFilter:ColorFilter.mode(Colors.grey, BlendMode.
flutter升级/版本切换
(1)在C++11标准时,open函数的文件路径可以传char指针也可以传string指针,而在C++98标准,open函数的文件路径只能传char指针;(2)open函数的第二个参数是打开文件的模式,从函数定义可以看出,如果调用open函数时省略mode模式参数,则默认按照可读可写(ios_base:in | ios_base::out)的方式打开;(3)打开文件时的mode的模式是从内存的角度来定义的,比如:in表示可读,就是从文件读数据往内存读写;out表示可写,就是把内存数据写到文件中;
文章目录方法一:分别将图片和文字置灰UIImage转成灰度图UIColor转成灰度颜色方法二:给App整体添加灰色滤镜参考App页面置灰,本质是将彩色图像转换为灰度图像,本文提供两种方法实现,一种是App整体置灰,一种是单个页面置灰,可结合具体的业务场景使用。方法一:分别将图片和文字置灰一般情况下,App页面的颜色深度是24bit,也就是RGB各8bit;如果算上Alpha通道的话就是32bit,RGBA(或者ARGB)各8bit。灰度图像的颜色深度是8bit,这8bit表示的颜色不是彩色,而是256
领导让调研下黑(灰)白化实现方案,自己调研了两天,根据网上资料,做下记录只是学习过程中的记录,还是写作者牛逼
让学前端不再害怕英语单词(二),通过本文,可以对css,js和es6的单词进行了在逻辑上和联想上的记忆,让初学者更快的上手前端代码
用Python送你一颗跳动的爱心
在uni-app项目中实现人脸识别,既使用uni-app中的live-pusher开启摄像头,创建直播推流。通过快照截取和压缩图片,以base64格式发往后端。
商户APP调用微信提供的SDK调用微信支付模块,商户APP会跳转到微信中完成支付,支付完后跳回到商户APP内,最后展示支付结果。CSDN前端领域优质创作者,资深前端开发工程师,专注前端开发,在CSDN总结工作中遇到的问题或者问题解决方法以及对新技术的分享,欢迎咨询交流,共同学习。),验证通过打开选择支付方式弹窗页面,选择微信支付或者支付宝支付;4.可取消支付,放弃支付会返回会员页面,页面提示支付取消;2.判断支付方式,如果是1,则是微信支付方式。1.判断是否在微信内支付,需要在微信外支付。
Mac命令行修改ipa并重新签名打包
首先在 iOS 设备中打开开发者模式。位于:设置 - 隐私&安全 - 开发者模式(需重启)
一 现象导入MBProgressHUD显示信息时,出现如下异常现象Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_MBProgressHUD", referenced from: objc-class-ref in ViewController.old: symbol(s) not found for architecture x86_64clang: error: linker command failed wit
Profiles >> 加号添加 >> Distribution >> "App Store" >> 选择 2.1 创建的App ID >> 选择绑定 2.3 的发布证书(.cer)>> 输入描述文件名称 >> Generate 生成描述文件 >> Download。Certificates >> 加号添加 >> "App Store and Ad Hoc" >> “Choose File...” >> 选择上一步生成的证书请求文件 >> Continue >> Download。
今天有需求,要实现的功能大致如下:在安卓和ios端实现分享功能可以分享链接,图片,文字,视频,文件,等欢迎大佬多多来给萌新指正,欢迎大家来共同探讨。如果各位看官觉得文章有点点帮助,跪求各位给点个“一键三连”,谢啦~声明:本博文章若非特殊注明皆为原创原文链接。