Forked from simonbroggi/TouchScriptInputModule.cs
Last active
August 29, 2015 14:17
-
-
Save gregharding/12081413d0f398a41e94 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
TouchScript Input Module | |
Input module that supports using Unity UI (Unity 4.6+, Unity 5.0+) with multi-touch tuio input from TouchScript. | |
Usage: | |
Add TouchScriptInputModule to the EventSystem. | |
Use #define CANCEL_BRIEF_TOUCHES to cancel touches that last less than validTouchTime. | |
Requirements: | |
TouchScript https://github.com/TouchScript (by Valentin Simonov https://github.com/valyard) | |
Based on: | |
Unity UI TouchInputModule.cs (https://bitbucket.org/Unity-Technologies/ui) | |
https://gist.github.com/simonbroggi/5539328f0d14a427646b (Simon Broggi, https://github.com/simonbroggi) | |
Copyright 2015 Flightless. http://www.flightless.co.nz | |
The MIT License (MIT) | |
Copyright (c) 2015 Flightless Ltd. | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
THE SOFTWARE. | |
*/ | |
#define CANCEL_BRIEF_TOUCHES | |
using UnityEngine; | |
using System.Collections.Generic; | |
using UnityEngine.EventSystems; | |
using System.Text; | |
using TouchScript; | |
namespace Flightless { | |
public class TouchScriptInputModule : PointerInputModule { | |
protected TouchScriptInputModule() {} | |
private Vector2 m_LastMousePosition; | |
private Vector2 m_MousePosition; | |
[SerializeField] | |
private bool m_AllowFakeTouches; | |
public bool allowFakeTouches | |
{ | |
get { return m_AllowFakeTouches; } | |
set { m_AllowFakeTouches = value; } | |
} | |
#if CANCEL_BRIEF_TOUCHES | |
public bool cancelBriefTouches { | |
get { return m_cancelBriefTouches; } | |
set { m_cancelBriefTouches = value; } | |
} | |
[SerializeField] | |
private bool m_cancelBriefTouches = false; | |
public float validTouchTime { | |
get { return m_validTouchTime; } | |
set { m_validTouchTime = value; } | |
} | |
[SerializeField] | |
[Range(0.0f, 1.0f)] | |
private float m_validTouchTime = 0.15f; | |
#endif | |
public bool useTouchOnly { | |
get { return m_useTouchOnly; } | |
set { m_useTouchOnly = value; } | |
} | |
[SerializeField] | |
private bool m_useTouchOnly = true; | |
public const int initialTouchCapacity = 10; | |
override protected void Awake() { | |
base.Awake(); | |
touches = new Dictionary<int, ITouch>(initialTouchCapacity); | |
} | |
override protected void OnEnable() { | |
base.OnEnable(); | |
if (TouchManager.Instance != null) { | |
TouchManager.Instance.TouchesBegan += OnTouchesBeganHandler; | |
TouchManager.Instance.TouchesEnded += OnTouchesEndedHandler; | |
TouchManager.Instance.TouchesMoved += OnTouchesMovedHandler; | |
TouchManager.Instance.TouchesCancelled += OnTouchesCancelledHandler; | |
} | |
} | |
override protected void OnDisable() { | |
if (TouchManager.Instance != null) { | |
TouchManager.Instance.TouchesBegan -= OnTouchesBeganHandler; | |
TouchManager.Instance.TouchesEnded -= OnTouchesEndedHandler; | |
TouchManager.Instance.TouchesMoved -= OnTouchesMovedHandler; | |
TouchManager.Instance.TouchesCancelled -= OnTouchesCancelledHandler; | |
} | |
touches.Clear(); | |
touchesBeganIDs.Clear(); | |
touchesEndedIDs.Clear(); | |
touchesCancelledIDs.Clear(); | |
#if CANCEL_BRIEF_TOUCHES | |
touchesBeganTime.Clear(); | |
#endif | |
base.OnDisable(); | |
} | |
public override void UpdateModule() | |
{ | |
m_LastMousePosition = m_MousePosition; | |
m_MousePosition = Input.mousePosition; | |
} | |
public override bool IsModuleSupported() | |
{ | |
return true; | |
} | |
public override bool ShouldActivateModule() | |
{ | |
if (!base.ShouldActivateModule()) | |
return false; | |
if (UseFakeInput()) | |
{ | |
bool wantsEnable = Input.GetMouseButtonDown(0); | |
wantsEnable |= (m_MousePosition - m_LastMousePosition).sqrMagnitude > 0.0f; | |
return wantsEnable; | |
} | |
return (TouchManager.Instance != null && TouchManager.Instance.NumberOfTouches > 0); | |
} | |
private bool UseFakeInput() | |
{ | |
return m_AllowFakeTouches; | |
} | |
public override void Process() | |
{ | |
if (UseFakeInput()) | |
FakeTouches(); | |
else | |
ProcessTouchEvents(); | |
} | |
/// <summary> | |
/// For debugging touch-based devices using the mouse. | |
/// </summary> | |
private void FakeTouches() | |
{ | |
var pointerData = GetMousePointerEventData(); | |
var leftPressData = pointerData.GetButtonState(PointerEventData.InputButton.Left).eventData; | |
// fake touches... on press clear delta | |
if (leftPressData.PressedThisFrame()) | |
leftPressData.buttonData.delta = Vector2.zero; | |
ProcessTouchPress(leftPressData.buttonData, leftPressData.PressedThisFrame(), leftPressData.ReleasedThisFrame(), false); | |
// only process move if we are pressed... | |
if (Input.GetMouseButton(0)) | |
{ | |
ProcessMove(leftPressData.buttonData); | |
ProcessDrag(leftPressData.buttonData); | |
} | |
} | |
/// <summary> | |
/// Process all touch events. | |
/// </summary> | |
private void ProcessTouchEvents() | |
{ | |
//Debug.Log(string.Format("Touches: {0}", TouchManager.Instance.NumberOfTouches)); | |
// process all touch events from the touches dictionary | |
foreach (KeyValuePair<int, ITouch> touch in touches) { | |
bool released; | |
bool pressed; | |
bool cancelled; | |
var pointer = GetTouchScriptPointerEventData(touch.Value, out pressed, out released, out cancelled); | |
ProcessTouchPress(pointer, pressed, released, cancelled); | |
if (released || cancelled) | |
{ | |
RemovePointerData(pointer); | |
} else { | |
ProcessMove(pointer); | |
ProcessDrag(pointer); | |
} | |
} | |
// cleanup, remove ended touches and clear lists of began/ended/cancelled touch ids | |
for (int i=0; i < touchesEndedIDs.Count; i++) { | |
touches.Remove(touchesEndedIDs[i]); | |
#if CANCEL_BRIEF_TOUCHES | |
touchesBeganTime.Remove(touchesEndedIDs[i]); | |
#endif | |
} | |
for (int i=0; i < touchesCancelledIDs.Count; i++) { | |
touches.Remove(touchesCancelledIDs[i]); | |
#if CANCEL_BRIEF_TOUCHES | |
touchesBeganTime.Remove(touchesCancelledIDs[i]); | |
#endif | |
} | |
touchesBeganIDs.Clear(); | |
touchesEndedIDs.Clear(); | |
touchesCancelledIDs.Clear(); | |
} | |
protected PointerEventData GetTouchScriptPointerEventData(ITouch input, out bool pressed, out bool released, out bool cancelled) | |
{ | |
PointerEventData pointerData; | |
var created = GetPointerData(input.Id, out pointerData, true); | |
pointerData.Reset(); | |
pressed = created || touchesBeganIDs.Contains(input.Id); | |
released = touchesEndedIDs.Contains(input.Id); | |
cancelled = touchesCancelledIDs.Contains(input.Id); | |
if (created) | |
pointerData.position = input.Position; | |
if (pressed) | |
pointerData.delta = Vector2.zero; | |
else | |
pointerData.delta = input.Position - pointerData.position; | |
pointerData.position = input.Position; | |
pointerData.button = PointerEventData.InputButton.Left; | |
eventSystem.RaycastAll(pointerData, m_RaycastResultCache); | |
var raycast = FindFirstRaycast(m_RaycastResultCache); | |
pointerData.pointerCurrentRaycast = raycast; | |
m_RaycastResultCache.Clear(); | |
return pointerData; | |
} | |
private void ProcessTouchPress(PointerEventData pointerEvent, bool pressed, bool released, bool cancelled) | |
{ | |
var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject; | |
// PointerDown notification | |
if (pressed) | |
{ | |
pointerEvent.eligibleForClick = true; | |
pointerEvent.delta = Vector2.zero; | |
pointerEvent.dragging = false; | |
pointerEvent.useDragThreshold = true; | |
pointerEvent.pressPosition = pointerEvent.position; | |
pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast; | |
DeselectIfSelectionChanged(currentOverGo, pointerEvent); | |
if (pointerEvent.pointerEnter != currentOverGo) | |
{ | |
// send a pointer enter to the touched element if it isn't the one to select... | |
HandlePointerExitAndEnter(pointerEvent, currentOverGo); | |
pointerEvent.pointerEnter = currentOverGo; | |
} | |
// search for the control that will receive the press | |
// if we can't find a press handler set the press | |
// handler to be what would receive a click. | |
var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler); | |
// didnt find a press handler... search for a click handler | |
if (newPressed == null) | |
newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo); | |
// Debug.Log("Pressed: " + newPressed); | |
float time = Time.unscaledTime; | |
if (newPressed == pointerEvent.lastPress) | |
{ | |
var diffTime = time - pointerEvent.clickTime; | |
if (diffTime < 0.3f) | |
++pointerEvent.clickCount; | |
else | |
pointerEvent.clickCount = 1; | |
pointerEvent.clickTime = time; | |
} | |
else | |
{ | |
pointerEvent.clickCount = 1; | |
} | |
pointerEvent.pointerPress = newPressed; | |
pointerEvent.rawPointerPress = currentOverGo; | |
pointerEvent.clickTime = time; | |
// Save the drag handler as well | |
pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo); | |
if (pointerEvent.pointerDrag != null) | |
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag); | |
} | |
// PointerUp notification | |
if (released || cancelled) | |
{ | |
// cancelled touches can't click but they do everything else to clean up nicely | |
if (cancelled) pointerEvent.eligibleForClick = false; | |
// Debug.Log("Executing pressup on: " + pointer.pointerPress); | |
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler); | |
// Debug.Log("KeyCode: " + pointer.eventData.keyCode); | |
// see if we mouse up on the same element that we clicked on... | |
var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo); | |
// PointerClick and Drop events | |
if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick) | |
{ | |
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler); | |
} | |
else if (pointerEvent.pointerDrag != null) | |
{ | |
ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler); | |
} | |
pointerEvent.eligibleForClick = false; | |
pointerEvent.pointerPress = null; | |
pointerEvent.rawPointerPress = null; | |
if (pointerEvent.pointerDrag != null && pointerEvent.dragging) | |
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler); | |
pointerEvent.dragging = false; | |
pointerEvent.pointerDrag = null; | |
if (pointerEvent.pointerDrag != null) | |
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler); | |
pointerEvent.pointerDrag = null; | |
// send exit events as we need to simulate this on touch up on touch device | |
ExecuteEvents.ExecuteHierarchy(pointerEvent.pointerEnter, pointerEvent, ExecuteEvents.pointerExitHandler); | |
pointerEvent.pointerEnter = null; | |
} | |
} | |
public override void DeactivateModule() | |
{ | |
base.DeactivateModule(); | |
ClearSelection(); | |
} | |
public override string ToString() | |
{ | |
var sb = new StringBuilder(); | |
sb.AppendLine(UseFakeInput() ? "Input: Faked" : "Input: Touch"); | |
if (UseFakeInput()) | |
{ | |
var pointerData = GetLastPointerEventData(kMouseLeftId); | |
if (pointerData != null) | |
sb.AppendLine(pointerData.ToString()); | |
} | |
else | |
{ | |
foreach (var pointerEventData in m_PointerData) | |
sb.AppendLine(pointerEventData.ToString()); | |
} | |
return sb.ToString(); | |
} | |
// | |
// tuio input | |
// | |
public Dictionary<int, ITouch> touches { get; protected set; } | |
private List<int> touchesBeganIDs = new List<int>(initialTouchCapacity); | |
private List<int> touchesEndedIDs = new List<int>(initialTouchCapacity); | |
private List<int> touchesCancelledIDs = new List<int>(initialTouchCapacity); | |
#if CANCEL_BRIEF_TOUCHES | |
private Dictionary<int, float> touchesBeganTime = new Dictionary<int, float>(initialTouchCapacity); | |
#endif | |
private bool IsValidTouch(ITouch touch) { | |
return (!m_useTouchOnly || (m_useTouchOnly && (touch.Tags.HasTag(Tags.INPUT_TOUCH) || touch.Tags.HasTag(Tags.INPUT_MOUSE)))); | |
} | |
private void OnTouchesBeganHandler(object sender, TouchEventArgs e) { | |
for (int i=0; i<e.Touches.Count; i++) { | |
ITouch touch = e.Touches[i]; | |
if (!IsValidTouch(touch)) continue; | |
touches.Add(touch.Id, touch); | |
touchesBeganIDs.Add(touch.Id); | |
#if CANCEL_BRIEF_TOUCHES | |
touchesBeganTime.Add(touch.Id, Time.realtimeSinceStartup); | |
#endif | |
} | |
} | |
private void UpdateTouch(ITouch touch) { | |
touches[touch.Id] = touch; | |
} | |
private void OnTouchesMovedHandler(object sender, TouchEventArgs e) { | |
for (int i=0; i<e.Touches.Count; i++) { | |
ITouch touch = e.Touches[i]; | |
if (!IsValidTouch(touch)) continue; | |
ITouch foundTouch; | |
if (!touches.TryGetValue(touch.Id, out foundTouch)) return; | |
UpdateTouch(touch); | |
} | |
} | |
private void OnTouchesEndedHandler(object sender, TouchEventArgs e) { | |
for (int i=0; i<e.Touches.Count; i++) { | |
ITouch touch = e.Touches[i]; | |
if (!IsValidTouch(touch)) continue; | |
ITouch foundTouch; | |
if (!touches.TryGetValue(touch.Id, out foundTouch)) return; | |
#if CANCEL_BRIEF_TOUCHES | |
// end or cancel touch | |
float touchBeginTime; | |
if (m_cancelBriefTouches && touchesBeganTime.TryGetValue(touch.Id, out touchBeginTime) && (Time.realtimeSinceStartup - touchBeginTime < m_validTouchTime)) { | |
touchesCancelledIDs.Add(touch.Id); | |
} else { | |
touchesEndedIDs.Add(touch.Id); | |
} | |
#else | |
// end touch | |
touchesEndedIDs.Add(touch.Id); | |
#endif | |
//touches.Remove(touch.Id); // removed after process handles the touch | |
} | |
} | |
private void OnTouchesCancelledHandler(object sender, TouchEventArgs e) { | |
// cancel touches same as end touches | |
//OnTouchesEndedHandler(sender, e); | |
// specifically cancel touches | |
for (int i=0; i<e.Touches.Count; i++) { | |
ITouch touch = e.Touches[i]; | |
if (!IsValidTouch(touch)) continue; | |
ITouch foundTouch; | |
if (!touches.TryGetValue(touch.Id, out foundTouch)) return; | |
touchesCancelledIDs.Add(touch.Id); | |
//touches.Remove(touch.Id); // removed after process handles the touch | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment