本人以前写过一篇关于UI的基础手势的文章,文章在这里:传送门。但那个限制过多,只支持UI,不支持2D、3D物体,不支持多人操作……前几天在做一个关于TUIO的项目时,突然有了一个想法,经过了几次测试证明了想法大致是可行的。
2D和3D相比较UGUI,需要添加Colloder,2D添加2D碰撞体,3D添加3D碰撞体,同时主相机添加上边的两个组件,实现2D就添加2D的Physics 2D Raycaster组件,3D的就添加3D的Physics Raycaster组件;另外场景必须要有EventSystem。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
internal class EntityUGUI : MonoBehaviour, IDragHandler, IPointerDownHandler, IPointerUpHandler
{
private RectTransform rectTransform;
#region 触摸点
///
/// 触摸事件参数集合
///
private List pointerEventDatas = new List(2);
///
/// Touch触摸点1的位置
///
private Vector2 touch00Postion;
///
/// Touch触摸点2的位置
///
private Vector2 touch01Postion;
#endregion
#region 移动
///
/// 偏移
///
private Vector2 pressOffSet;
///
/// 手势移动阈值
///
private static float gestureMoveThreshold = 0.1f;
#endregion
#region 旋转
///
/// 手势旋转速度
///
private static float gestureRotateSpeed = 100.0f;
///
/// 手势旋转阈值
///
private static float gestureRotateThreshold = 0.3f;
#endregion
#region 缩放
///
/// 缩放速度
///
private static float scaleSpeed = 1.0f;
///
/// 缩放阈值
///
private static float scaleThreshold = 1f;
///
/// 最大缩放倍数
///
private static float maxScaleSize = 5.0f;
///
/// 最小缩放倍数
///
private static float minScaleSize = 1.0f;
#endregion
#region Unity
private void Start()
{
this.rectTransform = this.transform as RectTransform;
}
#endregion
#region interface
///
/// 当按下时
///
///
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
//鼠标左键:-1;鼠标右键:-2;鼠标中键:-3
this.pointerEventDatas.Add(eventData);
//偏移
if (this.pressOffSet == Vector2.zero)
this.pressOffSet = this.rectTransform.anchoredPosition - eventData.pressPosition;
}
///
/// 当拖拽时
///
///
void IDragHandler.OnDrag(PointerEventData eventData)
{
if (this.pointerEventDatas.Count <= 0)
return;
if (this.pointerEventDatas.Count == 1)
{
Vector2 tarPos = eventData.position + this.pressOffSet;
//限制位置
if (Vector2.Distance(this.rectTransform.anchoredPosition, tarPos) <= EntityUGUI.gestureMoveThreshold)
return;
this.rectTransform.anchoredPosition = tarPos;
}
else if (this.pointerEventDatas.Count == 2)
{
#region 初始化位置
if (this.touch00Postion == Vector2.zero && this.touch01Postion == Vector2.zero)
{
this.touch00Postion = this.pointerEventDatas[0].position;
this.touch01Postion = this.pointerEventDatas[1].position;
return;
}
#endregion
this.Rotating();
this.Zoom();
#region 更新位置
this.touch00Postion = this.pointerEventDatas[0].position;
this.touch01Postion = this.pointerEventDatas[1].position;
#endregion
}
}
///
/// 当抬起时
///
///
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
for (int i = 0; i < this.pointerEventDatas.Count; i++)
if (this.pointerEventDatas[i].pointerId == eventData.pointerId)
{
this.pointerEventDatas.RemoveAt(i);
break;
}
//偏移归零
if (this.pointerEventDatas.Count <= 0)
this.pressOffSet = Vector2.zero;
//更新偏移
else if (this.pointerEventDatas.Count == 1)
this.pressOffSet = this.rectTransform.anchoredPosition - this.pointerEventDatas[0].position;
}
#endregion
#region PrivateFunction
///
/// 旋转
///
private void Rotating()
{
//上一帧方向
Vector2 lastDir = (this.touch00Postion - this.touch01Postion).normalized;
//当前帧方向
Vector2 currentDir = (this.pointerEventDatas[0].position - this.pointerEventDatas[1].position).normalized;
//角度差
float angle = this.VectorAngle(lastDir, currentDir);
if (angle < -EntityUGUI.gestureRotateThreshold)
this.rectTransform.Rotate(Vector3.forward, Time.deltaTime * EntityUGUI.gestureRotateSpeed);
else if (angle > EntityUGUI.gestureRotateThreshold)
this.rectTransform.Rotate(Vector3.forward, -Time.deltaTime * EntityUGUI.gestureRotateSpeed);
}
///
/// 缩放
///
private void Zoom()
{
//上一帧距离
float lastDistance = Vector2.Distance(this.touch00Postion, this.touch01Postion);
//当前帧距离
float currentDistance = Vector2.Distance(this.pointerEventDatas[0].position, this.pointerEventDatas[1].position);
//差值
float difference = lastDistance - currentDistance;
if (difference < -EntityUGUI.scaleThreshold)
{
this.rectTransform.localScale = Vector3.Lerp(this.rectTransform.localScale, Vector3.one * EntityUGUI.maxScaleSize, Time.deltaTime * EntityUGUI.scaleSpeed);
if (Vector3.Distance(this.rectTransform.localScale, Vector3.one * EntityUGUI.maxScaleSize) <= 0.01f)
this.rectTransform.localScale = Vector3.one * EntityUGUI.maxScaleSize;
}
else if (difference > EntityUGUI.scaleThreshold)
{
this.rectTransform.localScale = Vector3.Lerp(this.rectTransform.localScale, Vector3.one * EntityUGUI.minScaleSize, Time.deltaTime * EntityUGUI.scaleSpeed);
if (Vector3.Distance(this.rectTransform.localScale, Vector3.one * EntityUGUI.minScaleSize) <= 0.01f)
this.rectTransform.localScale = Vector3.one * EntityUGUI.minScaleSize;
}
}
///
/// 计算两个向量之间的夹角(-180 ,180)
///
///
///
///
public float VectorAngle(Vector2 last, Vector2 curr)
{
Vector3 cross = Vector3.Cross(last, curr);
float angle = Vector2.Angle(last, curr);
return cross.z > 0 ? -angle : angle;
}
#endregion
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
///
/// 实体
///
internal class Entity2D : MonoBehaviour, IDragHandler, IPointerDownHandler, IPointerUpHandler
{
private Rigidbody2D rigidBody2D;
#region 触摸点
///
/// 触摸事件参数集合
///
private List pointerEventDatas = new List(2);
///
/// Touch触摸点1的位置
///
private Vector2 touch00Postion;
///
/// Touch触摸点2的位置
///
private Vector2 touch01Postion;
#endregion
#region 移动
///
/// 偏移
///
private Vector3 pressOffSet;
///
/// 手势移动阈值
///
private static float gestureMoveThreshold = 0.1f;
#endregion
#region 旋转
///
/// 手势旋转速度
///
private static float gestureRotateSpeed = 100.0f;
///
/// 手势旋转阈值
///
private static float gestureRotateThreshold = 0.3f;
#endregion
#region 缩放
///
/// 缩放速度
///
private static float scaleSpeed = 1.0f;
///
/// 缩放阈值
///
private static float scaleThreshold = 1f;
///
/// 最大缩放倍数
///
private static float maxScaleSize = 5.0f;
///
/// 最小缩放倍数
///
private static float minScaleSize = 1.0f;
#endregion
#region Unity
private void Start()
{
this.rigidBody2D = this.GetComponent();
if (!this.rigidBody2D)
this.rigidBody2D = this.gameObject.AddComponent();
}
#endregion
#region interface
///
/// 当按下时
///
///
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
//鼠标左键:-1;鼠标右键:-2;鼠标中键:-3
this.pointerEventDatas.Add(eventData);
//按下的位置
Vector3 prePostion = Camera.main.ScreenToWorldPoint(eventData.pressPosition);
//偏移
if (this.pressOffSet == Vector3.zero)
this.pressOffSet = (Vector3)this.rigidBody2D.position - prePostion;
}
///
/// 当拖拽时
///
///
void IDragHandler.OnDrag(PointerEventData eventData)
{
if (this.pointerEventDatas.Count <= 0)
return;
if (this.pointerEventDatas.Count == 1)
{
Vector3 worldPostion = Camera.main.ScreenToWorldPoint(eventData.position);
Vector3 tarPos = worldPostion + this.pressOffSet;
//限制位置
if (Vector3.Distance(this.rigidBody2D.position, tarPos) <= Entity2D.gestureMoveThreshold)
return;
this.rigidBody2D.position = tarPos;
}
else if (this.pointerEventDatas.Count == 2)
{
#region 初始化位置
if (this.touch00Postion == Vector2.zero && this.touch01Postion == Vector2.zero)
{
this.touch00Postion = this.pointerEventDatas[0].position;
this.touch01Postion = this.pointerEventDatas[1].position;
return;
}
#endregion
this.Rotating();
this.Zoom();
#region 更新位置
this.touch00Postion = this.pointerEventDatas[0].position;
this.touch01Postion = this.pointerEventDatas[1].position;
#endregion
}
}
///
/// 当抬起时
///
///
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
for (int i = 0; i < this.pointerEventDatas.Count; i++)
if (this.pointerEventDatas[i].pointerId == eventData.pointerId)
{
this.pointerEventDatas.RemoveAt(i);
break;
}
//偏移归零
if (this.pointerEventDatas.Count <= 0)
this.pressOffSet = Vector3.zero;
//更新偏移
else if (this.pointerEventDatas.Count == 1)
this.pressOffSet = (Vector3)this.rigidBody2D.position - Camera.main.ScreenToWorldPoint(this.pointerEventDatas[0].position);
}
#endregion
#region PrivateFunction
///
/// 旋转
///
private void Rotating()
{
//上一帧方向
Vector2 lastDir = (this.touch00Postion - this.touch01Postion).normalized;
//当前帧方向
Vector2 currentDir = (this.pointerEventDatas[0].position - this.pointerEventDatas[1].position).normalized;
//角度差
float angle = this.VectorAngle(lastDir, currentDir);
if (angle < -Entity2D.gestureRotateThreshold)
this.transform.Rotate(Vector3.forward, Time.deltaTime * Entity2D.gestureRotateSpeed);
else if (angle > Entity2D.gestureRotateThreshold)
this.transform.Rotate(Vector3.forward, -Time.deltaTime * Entity2D.gestureRotateSpeed);
}
///
/// 缩放
///
private void Zoom()
{
//上一帧距离
float lastDistance = Vector2.Distance(this.touch00Postion, this.touch01Postion);
//当前帧距离
float currentDistance = Vector2.Distance(this.pointerEventDatas[0].position, this.pointerEventDatas[1].position);
//差值
float difference = lastDistance - currentDistance;
if (difference < -Entity2D.scaleThreshold)
{
this.transform.localScale = Vector3.Lerp(this.transform.localScale, Vector3.one * Entity2D.maxScaleSize, Time.deltaTime * Entity2D.scaleSpeed);
if (Vector3.Distance(this.transform.localScale, Vector3.one * Entity2D.maxScaleSize) <= 0.01f)
this.transform.localScale = Vector3.one * Entity2D.maxScaleSize;
}
else if (difference > Entity2D.scaleThreshold)
{
this.transform.localScale = Vector3.Lerp(this.transform.localScale, Vector3.one * Entity2D.minScaleSize, Time.deltaTime * Entity2D.scaleSpeed);
if (Vector3.Distance(this.transform.localScale, Vector3.one * Entity2D.minScaleSize) <= 0.01f)
this.transform.localScale = Vector3.one * Entity2D.minScaleSize;
}
}
///
/// 计算两个向量之间的夹角(-180 ,180)
///
///
///
///
public float VectorAngle(Vector2 last, Vector2 curr)
{
Vector3 cross = Vector3.Cross(last, curr);
float angle = Vector2.Angle(last, curr);
return cross.z > 0 ? -angle : angle;
}
#endregion
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
///
/// 3D实体
///
public class Entity3D : MonoBehaviour, IDragHandler, IPointerDownHandler, IPointerUpHandler
{
#region 触摸点
///
/// 触摸事件参数集合
///
private List pointerEventDatas = new List(2);
///
/// Touch触摸点1的位置
///
private Vector2 touch00Postion;
///
/// Touch触摸点2的位置
///
private Vector2 touch01Postion;
#endregion
#region 移动
///
/// 偏移
///
private Vector3 pressOffSet;
///
/// 手势移动阈值
///
private static float gestureMoveThreshold = 0.1f;
#endregion
#region 旋转
///
/// 手势旋转速度
///
private static float gestureRotateSpeed = 100.0f;
///
/// 手势旋转阈值
///
private static float gestureRotateThreshold = 0.3f;
#endregion
#region 缩放
///
/// 缩放速度
///
private static float scaleSpeed = 1.0f;
///
/// 缩放阈值
///
private static float scaleThreshold = 1f;
///
/// 最大缩放倍数
///
private static float maxScaleSize = 5.0f;
///
/// 最小缩放倍数
///
private static float minScaleSize = 1.0f;
#endregion
#region interface
///
/// 当按下时
///
///
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
//鼠标左键:-1;鼠标右键:-2;鼠标中键:-3
this.pointerEventDatas.Add(eventData);
//按下的位置
Vector3 prePostion = Camera.main.ScreenToWorldPoint(this.PointerEventDataPostionToVector3(eventData.pressPosition));
//偏移
if (this.pressOffSet == Vector3.zero)
this.pressOffSet = (Vector3)this.transform.position - prePostion;
}
///
/// 当拖拽时
///
///
void IDragHandler.OnDrag(PointerEventData eventData)
{
if (this.pointerEventDatas.Count <= 0)
return;
if (this.pointerEventDatas.Count == 1)
{
Vector3 worldPostion = Camera.main.ScreenToWorldPoint(this.PointerEventDataPostionToVector3(eventData.position));
Vector3 tarPos = worldPostion + this.pressOffSet;
//限制位置
if (Vector3.Distance(this.transform.position, tarPos) <= Entity3D.gestureMoveThreshold)
return;
this.transform.position = tarPos;
}
else if (this.pointerEventDatas.Count == 2)
{
#region 初始化位置
if (this.touch00Postion == Vector2.zero && this.touch01Postion == Vector2.zero)
{
this.touch00Postion = this.pointerEventDatas[0].position;
this.touch01Postion = this.pointerEventDatas[1].position;
return;
}
#endregion
this.Rotating();
this.Zoom();
#region 更新位置
this.touch00Postion = this.pointerEventDatas[0].position;
this.touch01Postion = this.pointerEventDatas[1].position;
#endregion
}
}
///
/// 当抬起时
///
///
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
for (int i = 0; i < this.pointerEventDatas.Count; i++)
if (this.pointerEventDatas[i].pointerId == eventData.pointerId)
{
this.pointerEventDatas.RemoveAt(i);
break;
}
//偏移归零
if (this.pointerEventDatas.Count <= 0)
this.pressOffSet = Vector3.zero;
//更新偏移
else if (this.pointerEventDatas.Count == 1)
this.pressOffSet = this.transform.position - Camera.main.ScreenToWorldPoint(
this.PointerEventDataPostionToVector3(this.pointerEventDatas[0].position));
}
#endregion
#region PrivateFunction
///
/// 旋转
///
private void Rotating()
{
//上一帧方向
Vector2 lastDir = (this.touch00Postion - this.touch01Postion).normalized;
//当前帧方向
Vector2 currentDir = (this.pointerEventDatas[0].position - this.pointerEventDatas[1].position).normalized;
//角度差
float angle = this.VectorAngle(lastDir, currentDir);
if (angle < -Entity3D.gestureRotateThreshold)
this.transform.Rotate(Vector3.forward, Time.deltaTime * Entity3D.gestureRotateSpeed);
else if (angle > Entity3D.gestureRotateThreshold)
this.transform.Rotate(Vector3.forward, -Time.deltaTime * Entity3D.gestureRotateSpeed);
}
///
/// 缩放
///
private void Zoom()
{
//上一帧距离
float lastDistance = Vector2.Distance(this.touch00Postion, this.touch01Postion);
//当前帧距离
float currentDistance = Vector2.Distance(this.pointerEventDatas[0].position, this.pointerEventDatas[1].position);
//差值
float difference = lastDistance - currentDistance;
if (difference < -Entity3D.scaleThreshold)
{
this.transform.localScale = Vector3.Lerp(this.transform.localScale, Vector3.one * Entity3D.maxScaleSize, Time.deltaTime * Entity3D.scaleSpeed);
if (Vector3.Distance(this.transform.localScale, Vector3.one * Entity3D.maxScaleSize) <= 0.01f)
this.transform.localScale = Vector3.one * Entity3D.maxScaleSize;
}
else if (difference > Entity3D.scaleThreshold)
{
this.transform.localScale = Vector3.Lerp(this.transform.localScale, Vector3.one * Entity3D.minScaleSize, Time.deltaTime * Entity3D.scaleSpeed);
if (Vector3.Distance(this.transform.localScale, Vector3.one * Entity3D.minScaleSize) <= 0.01f)
this.transform.localScale = Vector3.one * Entity3D.minScaleSize;
}
}
///
/// 计算两个向量之间的夹角(-180 ,180)
///
/// 上一帧
/// 当前帧
///
internal float VectorAngle(Vector2 last, Vector2 curr)
{
Vector3 cross = Vector3.Cross(last, curr);
float angle = Vector2.Angle(last, curr);
return cross.z > 0 ? -angle : angle;
}
///
/// 将PointerEventData位置转换为Vector3
///
/// PointerEventData位置
///
internal Vector3 PointerEventDataPostionToVector3(Vector2 postion)
{
//取距离相机距离
float z = Mathf.Abs(Camera.main.transform.position.z - this.transform.position.z);
return new Vector3(postion.x, postion.y, z);
}
#endregion
}
注意:2D脚本中本人是通过Rigidbody2D来移动位置,是因为如果直接通过Transform来移动位置会导致物理检测可能会有问题,官方手册有说明:Rigidbody2D,另外2D刚体默认有重力,可以设置为Kinematic或自行处理。
本人水平有限,欢迎指正!!!