// This code is part of the Fungus library (https://github.com/snozbot/fungus) // It is released for free under the MIT open source license (https://github.com/snozbot/fungus/blob/master/LICENSE) using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using Fungus; namespace Fungus { /// /// Manager for main camera. Supports several types of camera transition including snap, pan & fade. /// public class CameraManager : MonoBehaviour { [Tooltip("Full screen texture used for screen fade effect.")] [SerializeField] protected Texture2D screenFadeTexture; [Tooltip("Icon to display when swipe pan mode is active.")] [SerializeField] protected Texture2D swipePanIcon; [Tooltip("Position of continue and swipe icons in normalized screen space coords. (0,0) = top left, (1,1) = bottom right")] [SerializeField] protected Vector2 swipeIconPosition = new Vector2(1,0); [Tooltip("Set the camera z coordinate to a fixed value every frame.")] [SerializeField] protected bool setCameraZ = true; [Tooltip("Fixed Z coordinate of main camera.")] [SerializeField] protected float cameraZ = -10f; [Tooltip("Camera to use when in swipe mode")] [SerializeField] protected Camera swipeCamera; protected float fadeAlpha = 0f; // Swipe panning control protected bool swipePanActive; protected float swipeSpeedMultiplier = 1f; protected View swipePanViewA; protected View swipePanViewB; protected Vector3 previousMousePos; //Coroutine handles for panning and fading commands protected LTDescr fadeTween, sizeTween, camPosTween, camRotTween; protected class CameraView { public Vector3 cameraPos; public Quaternion cameraRot; public float cameraSize; }; protected Dictionary storedViews = new Dictionary(); protected virtual void OnGUI() { if (swipePanActive) { // Draw the swipe panning icon if (swipePanIcon) { float x = Screen.width * swipeIconPosition.x; float y = Screen.height * swipeIconPosition.y; float width = swipePanIcon.width; float height = swipePanIcon.height; x = Mathf.Max(x, 0); y = Mathf.Max(y, 0); x = Mathf.Min(x, Screen.width - width); y = Mathf.Min(y, Screen.height - height); Rect rect = new Rect(x, y, width, height); GUI.DrawTexture(rect, swipePanIcon); } } // Draw full screen fade texture if (fadeAlpha > 0f && screenFadeTexture != null) { // 1 = scene fully visible // 0 = scene fully obscured GUI.color = new Color(1,1,1, fadeAlpha); GUI.depth = -1000; GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), screenFadeTexture); } } protected virtual void SetCameraZ(Camera camera) { if (!setCameraZ) { return; } if (camera == null) { Debug.LogWarning("Camera is null"); return; } camera.transform.position = new Vector3(camera.transform.position.x, camera.transform.position.y, cameraZ); } protected virtual void Update() { if (!swipePanActive) { return; } if (swipeCamera == null) { Debug.LogWarning("Camera is null"); return; } Vector3 delta = Vector3.zero; if (Input.touchCount > 0) { if (Input.GetTouch(0).phase == TouchPhase.Moved) { delta = Input.GetTouch(0).deltaPosition; } } if (Input.GetMouseButtonDown(0)) { previousMousePos = Input.mousePosition; } else if (Input.GetMouseButton(0)) { delta = Input.mousePosition - previousMousePos; previousMousePos = Input.mousePosition; } Vector3 cameraDelta = swipeCamera.ScreenToViewportPoint(delta); cameraDelta.x *= -2f * swipeSpeedMultiplier; cameraDelta.y *= -2f * swipeSpeedMultiplier; cameraDelta.z = 0f; Vector3 cameraPos = swipeCamera.transform.position; cameraPos += cameraDelta; swipeCamera.transform.position = CalcCameraPosition(cameraPos, swipePanViewA, swipePanViewB); swipeCamera.orthographicSize = CalcCameraSize(cameraPos, swipePanViewA, swipePanViewB); } // 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; } // 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; } #region Public members /// /// Creates a flat colored texture. /// public static Texture2D CreateColorTexture(Color color, int width, int height) { Color[] pixels = new Color[width * height]; for (int i = 0; i < pixels.Length; i++) { pixels[i] = color; } Texture2D texture = new Texture2D(width, height, TextureFormat.ARGB32, false); texture.SetPixels(pixels); texture.Apply(); return texture; } /// /// Full screen texture used for screen fade effect. /// /// The screen fade texture. public Texture2D ScreenFadeTexture { set { screenFadeTexture = value; } } /// /// Perform a fullscreen fade over a duration. /// public virtual void Fade(float targetAlpha, float fadeDuration, Action fadeAction, LeanTweenType leanTweenType = LeanTweenType.easeInOutQuad) { StopFadeTween(); if (Mathf.Approximately(fadeDuration, 0)) { fadeAlpha = targetAlpha; if (fadeAction != null) fadeAction(); } else { fadeTween = LeanTween.value(fadeAlpha, targetAlpha, fadeDuration) .setEase(leanTweenType) .setOnUpdate((x) => fadeAlpha = x) .setOnComplete(() => { fadeAlpha = targetAlpha; if (fadeAction != null) fadeAction(); fadeTween = null; }); } } /// /// Fade out, move camera to view and then fade back in. /// public virtual void FadeToView(Camera camera, View view, float fadeDuration, bool fadeOut, Action fadeAction, LeanTweenType fadeType = LeanTweenType.easeInOutQuad, LeanTweenType sizeTweenType = LeanTweenType.easeInOutQuad, LeanTweenType posTweenType = LeanTweenType.easeInOutQuad, LeanTweenType rotTweenType = LeanTweenType.easeInOutQuad) { 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(camera, view.transform.position, view.transform.rotation, view.ViewSize, 0f, null, sizeTweenType, posTweenType, rotTweenType); // Fade in Fade(0f, inDuration, delegate { if (fadeAction != null) { fadeAction(); } }, fadeType); }, fadeType); } /// /// Stop all camera tweening. /// public virtual void Stop() { StopFadeTween(); StopPosTweens(); } protected void StopFadeTween() { if (fadeTween != null) { LeanTween.cancel(fadeTween.id, true); fadeTween = null; } } protected void StopPosTweens() { if (sizeTween != null) { LeanTween.cancel(sizeTween.id, true); sizeTween = null; } if (camPosTween != null) { LeanTween.cancel(camPosTween.id, true); camPosTween = null; } if (camRotTween != null) { LeanTween.cancel(camRotTween.id, true); camRotTween = null; } } /// /// Moves camera from current position to a target position over a period of time. /// public virtual void PanToPosition(Camera camera, Vector3 targetPosition, Quaternion targetRotation, float targetSize, float duration, Action arriveAction, LeanTweenType sizeTweenType = LeanTweenType.easeInOutQuad, LeanTweenType posTweenType = LeanTweenType.easeInOutQuad, LeanTweenType rotTweenType = LeanTweenType.easeInOutQuad) { if (camera == null) { Debug.LogWarning("Camera is null"); return; } if(setCameraZ) { targetPosition.z = camera.transform.position.z; } // Stop any pan that is currently active StopPosTweens(); swipePanActive = false; if (Mathf.Approximately(duration, 0f)) { // Move immediately camera.orthographicSize = targetSize; camera.transform.position = targetPosition; camera.transform.rotation = targetRotation; SetCameraZ(camera); if (arriveAction != null) { arriveAction(); } } else { sizeTween = LeanTween.value(camera.orthographicSize, targetSize, duration) .setEase(sizeTweenType) .setOnUpdate(x => camera.orthographicSize = x) .setOnComplete(() => { camera.orthographicSize = targetSize; if (arriveAction != null) arriveAction(); sizeTween = null; }); camPosTween = LeanTween.move(camera.gameObject, targetPosition, duration) .setEase(posTweenType) .setOnComplete(() => { camera.transform.position = targetPosition; camPosTween = null; }); camRotTween = LeanTween.rotate(camera.gameObject, targetRotation.eulerAngles, duration) .setEase(rotTweenType) .setOnComplete(() => { camera.transform.rotation = targetRotation; camRotTween = null; }); } } /// /// Activates swipe panning mode. The player can pan the camera within the area between viewA & viewB. /// public virtual void StartSwipePan(Camera camera, View viewA, View viewB, float duration, float speedMultiplier, Action arriveAction) { if (camera == null) { Debug.LogWarning("Camera is 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(camera, targetPosition, Quaternion.identity, targetSize, duration, delegate { swipePanActive = true; swipeCamera = camera; if (arriveAction != null) { arriveAction(); } }); } /// /// Deactivates swipe panning mode. /// public virtual void StopSwipePan() { swipePanActive = false; swipePanViewA = null; swipePanViewB = null; swipeCamera = null; } #endregion } }