在现代Unity3D中,我们使用IPointerDownHandler系列调用。
关于IPointerDownHandler
系列电话,
public class FingerMove:MonoBehaviour, IPointerDownHandler...
{
public void OnPointerDown (PointerEventData data)
{
当然他们很棒
但是你如何以严肃的方式处理多次接触?
你可以“亲自动手”跟踪自己的动作,但看起来令人难以置信的Unity会让你为那些绝对基本的东西做到这一点。 (我的意思是 - 它是一个游戏引擎。当然,我也可以编写我自己的渲染和物理!)
这是一个基本上“牛仔编程”的例子,只是手工完成而没有软件工程。什么是真正的解决方案?
//
// example of programming a pinch (as well as swipes) using modern Unity
//
// here we are forced to track "by hand" in your own code
// how many fingers are down and which
// fingers belong to you etc etc:
//
// pedagogic example code:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.EventSystems;
public class FingerMove:MonoBehaviour,
IPointerDownHandler, IDragHandler, IPointerUpHandler
{
// these three for the ordinary one-finger-only drag
private Vector2 prevPoint;
private Vector2 newPoint;
private Vector2 screenTravel;
// and this one is the ordinary one-finger-only drag
private int currentMainFinger = -1;
// and this for the (strictly second finger only) drag...
private int currentSecondFinger = -1;
private Vector2 posA;
private Vector2 posB;
private float previousDistance = -1f;
private float distance;
private float pinchDelta = 0f;
public void OnPointerDown (PointerEventData data)
{
if (currentMainFinger == -1)
{
// this is the NEW currentMainFinger
currentMainFinger = data.pointerId;
prevPoint = data.position;
// and for the drag (if it becomes used)...
posA = data.position;
return;
}
if (currentSecondFinger == -1)
{
// this is the NEW currentSecondFinger
currentSecondFinger = data.pointerId;
posB = data.position;
figureDelta();
previousDistance = distance;
return;
}
Debug.Log("third+ finger! (ignore)");
}
public void OnDrag (PointerEventData data)
{
// handle single-finger moves (swipes, drawing etc) only:
if ( currentMainFinger == data.pointerId )
{
newPoint = data.position;
screenTravel = newPoint - prevPoint;
prevPoint = newPoint;
if (currentSecondFinger == -1)
{
Debug.Log("NO 2f");
_processSwipe(); // handle it your way
}
else
{
}
// and for two-finger if it becomes used next frame
// or is already being used...
posA = data.position;
}
if (currentSecondFinger == -1) return;
// handle two-finger (eg, pinch, rotate etc)...
if ( currentMainFinger == data.pointerId ) posA = data.position;
if ( currentSecondFinger == data.pointerId ) posB = data.position;
figureDelta();
pinchDelta = distance - previousDistance;
previousDistance = distance;
_processPinch(); // handle it your way
}
private void figureDelta()
{
// when/if two touches, keep track of the distance between them
distance = Vector2.Distance(posA, posB);
}
public void OnPointerUp (PointerEventData data)
{
if ( currentMainFinger == data.pointerId )
{
currentMainFinger = -1;
}
if ( currentSecondFinger == data.pointerId )
{
currentSecondFinger = -1;
}
}
private float sensitivity = 0.3f;
// in this example, the swipes/pinch affects these three calls:
public Changer orbitLR;
public Changer orbitUD;
public Changer distanceZ;
// initial values of those...
private float LR = -20f;
private float UD = 20f;
private float distanceCam = 5f;
private void _processSwipe()
{
// in this example, just left-right or up-down swipes
LR = LR + sensitivity * screenTravel.x;
UD = UD - sensitivity * screenTravel.y;
LR = Mathf.Clamp(LR, -150f, 30f);
UD = Mathf.Clamp(UD, 5f, 50f);
orbitLR.RotationY = LR;
orbitUD.RotationX = UD;
}
private void _processPinch()
{
// in this example, pinch to zoom
distanceCam = distanceCam - pinchDelta * 0.0125f;
distanceCam = Mathf.Clamp(distanceCam, 3f, 8f);
distanceZ.DistanceZ = distanceCam;
}
}
(注意,请不要回答关于遗留下来的“触摸”系统的问题。这是关于正常的现代Unity开发。)
答案 0 :(得分:2)
实现这一点并不复杂。
使用List
并在每次有pointerId
事件时存储OnPointerDown
,然后增加touchCount
变量。如果pointerId
中已存在{On},请不要将List
存储在OnPointerDown上。
调用OnPointerUp
或发布时,请检查pointerId
是否存在。如果是,则递减touchCount
变量。如果<{1}}中存在不,则不要减少任何内容。
1 。非常简单的实施:
List
2 。第一个示例非常简单,但可以使用我们自己的界面进行改进。
此方法使用两个脚本:
public class FingerMove : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public int touchCount;
public List<int> touchID = new List<int>(6); //6 touches limit
public void OnPointerDown(PointerEventData data)
{
Debug.Log("Pressed");
//Check If PointerId exist, if it doesn't add to list
if (touchID.Contains(data.pointerId))
{
return; //Exit if PointerId exist
}
//PointerId does not exist, add it to the list then increment touchCount
touchID.Add(data.pointerId);
touchCount++;
}
public void OnPointerUp(PointerEventData data)
{
Debug.Log("Released");
//Check If PointerId exist, if it exist remove it from list then decrement touchCount
if (touchID.Contains(data.pointerId))
{
touchID.Remove(data.pointerId);
touchCount--;
return;
}
}
void Update()
{
Debug.Log("Touch Count: " + touchCount);
}
}
interface:
IPointerCounterHandler.cs
public interface IPointerCounterHandler : IEventSystemHandler
{
void OnPointerCounterChanged(int touchCount);
void OnPointerCounterChanged(PointerCounterEventData touchCountData);
}
脚本。
PointerCounterEventData.cs
<强>用法强>:
在您的脚本中实施public class PointerCounterEventData : BaseEventData
{
//The callback with int parameter
public static readonly ExecuteEvents.EventFunction<IPointerCounterHandler> counterChangedV1Delegate
= delegate (IPointerCounterHandler handler, BaseEventData data)
{
//var casted = ExecuteEvents.ValidateEventData<PointerCounterEventData>(data);
handler.OnPointerCounterChanged(touchCount);
};
//The callback with PointerCounterEventData parameter
public static readonly ExecuteEvents.EventFunction<IPointerCounterHandler> counterChangedV2Delegate
= delegate (IPointerCounterHandler handler, BaseEventData data)
{
var casted = ExecuteEvents.ValidateEventData<PointerCounterEventData>(data);
handler.OnPointerCounterChanged(casted);
};
public static int touchCount = 0;
public PointerCounterInfo touchCountData = new PointerCounterInfo();
public static List<int> touchID = new List<int>(6); //6 touches limit
//Constructor with the int parameter
public PointerCounterEventData(
EventSystem eventSystem,
int tempTouchId,
PointerState pointerStat
)
: base(eventSystem)
{
//Process the Input event
processTouches(pointerStat, tempTouchId, null, CallBackType.TouchCountOnly);
}
//Constructor with the PointerEventData parameter
public PointerCounterEventData(
EventSystem eventSystem,
PointerEventData eventData,
PointerState pointerStat,
GameObject target
)
: base(eventSystem)
{
//Process the Input event
processTouches(pointerStat, eventData.pointerId, eventData, CallBackType.CounterData);
//Create new PointerCounterInfo for the OnPointerCounterChanged(PointerCounterEventData eventData) function
PointerCounterInfo pcInfo = createPointerInfo(eventData,
target, pointerStat);
//Update touchCountData
touchCountData = pcInfo;
}
void processTouches(PointerState pointerStat, int tempTouchId, PointerEventData touchCountData, CallBackType cbType)
{
if (pointerStat == PointerState.DOWN)
{
//Check If PointerId exist, if it doesn't add to list
if (touchID.Contains(tempTouchId))
{
//eventData.eventData
return; //Exit if PointerId exist
}
//PointerId does not exist, add it to the list then increment touchCount
touchID.Add(tempTouchId);
touchCount++;
}
if (pointerStat == PointerState.UP)
{
//Check If PointerId exist, if it exist remove it from list then decrement touchCount
if (touchID.Contains(tempTouchId))
{
touchID.Remove(tempTouchId);
touchCount--;
return;
}
}
}
public static void notifyPointerDown(EventSystem eventSystem, PointerEventData eventData,
GameObject target)
{
PointerState pointerStat = PointerState.DOWN;
notifyfuncs(eventSystem, eventData, target, pointerStat);
}
public static void notifyPointerUp(EventSystem eventSystem, PointerEventData eventData,
GameObject target)
{
PointerState pointerStat = PointerState.UP;
notifyfuncs(eventSystem, eventData, target, pointerStat);
}
private static void notifyfuncs(EventSystem eventSystem, PointerEventData eventData,
GameObject target, PointerState pointerStat)
{
//////////////////////Call the int parameter//////////////////////
PointerCounterEventData eventParam1 = new PointerCounterEventData(
eventSystem,
eventData.pointerId,
pointerStat);
ExecuteEvents.Execute<IPointerCounterHandler>(
target,
eventParam1,
PointerCounterEventData.counterChangedV1Delegate);
//////////////////////Call the PointerCounterEventData parameter//////////////////////
PointerCounterEventData eventParam2 = new PointerCounterEventData(
eventSystem,
eventData,
pointerStat,
target);
ExecuteEvents.Execute<IPointerCounterHandler>(
target,
eventParam2,
PointerCounterEventData.counterChangedV2Delegate);
}
//Creates PointerCounterInfo for the OnPointerCounterChanged(PointerCounterEventData eventData) function
private static PointerCounterInfo createPointerInfo(PointerEventData eventData,
GameObject target, PointerState pointerStat)
{
PointerCounterInfo pointerCounterInfo = new PointerCounterInfo();
pointerCounterInfo.pointerId = eventData.pointerId;
pointerCounterInfo.touchCount = touchCount;
pointerCounterInfo.eventData = eventData;
pointerCounterInfo.pointerState = pointerStat;
pointerCounterInfo.target = target;
return pointerCounterInfo;
}
public enum CallBackType
{
TouchCountOnly, CounterData
}
}
public enum PointerState { NONE, DOWN, UP }
public class PointerCounterInfo
{
public int pointerId = 0;
public int touchCount = 0;
public PointerEventData eventData;
public PointerState pointerState;
public GameObject target;
}
,然后覆盖
IPointerCounterHandler
和
void OnPointerCounterChanged(int touchCount);
函数。
最后,在void OnPointerCounterChanged(PointerCounterEventData touchCountData);
函数中调用PointerCounterEventData.notifyPointerDown
,并在OnPointerDown
函数中调用PointerCounterEventData.notifyPointerUp
。
测试:
OnPointerUp
答案 1 :(得分:2)
您没有预定义的方法在Unity中执行此操作。您所能做的就是以面向对象的方式再次使用自定义解决方案。最好的办法是拆分事件检测和事件处理。
要问的主要问题是,如何以OOP方式表示手指,触摸,手势等。我选择这样做:
f1, f2, f3
,则创建的手指组合为:f3, f1f3, f2f3, f1f2f3
。当使用多个手指时,这提供了极大的灵活性。你可以做手势like this。例如,如果您想要执行锚定手势,则只需要f2f3
的手势,但f1
也必须存在。在这种情况下,您可以忽略f1
。此外,您通常需要多点触控事件:
未来的长代码:
using UnityEngine;
using UnityEngine.EventSystems;
using System.Linq;
using System.Collections.Generic;
public class MultitouchHandler : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler {
public List<Finger> Fingers = new List<Finger>();
public List<FingerCombination> FingerCombinations = new List<FingerCombination>();
public FingerCombination GetFingerCombination(params int[] fingerIndices) {
var fc = FingerCombinations.Find(x => x.IDs.Count == fingerIndices.Length && fingerIndices.All(y => x.IDs.Contains(Fingers[y].ID)));
if (fc != null) return fc;
fc = new FingerCombination() {
Fingers = fingerIndices.Select(x => Fingers[x]).ToList()
};
fc.IDs = fc.Fingers.Select(x => x.ID).ToList();
fc.Data = Fingers.Select(x => x.Data).ToList();
fc.PreviousData = Fingers.Select(x => x.Data).ToList();
FingerCombinations.Add(fc);
return fc;
}
public delegate void MultitouchEventHandler(int touchCount, MultitouchHandler sender);
public event MultitouchEventHandler OnFingerAdded;
public event MultitouchEventHandler OnFingerRemoved;
public void OnDrag(PointerEventData eventData) {
var finger = Fingers.Find(x => x.ID == eventData.pointerId);
var fcs = FingerCombinations.Where(x => x.IDs.Contains(eventData.pointerId));
finger.PreviousData = finger.Data;
finger.Data = eventData;
foreach (var fc in fcs) {
fc.PreviousData = fc.Data;
fc.Data = fc.Fingers.Select(x => x.Data).ToList();
fc.PreviousGesture = fc.Gesture;
fc.Gesture = new Gesture() {
Center = fc.Center,
Size = fc.Size,
Angle = fc.Angle,
SizeDelta = 1
};
if (fc.PreviousGesture != null) {
fc.Gesture.CenterDelta = fc.Center - fc.PreviousGesture.Center;
fc.Gesture.SizeDelta = fc.Size / fc.PreviousGesture.Size;
fc.Gesture.AngleDelta = fc.Angle - fc.PreviousGesture.Angle;
}
fc.Changed();
}
}
public void OnPointerDown(PointerEventData eventData) {
var finger = new Finger() { ID = eventData.pointerId, Data = eventData };
Fingers.Add(finger);
if (OnFingerAdded != null)
OnFingerAdded(Fingers.Count, this);
}
public void OnPointerUp(PointerEventData eventData) {
Fingers.RemoveAll(x => x.ID == eventData.pointerId);
if (OnFingerRemoved != null)
OnFingerRemoved(Fingers.Count, this);
var fcs = FingerCombinations.Where(x => x.IDs.Contains(eventData.pointerId));
foreach (var fc in fcs) {
fc.Finished();
}
FingerCombinations.RemoveAll(x => x.IDs.Contains(eventData.pointerId));
}
public class Finger {
public int ID;
public PointerEventData Data;
public PointerEventData PreviousData;
}
public class FingerCombination {
public List<int> IDs = new List<int>();
public List<Finger> Fingers;
public List<PointerEventData> PreviousData;
public List<PointerEventData> Data;
public delegate void GestureEventHandler(Gesture gesture, FingerCombination sender);
public event GestureEventHandler OnChange;
public delegate void GestureEndHandler(FingerCombination sender);
public event GestureEndHandler OnFinish;
public Gesture Gesture;
public Gesture PreviousGesture;
public Vector2 Center
{
get { return Data.Aggregate(Vector2.zero, (x, y) => x + y.position) / Data.Count; }
}
public float Size
{
get
{
if (Data.Count == 1) return 0;
var magnitudeSum = 0f;
for (int i = 1; i < Data.Count; i++) {
var dif = (Data[i].position - Data[0].position);
magnitudeSum += dif.magnitude;
}
return magnitudeSum / (Data.Count - 1);
}
}
public float Angle
{
get
{
if (Data.Count == 1) return 0;
var angleSum = 0f;
for (int i = 1; i < Data.Count; i++) {
var dif = (Data[i].position - Data[0].position);
angleSum += Mathf.Atan2(dif.y, dif.x) * Mathf.Rad2Deg;
}
return angleSum / (Data.Count - 1);
}
}
internal void Changed() {
if (OnChange != null)
OnChange.Invoke(Gesture, this);
}
internal void Finished() {
if (OnFinish != null)
OnFinish.Invoke(this);
}
}
public class Gesture {
public Vector2 Center;
public float Size;
public float Angle;
public Vector2 CenterDelta;
public float SizeDelta;
public float AngleDelta;
}
}
以下是一个示例,说明如何使用4个手指。
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
public class MultiTouchTest : MonoBehaviour {
public Vector2 rectSize = Vector2.one * 2;
public Vector2 skewedRectSize = Vector2.one;
public Vector2 rectPos = Vector2.zero;
public List<Vector3> Fingers = new List<Vector3>();
void Start() {
var h = GetComponent<MultitouchHandler>();
h.OnFingerAdded += OnGestureStart;
}
private void OnGestureStart(int touchCount, MultitouchHandler sender) {
if (touchCount != 4) return;
var fc = sender.GetFingerCombination(0, 1, 2, 3);
fc.OnChange += OnGesture;
}
private void OnGesture(MultitouchHandler.Gesture gesture, MultitouchHandler.FingerCombination sender) {
rectSize *= gesture.SizeDelta;
Fingers = sender.Fingers.Select(x => Camera.main.ScreenToWorldPoint(x.Data.position)).ToList();
var tan = Mathf.Tan(gesture.Angle * Mathf.Deg2Rad);
skewedRectSize = new Vector2(rectSize.x / tan, rectSize.y * tan);
rectPos += gesture.CenterDelta / 50;
}
public void OnDrawGizmos() {
Gizmos.color = Color.red;
Gizmos.DrawCube(rectPos, skewedRectSize);
Gizmos.color = Color.blue;
foreach (var finger in Fingers) Gizmos.DrawSphere(finger + Vector3.forward, 0.5f);
}
}
结果如下:
这只是一个简单的例子。对于SO的格式,一个好的答案太长了。
答案 2 :(得分:1)
统一输入系统还不完善。您现在必须使用低级别系统自行跟踪触摸。 See an example here。