public void PanToView(View view, float duration, Action arriveAction)
        {
            Reset();

            if (duration == 0f)
            {
                // Move immediately
                mainCamera.orthographicSize = view.viewSize;
                mainCamera.transform.position = view.transform.position;
                SetCameraZ();
                if (arriveAction != null)
                {
                    arriveAction();
                }
            }
            else
            {
                moveDuration = duration;
                onArriveAction = arriveAction;
                moveTimer = 0;
                startSize = mainCamera.orthographicSize;
                endSize = view.viewSize;
                startPos = mainCamera.transform.position;
                endPos = view.transform.position;
            }
        }
        public void FadeToView(View view, float fadeDuration, Action fadeAction)
        {
            // Fade out
            Fade(0f, fadeDuration / 2f, delegate {

                // Snap to new view
                PanToView(view, 0f, null);

                // Fade in
                Fade(1f, fadeDuration / 2f, delegate {
                    if (fadeAction != null)
                    {
                        fadeAction();
                    }
                });
            });
        }
 // Pans the camera to the target view of a period of time
 public void PanToView(View targetView, float duration)
 {
     commandQueue.AddCommand(new PanToViewCommand(targetView, duration));
 }
        public SetViewCommand(View _view)
        {
            if (_view == null)
            {
                Debug.LogError("View must not be null");
            }

            view = _view;
        }
        public PanToViewCommand(View _view,
		                        float _duration)
        {
            if (_view == null)
            {
                Debug.LogError("View must not be null.");
                return;
            }

            view = _view;
            duration = _duration;
        }
 public void SnapToView(View view)
 {
     PanToView(view, 0, null);
 }
		// Clamp camera position to region defined by the two views
		protected virtual Vector3 CalcCameraPosition(Vector3 pos, View viewA, View viewB)
		{
			Vector3 safePos = pos;
			
			// Clamp camera position to region defined by the two views
			safePos.x = Mathf.Max(safePos.x, Mathf.Min(viewA.transform.position.x, viewB.transform.position.x));
			safePos.x = Mathf.Min(safePos.x, Mathf.Max(viewA.transform.position.x, viewB.transform.position.x));
			safePos.y = Mathf.Max(safePos.y, Mathf.Min(viewA.transform.position.y, viewB.transform.position.y));
			safePos.y = Mathf.Min(safePos.y, Mathf.Max(viewA.transform.position.y, viewB.transform.position.y));
			
			return safePos;
		}
		/**
		 * Activates swipe panning mode.
		 * The player can pan the camera within the area between viewA & viewB.
		 */
		public void StartSwipePan(View viewA, View viewB, float duration, Action arriveAction)
		{
			swipePanViewA = viewA;
			swipePanViewB = viewB;
			
			Vector3 cameraPos = Camera.main.transform.position;
			
			Vector3 targetPosition = CalcCameraPosition(cameraPos, swipePanViewA, swipePanViewB);
			float targetSize = CalcCameraSize(cameraPos, swipePanViewA, swipePanViewB); 
			
			PanToPosition(targetPosition, Quaternion.identity, targetSize, duration, delegate {
				
				swipePanActive = true;
				
				if (arriveAction != null)
				{
					arriveAction();
				}
			}); 
		}
		/**
		 * Activates swipe panning mode.
		 * The player can pan the camera within the area between viewA & viewB.
		 */
		public virtual void StartSwipePan(View viewA, View viewB, float duration, float speedMultiplier, Action arriveAction)
		{
			Camera camera = GetCamera();
			if (camera == null)
			{
				return;
			}

			swipePanViewA = viewA;
			swipePanViewB = viewB;
			swipeSpeedMultiplier = speedMultiplier;
			
			Vector3 cameraPos = camera.transform.position;
			
			Vector3 targetPosition = CalcCameraPosition(cameraPos, swipePanViewA, swipePanViewB);
			float targetSize = CalcCameraSize(cameraPos, swipePanViewA, swipePanViewB); 
			
			PanToPosition(targetPosition, Quaternion.identity, targetSize, duration, delegate {
				
				swipePanActive = true;
				
				if (arriveAction != null)
				{
					arriveAction();
				}
			}); 
		}
		/**
		 * Deactivates swipe panning mode.
		 */
		public virtual void StopSwipePan()
		{
			swipePanActive = false;
			swipePanViewA = null;
			swipePanViewB = null;
		}
		/**
		 * Moves camera smoothly through a sequence of Views over a period of time
		 */
		public virtual void PanToPath(View[] viewList, float duration, Action arriveAction)
		{
			Camera camera = GetCamera();
			if (camera == null)
			{
				return;
			}

			swipePanActive = false;
			
			List<Vector3> pathList = new List<Vector3>();
			
			// Add current camera position as first point in path
			// Note: We use the z coord to tween the camera orthographic size
			Vector3 startPos = new Vector3(camera.transform.position.x,
			                               camera.transform.position.y,
			                               camera.orthographicSize);
			pathList.Add(startPos);
			
			for (int i = 0; i < viewList.Length; ++i)
			{
				View view = viewList[i];
				
				Vector3 viewPos = new Vector3(view.transform.position.x, 
				                              view.transform.position.y, 
				                              view.viewSize);
				pathList.Add(viewPos);
			}
			
			StartCoroutine(PanToPathInternal(duration, arriveAction, pathList.ToArray()));
		}
		public virtual void PanToView(View view, float duration, Action arriveAction)
		{
			PanToPosition(view.transform.position, view.transform.rotation, view.viewSize, duration, arriveAction);
		}
		/**
		 * Fade out, move camera to view and then fade back in.
		 */
		public virtual void FadeToView(View view, float fadeDuration, bool fadeOut, Action fadeAction)
		{
			swipePanActive = false;
			fadeAlpha = 0f;

			float outDuration;
			float inDuration;

			if (fadeOut)
			{
				outDuration = fadeDuration / 2f;
				inDuration = fadeDuration / 2f;
			}
			else
			{
				outDuration = 0;
				inDuration = fadeDuration;
			}

			// Fade out
			Fade(1f, outDuration, delegate {
				
				// Snap to new view
				PanToPosition(view.transform.position, view.transform.rotation, view.viewSize, 0f, null);
				
				// Fade in
				Fade(0f, inDuration, delegate {
					if (fadeAction != null)
					{
						fadeAction();
					}
				});
			});
		}
 // Sets the currently active view immediately.
 // The main camera snaps to the active view.
 public void SetView(View view)
 {
     commandQueue.AddCommand(new SetViewCommand(view));
 }
		// Smoothly interpolate camera orthographic size based on relative position to two views
		protected virtual float CalcCameraSize(Vector3 pos, View viewA, View viewB)
		{
			// Get ray and point in same space
			Vector3 toViewB = viewB.transform.position - viewA.transform.position;
			Vector3 localPos = pos - viewA.transform.position;
			
			// Normalize
			float distance = toViewB.magnitude;
			toViewB /= distance;
			localPos /= distance;
			
			// Project point onto ray
			float t = Vector3.Dot(toViewB, localPos);
			t = Mathf.Clamp01(t); // Not really necessary but no harm
			
			float cameraSize = Mathf.Lerp(viewA.viewSize, viewB.viewSize, t);
			
			return cameraSize;
		}
 // Snaps the camera to the target view immediately
 public void SnapToView(View targetView)
 {
     commandQueue.AddCommand(new PanToViewCommand(targetView, 0f));
 }
		/**
		 * Fade out, move camera to view and then fade back in.
		 */
		public void FadeToView(View view, float fadeDuration, Action fadeAction)
		{
			swipePanActive = false;
			fadeAlpha = 0f;
			
			// Fade out
			Fade(1f, fadeDuration / 2f, delegate {
				
				// Snap to new view
				PanToPosition(view.transform.position, view.transform.rotation, view.viewSize, 0f, null);
				
				// Fade in
				Fade(0f, fadeDuration / 2f, delegate {
					if (fadeAction != null)
					{
						fadeAction();
					}
				});
			});
		}