一、效果展示
二、前言
我们制作多指触碰主要用到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:这里面我加了一个鼠标左键控制旋转,中间滑轮控制放大缩小的功能。脚本中有详细注释
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, 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
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |