Skip to content

Instantly share code, notes, and snippets.

@gregharding
Forked from simonbroggi/TouchScriptInputModule.cs
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gregharding/12081413d0f398a41e94 to your computer and use it in GitHub Desktop.
Save gregharding/12081413d0f398a41e94 to your computer and use it in GitHub Desktop.
/*
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