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);
}
}
}