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; /// /// lets TouchKit know if it should scale all rects and distances based on the designTimeResolution /// public bool autoScaleRectsAndDistances = true; /// /// if false, TouchKit will not do anything in Update. You will need to call updateTouches yourself. /// public bool shouldAutoUpdateTouches = true; private Vector2 _designTimeResolution = new Vector2( 320, 180 ); // 16:9 is a decent starting point for aspect ratio /// /// all TKRect sizes should be based on this screen size. They will be adjusted at runtime if autoUpdateRects is true /// public Vector2 designTimeResolution { get { return _designTimeResolution; } set { _designTimeResolution = value; setupRuntimeScale(); } } public int maxTouchesToProcess = 2; /// /// used at runtime to scale any TKRects as they are made for the current screen size /// public Vector2 runtimeScaleModifier { get; private set; } /// /// used at runtime to modify distances /// public float runtimeDistanceModifier { get; private set; } /// /// 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! /// public Vector2 pixelsToUnityUnitsMultiplier { get; private set; } private List _gestureRecognizers = new List( 5 ); private TKTouch[] _touchCache; private List _liveTouches = new List( 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(); 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; } } /// /// Unity often misses the Ended phase of touches so this method will look out for that /// 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 /// /// 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. /// /// Cam. 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 }