309 lines
8.3 KiB
C#
309 lines
8.3 KiB
C#
using UnityEngine;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
|
|
public partial class TouchKit : MonoBehaviour
|
|
{
|
|
[HideInInspector]
|
|
public bool simulateTouches = true;
|
|
[HideInInspector]
|
|
public bool simulateMultitouch = true;
|
|
[HideInInspector]
|
|
public bool drawTouches = false;
|
|
[HideInInspector]
|
|
public bool drawDebugBoundaryFrames = false;
|
|
|
|
/// <summary>
|
|
/// lets TouchKit know if it should scale all rects and distances based on the designTimeResolution
|
|
/// </summary>
|
|
public bool autoScaleRectsAndDistances = true;
|
|
|
|
/// <summary>
|
|
/// if false, TouchKit will not do anything in Update. You will need to call updateTouches yourself.
|
|
/// </summary>
|
|
public bool shouldAutoUpdateTouches = true;
|
|
|
|
private Vector2 _designTimeResolution = new Vector2( 320, 180 ); // 16:9 is a decent starting point for aspect ratio
|
|
/// <summary>
|
|
/// all TKRect sizes should be based on this screen size. They will be adjusted at runtime if autoUpdateRects is true
|
|
/// </summary>
|
|
public Vector2 designTimeResolution {
|
|
get {
|
|
return _designTimeResolution;
|
|
}
|
|
set {
|
|
_designTimeResolution = value;
|
|
setupRuntimeScale();
|
|
}
|
|
}
|
|
public int maxTouchesToProcess = 2;
|
|
|
|
/// <summary>
|
|
/// used at runtime to scale any TKRects as they are made for the current screen size
|
|
/// </summary>
|
|
public Vector2 runtimeScaleModifier { get; private set; }
|
|
|
|
/// <summary>
|
|
/// used at runtime to modify distances
|
|
/// </summary>
|
|
public float runtimeDistanceModifier { get; private set; }
|
|
|
|
/// <summary>
|
|
/// used at runtime to translate a pixel value into Unity units. Just multiply the pixel value by the pixelsToUnityUnitsMultiplier.
|
|
/// Note that this will only work for orthographic cameras!
|
|
/// </summary>
|
|
public Vector2 pixelsToUnityUnitsMultiplier { get; private set; }
|
|
|
|
|
|
private List<TKAbstractGestureRecognizer> _gestureRecognizers = new List<TKAbstractGestureRecognizer>( 5 );
|
|
private TKTouch[] _touchCache;
|
|
private List<TKTouch> _liveTouches = new List<TKTouch>( 2 );
|
|
private bool _shouldCheckForLostTouches = false; // used internally to ensure we dont check for lost touches too often
|
|
|
|
private const float inchesToCentimeters = 2.54f;
|
|
|
|
public float ScreenPixelsPerCm
|
|
{
|
|
get
|
|
{
|
|
float fallbackDpi = 72f;
|
|
#if UNITY_ANDROID
|
|
// Android MDPI setting fallback
|
|
// http://developer.android.com/guide/practices/screens_support.html
|
|
fallbackDpi = 160f;
|
|
#elif (UNITY_WP8 || UNITY_WP8_1 || UNITY_WSA || UNITY_WSA_8_0)
|
|
// Windows phone is harder to track down
|
|
// http://www.windowscentral.com/higher-resolution-support-windows-phone-7-dpi-262
|
|
fallbackDpi = 92f;
|
|
#elif UNITY_IOS
|
|
// iPhone 4-6 range
|
|
fallbackDpi = 326f;
|
|
#endif
|
|
|
|
return Screen.dpi == 0f ? fallbackDpi / inchesToCentimeters : Screen.dpi / inchesToCentimeters;
|
|
}
|
|
}
|
|
|
|
private static TouchKit _instance = null;
|
|
public static TouchKit instance
|
|
{
|
|
get
|
|
{
|
|
if( !_instance )
|
|
{
|
|
// check if there is a GO instance already available in the scene graph
|
|
_instance = FindObjectOfType( typeof( TouchKit ) ) as TouchKit;
|
|
|
|
// nope, create a new one
|
|
if( !_instance )
|
|
{
|
|
var obj = new GameObject( "TouchKit" );
|
|
_instance = obj.AddComponent<TouchKit>();
|
|
DontDestroyOnLoad( obj );
|
|
}
|
|
|
|
// prep the scalers. for the distance scaler we just use an average of the width and height scales
|
|
var aCamera = Camera.main ?? Camera.allCameras[0];
|
|
if( aCamera.orthographic )
|
|
{
|
|
setupPixelsToUnityUnitsMultiplierWithCamera( aCamera );
|
|
}
|
|
else
|
|
{
|
|
_instance.pixelsToUnityUnitsMultiplier = Vector2.one;
|
|
}
|
|
|
|
_instance.setupRuntimeScale();
|
|
}
|
|
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
protected void setupRuntimeScale()
|
|
{
|
|
_instance.runtimeScaleModifier = new Vector2( Screen.width / _instance.designTimeResolution.x, Screen.height / _instance.designTimeResolution.y );
|
|
_instance.runtimeDistanceModifier = ( _instance.runtimeScaleModifier.x + _instance.runtimeScaleModifier.y ) / 2f;
|
|
|
|
if( !_instance.autoScaleRectsAndDistances )
|
|
{
|
|
_instance.runtimeScaleModifier = Vector2.one;
|
|
_instance.runtimeDistanceModifier = 1f;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unity often misses the Ended phase of touches so this method will look out for that
|
|
/// </summary>
|
|
private void addTouchesUnityForgotToEndToLiveTouchesList()
|
|
{
|
|
for( int i = 0; i < _touchCache.Length; i++ )
|
|
{
|
|
if( _touchCache[i].phase != TouchPhase.Ended )
|
|
{
|
|
Debug.LogWarning( "found touch Unity forgot to end with phase: " + _touchCache[i].phase );
|
|
_touchCache[i].phase = TouchPhase.Ended;
|
|
_liveTouches.Add( _touchCache[i] );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void internalUpdateTouches()
|
|
{
|
|
// the next couple sections are disgustingly, appallingly, horrendously horrible due to all the #ifs but it
|
|
// helps when testing in the editor
|
|
#if UNITY_EDITOR
|
|
// check to see if the Unity Remote is active
|
|
if( shouldProcessMouseInput() )
|
|
{
|
|
#endif
|
|
|
|
#if UNITY_EDITOR || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_WEBPLAYER || UNITY_WEBGL
|
|
|
|
// we only need to process if we have some interesting input this frame
|
|
if( Input.GetMouseButtonUp( 0 ) || Input.GetMouseButton( 0 ) )
|
|
_liveTouches.Add( _touchCache[0].populateFromMouse() );
|
|
|
|
#endif
|
|
|
|
#if UNITY_EDITOR
|
|
}
|
|
#endif
|
|
|
|
|
|
// get all touches and examine them. only do our touch processing if we have some touches
|
|
if( Input.touchCount > 0 )
|
|
{
|
|
_shouldCheckForLostTouches = true;
|
|
|
|
var maxTouchIndexToExamine = Mathf.Min( Input.touches.Length, maxTouchesToProcess );
|
|
for( var i = 0; i < maxTouchIndexToExamine; i++ )
|
|
{
|
|
var touch = Input.touches[i];
|
|
if( touch.fingerId < maxTouchesToProcess )
|
|
_liveTouches.Add( _touchCache[touch.fingerId].populateWithTouch( touch ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we guard this so that we only check once after all the touches are lifted
|
|
if( _shouldCheckForLostTouches )
|
|
{
|
|
addTouchesUnityForgotToEndToLiveTouchesList();
|
|
_shouldCheckForLostTouches = false;
|
|
}
|
|
}
|
|
|
|
// pass on the touches to all the recognizers
|
|
if( _liveTouches.Count > 0 )
|
|
{
|
|
for( var i = 0; i < _gestureRecognizers.Count; i++ )
|
|
_gestureRecognizers[i].recognizeTouches( _liveTouches );
|
|
_liveTouches.Clear();
|
|
}
|
|
}
|
|
|
|
|
|
#region MonoBehaviour
|
|
|
|
private void Awake()
|
|
{
|
|
// prep our TKTouch cache so we avoid excessive allocations
|
|
_touchCache = new TKTouch[maxTouchesToProcess];
|
|
for( int i = 0; i < maxTouchesToProcess; i++ )
|
|
_touchCache[i] = new TKTouch( i );
|
|
}
|
|
|
|
|
|
private void Update()
|
|
{
|
|
if( shouldAutoUpdateTouches )
|
|
internalUpdateTouches();
|
|
}
|
|
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
_instance = null;
|
|
Destroy( gameObject );
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region Public API
|
|
|
|
/// <summary>
|
|
/// TouchKit will attempt to use the first camera to set this up. If that is not the camera you would like used
|
|
/// just call this method with your HUD (or standard orthographic) camera.
|
|
/// </summary>
|
|
/// <param name="cam">Cam.</param>
|
|
public static void setupPixelsToUnityUnitsMultiplierWithCamera( Camera cam )
|
|
{
|
|
if( !cam.orthographic )
|
|
{
|
|
Debug.LogError( "Attempting to setup unity pixel-to-units modifier with a non-orthographic camera" );
|
|
return;
|
|
}
|
|
|
|
var screenSizeUnityUnits = new Vector2( cam.aspect * cam.orthographicSize * 2f, cam.orthographicSize * 2f );
|
|
_instance.pixelsToUnityUnitsMultiplier = new Vector2
|
|
(
|
|
screenSizeUnityUnits.x / (float)Screen.width,
|
|
screenSizeUnityUnits.y / (float)Screen.height
|
|
);
|
|
}
|
|
|
|
|
|
public static void updateTouches()
|
|
{
|
|
if( _instance == null )
|
|
return;
|
|
|
|
_instance.internalUpdateTouches();
|
|
}
|
|
|
|
|
|
public static void addGestureRecognizer( TKAbstractGestureRecognizer recognizer )
|
|
{
|
|
// add, then sort and reverse so the higher zIndex items will be on top
|
|
instance._gestureRecognizers.Add( recognizer );
|
|
|
|
if( recognizer.zIndex > 0 )
|
|
{
|
|
_instance._gestureRecognizers.Sort();
|
|
_instance._gestureRecognizers.Reverse();
|
|
}
|
|
}
|
|
|
|
|
|
public static void removeGestureRecognizer( TKAbstractGestureRecognizer recognizer )
|
|
{
|
|
if( _instance == null )
|
|
return;
|
|
|
|
if( !_instance._gestureRecognizers.Contains( recognizer ) )
|
|
{
|
|
Debug.LogError( "Trying to remove gesture recognizer that has not been added: " + recognizer );
|
|
return;
|
|
}
|
|
|
|
recognizer.reset();
|
|
instance._gestureRecognizers.Remove( recognizer );
|
|
}
|
|
|
|
|
|
public static void removeAllGestureRecognizers()
|
|
{
|
|
if( _instance == null )
|
|
return;
|
|
|
|
instance._gestureRecognizers.Clear();
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|