一、效果展示

二、前言

我们制作多指触碰主要用到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物体,则检测uiif (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){//滑动发出射线获取碰撞到的itemMouseObj = RayDetection(Input.mousePosition);//如果没有检测到3D物体,则检测uiif (MouseObj == null)MouseObj = GetFirstPickGameObject(Input.mousePosition);//item为空不执行if (MouseObj!= null)MouseObj.OnMouseWheelFountion(value);}//鼠标拖动旋转物体碰撞的物体不为空if (Input.GetMouseButtonUp(0))MouseObj = null;return;}#endregion#region 多指触摸for (int i = 0; i < Input.touches.Length; i++){//射线检测获取ItemTouchItem touchObj = RayDetection(Input.GetTouch(i).position);//如果没有检测到3D物体,则检测uiif (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;//射线检测uiList<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#region 2D BoxClider物体射线检测Transform RayDetection(Vector3 pos){pos.z = 10;Vector3 screenPos = Camera.main.ScreenToWorldPoint(pos);RaycastHit2D hit = Physics2D.Raycast(screenPos, Vector2.zero);if (hit&& hit.transform== gearBtn){return hit.transform;}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;}//初始化layergameObject.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;}}elsetimer = 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;elseif (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, minSize, maxSize),Mathf.Clamp(localScale.z, minSize, 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, minSize, maxSize),Mathf.Clamp(localScale.y, minSize, maxSize),Mathf.Clamp(localScale.z, minSize, 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, moveobj.eulerAngles.y, 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

Unity之Touch触摸屏单指、多指触碰相关推荐

  1. Unity开发HTC vive 五、拾取和触碰

    Unity开发HTC vive 五.拾取和触碰 <div class="article_manage clearfix"><div class="art ...

  2. java只允许单一继承_问题:Java语言只允许单继承,指每个类只能有一个 ( )

    问题:Java语言只允许单继承,指每个类只能有一个 ( ) 更多相关问题 用List命令显示表文件中的当前记录的命令格式是________. WHERE子句的条件表达式中,可以匹配0个到多个字符的通配 ...

  3. Unity触摸屏TouchPhase多点触碰旋转放大缩小

    Unity触摸屏TouchPhase多点触碰旋转放大缩小 最近做了一个展厅的项目需要用到触摸屏多点触控,直接上代码 最近做了一个展厅的项目需要用到触摸屏多点触控,直接上代码 private Touch ...

  4. 【unity HoloLens2】触碰了物体但没反应,触碰了一次却调用多次,HoloLens2的touch触发机制探究

    [unity HoloLens2]触碰了物体但没反应,触碰了一次却调用多次,HoloLens2的touch触发机制探究 开发项目时发现,有时候触碰了物体却没有触发touch事件,有时候触碰了一次物体, ...

  5. unity android 触屏,Unity 移动端触摸屏操作

    using UnityEngine; using System.Collections; public class AndroidTouch : MonoBehaviour { private int ...

  6. Unity 2.Space Shooter(碰撞器Collider,WebGL,刚体中属性,(定时)实例化、销毁游戏对象,触碰OnTriggerEnter/Exit,爆炸效果,音频,文字,定时调方法)

    目录 项目介绍 WebGL发布 游戏对象设置 灯光.相机 背景 移动游戏对象 Debug 制作子弹 射击动作 清理离开边界的游戏对象 制作危险物 添加爆炸,移动小行星,作为预制件 创建游戏控制器 循环 ...

  7. 付费系列 3 - 单障碍和单触碰期权 PDE 有限差分

    本篇对单障碍 (single barrier) 期权和单触碰 (single touch) 期权用 PDE FD 做定价.用 PDE 来对其求解的难度和欧式相当,只需根据障碍水平和返还支付方式来变化一 ...

  8. cocos2dx基础篇(9)——触碰事件Touch

    [唠叨] cocos2dx游戏引擎的重点是在于移动设备的跨平台开发,而移动设备上的游戏大部分都是通过屏幕触碰来进行的.比如主菜单的按钮触碰,打飞机中飞机的触碰移动,都需要用到触碰操作.想一想之前讲的菜 ...

  9. matebook14支持触摸屏吗_matebook 14有触屏吗

    matebook 14被定义为轻薄.便携的生产力工具,它的亮点有很多,但对于有些人来说遗憾也不少,比如它不支持键盘常亮.无法刻录等等,除此之外还有哪些槽点呢?matebook 14有触屏吗?它的屏幕分 ...

  10. HK01BS单通道电容式触控芯片IC内置稳压LDO低压复位模块

    HK01BS单通道电容式触控芯片,内置稳压模块/低压复位模块,支持硬件去抖动/环境自适应算法等,有较强的抗干扰性能:可通过引脚配置成多种模式. 泛海微HK01BS 应用电路简单,灵敏度调整范围大,且工 ...

最新文章

  1. Dokcer启动2个mysql容器
  2. 额外篇| Python制作词云
  3. linux java main 参数设置_Java虚拟机参数设置
  4. 12月23号 Foundation库NSString操作
  5. mysql 严格模式查看,如何查找和禁用MySQL严格模式?
  6. Spring Boot CommandLineRunner和ApplicationRunner
  7. 用生动的例子花式解释:python类中一定需要有 __init__方法么?没有会怎样?
  8. Linux dd 命令
  9. python isinstance_Python之isinstance | 学步园
  10. vs该文件没有与之关联的应用来执行该操作_Hadoop大数据实战系列文章之Zookeeper...
  11. 2. crontab 的使用
  12. java高并发之线程池
  13. Flash Builder 4.6(安装破解)
  14. 常用的vue组件库总结
  15. 有道词典的本地/扩展/离线词库
  16. JAVA之基数排序LSD顺序
  17. UltraVNC 使用,内网局域网远程控制
  18. cesm2(clm5.0)移植方法
  19. uni-ui简单入门教程 - 如何用HBuilderX为uni-app项目启用uni-ui扩展组件?
  20. arx开发版本对照表

热门文章

  1. vscode 设置关键字高亮显示
  2. 2019 谷歌dat.GUI组件对中文的支持
  3. 一台阿里云ECS下的CIDI方案 最具性价比的简化DevOps自动化部署方案
  4. 语音识别/合成开源项目
  5. 金额转换,阿拉伯数字的金额转换成中国传统的形式
  6. Android 高通平台指南针跳变,竖起过程数据不准,Android getRotationMatrix和getOrientation使用记录
  7. Linux 设置代理
  8. 【解决】RuntimeError:Trying to backward throughthe graph a second time
  9. ps 改变图片中的文字
  10. 海外app应用市场分享!