using UnityEngine; using UnityInput = UnityEngine.Input; namespace Core.Input { /// /// Base control scheme for touch devices, which performs CameraRig control /// public class TouchInput : CameraInputScheme { /// /// Configuration of the pan speed /// public float panSpeed = 5; /// /// How quickly flicks decay /// public float flickDecayFactor = 0.2f; /// /// Flick direction /// Vector3 m_FlickDirection; /// /// Gets whether the scheme should be activated or not /// public override bool shouldActivate { get { return UnityInput.touchCount > 0; } } /// /// This default scheme on IOS and Android devices /// public override bool isDefault { get { #if UNITY_IOS || UNITY_ANDROID return true; #else return false; #endif } } /// /// Register input events /// protected virtual void OnEnable() { if (!InputController.instanceExists) { Debug.LogError("[UI] Keyboard and Mouse UI requires InputController"); return; } // Register drag event InputController inputController = InputController.instance; inputController.pressed += OnPress; inputController.released += OnRelease; inputController.dragged += OnDrag; inputController.pinched += OnPinch; } /// /// Deregister input events /// protected virtual void OnDisable() { if (!InputController.instanceExists) { return; } if (InputController.instanceExists) { InputController inputController = InputController.instance; inputController.pressed -= OnPress; inputController.released -= OnRelease; inputController.dragged -= OnDrag; inputController.pinched -= OnPinch; } } /// /// Perform flick and zoom /// protected virtual void Update() { if (cameraRig != null) { UpdateFlick(); DecayZoom(); } } /// /// Called on input press /// protected virtual void OnPress(PointerActionInfo pointer) { if (cameraRig != null) { DoFlickCatch(pointer); } } /// /// Called on input release /// protected virtual void OnRelease(PointerActionInfo pointer) { if (cameraRig != null) { DoReleaseFlick(pointer); } } /// /// Called when we drag /// protected virtual void OnDrag(PointerActionInfo pointer) { // Drag panning for touch input if (cameraRig != null) { DoDragPan(pointer); } } /// /// Called on pinch gestures /// protected virtual void OnPinch(PinchInfo pinch) { if (cameraRig != null) { DoPinchZoom(pinch); } } /// /// Update current flick velocity /// protected void UpdateFlick() { // Flick? if (m_FlickDirection.sqrMagnitude > Mathf.Epsilon) { cameraRig.PanCamera(m_FlickDirection * Time.deltaTime); m_FlickDirection *= flickDecayFactor; } } /// /// Decay the zoom if no touches are active /// protected void DecayZoom() { if (InputController.instance.activeTouchCount == 0) { cameraRig.ZoomDecay(); } } /// /// "Catch" flicks on press, to stop the panning momentum /// /// The press pointer event protected void DoFlickCatch(PointerActionInfo pointer) { var touchInfo = pointer as TouchInfo; // Stop flicks on touch if (touchInfo != null) { m_FlickDirection = Vector2.zero; cameraRig.StopTracking(); } } /// /// Do flicks, on release only /// /// The release pointer event protected void DoReleaseFlick(PointerActionInfo pointer) { var touchInfo = pointer as TouchInfo; if (touchInfo != null && touchInfo.flickVelocity.sqrMagnitude > Mathf.Epsilon) { // We have a flick! // Work out velocity from motion Ray prevRay = cameraRig.cachedCamera.ScreenPointToRay(pointer.currentPosition - pointer.flickVelocity); Ray currRay = cameraRig.cachedCamera.ScreenPointToRay(pointer.currentPosition); Vector3 startPoint = Vector3.zero; Vector3 endPoint = Vector3.zero; float dist; if (cameraRig.floorPlane.Raycast(prevRay, out dist)) { startPoint = prevRay.GetPoint(dist); } if (cameraRig.floorPlane.Raycast(currRay, out dist)) { endPoint = currRay.GetPoint(dist); } // Work out that movement in units per second m_FlickDirection = (startPoint - endPoint) / Time.deltaTime; } } /// /// Controls the pan with a drag /// protected void DoDragPan(PointerActionInfo pointer) { var touchInfo = pointer as TouchInfo; if (touchInfo != null) { // Work out movement amount by raycasting onto floor plane from delta positions // and getting that distance Ray currRay = cameraRig.cachedCamera.ScreenPointToRay(touchInfo.currentPosition); Vector3 endPoint = Vector3.zero; float dist; if (cameraRig.floorPlane.Raycast(currRay, out dist)) { endPoint = currRay.GetPoint(dist); } // Pan Ray prevRay = cameraRig.cachedCamera.ScreenPointToRay(touchInfo.previousPosition); Vector3 startPoint = Vector3.zero; if (cameraRig.floorPlane.Raycast(prevRay, out dist)) { startPoint = prevRay.GetPoint(dist); } Vector3 panAmount = startPoint - endPoint; // If this is a touch, we divide the pan amount by the number of touches if (UnityInput.touchCount > 0) { panAmount /= UnityInput.touchCount; } PanCamera(panAmount); } } /// /// Perform a zoom with the given pinch /// protected void DoPinchZoom(PinchInfo pinch) { float currentDistance = (pinch.touch1.currentPosition - pinch.touch2.currentPosition).magnitude; float prevDistance = (pinch.touch1.previousPosition - pinch.touch2.previousPosition).magnitude; float zoomChange = prevDistance / currentDistance; float prevZoomDist = cameraRig.zoomDist; cameraRig.SetZoom(zoomChange * cameraRig.rawZoomDist); // Calculate actual zoom change after clamping zoomChange = cameraRig.zoomDist / prevZoomDist; // First get floor position of middle of gesture Vector2 averageScreenPos = (pinch.touch1.currentPosition + pinch.touch2.currentPosition) * 0.5f; Ray ray = cameraRig.cachedCamera.ScreenPointToRay(averageScreenPos); Vector3 worldPos = Vector3.zero; float dist; if (cameraRig.floorPlane.Raycast(ray, out dist)) { worldPos = ray.GetPoint(dist); } // Vector from our current look pos to this point Vector3 offsetValue = worldPos - cameraRig.lookPosition; // Pan towards or away from our zoom center PanCamera(offsetValue * (1 - zoomChange)); } /// /// Pans the camera /// /// /// The vector to pan /// protected void PanCamera(Vector3 panAmount) { cameraRig.StopTracking(); cameraRig.PanCamera(panAmount); } } }