using UnityEngine; using System.Collections.Generic; using System.Collections; using UnityEngine.Events; namespace Com.LuisPedroFonseca.ProCamera2D { [System.Serializable] public class Room { public string ID = ""; public Rect Dimensions; [Range(0f, 10f)] public float TransitionDuration; public EaseType TransitionEaseType; public bool ScaleCameraToFit; public bool Zoom; [Range(0.1f, 10f)] public float ZoomScale; public int InternalID; public Room(Room otherRoom) { Dimensions = otherRoom.Dimensions; TransitionDuration = otherRoom.TransitionDuration; TransitionEaseType = otherRoom.TransitionEaseType; ScaleCameraToFit = otherRoom.ScaleCameraToFit; Zoom = otherRoom.Zoom; ZoomScale = otherRoom.ZoomScale; } public Room() { } } [System.Serializable] public class RoomEvent : UnityEvent { } [HelpURLAttribute("http://www.procamera2d.com/user-guide/extension-rooms/")] public class ProCamera2DRooms : BasePC2D, IPositionOverrider, ISizeOverrider { public const string ExtensionName = "Rooms"; public int CurrentRoomIndex { get { return _currentRoomIndex; } } private int _currentRoomIndex = -1; public int PreviousRoomIndex { get { return _previousRoomIndex; } } private int _previousRoomIndex = -1; public Room CurrentRoom { get { return (_currentRoomIndex >= 0 && _currentRoomIndex < Rooms.Count) ? Rooms[_currentRoomIndex] : null; } } public float OriginalSize { get; private set; } public List Rooms = new List(); public float UpdateInterval = .1f; public bool UseTargetsMidPoint = true; public Transform TriggerTarget; public bool TransitionInstanlyOnStart = true; public bool RestoreOnRoomExit; public float RestoreDuration = 1f; public EaseType RestoreEaseType = EaseType.EaseInOut; public bool AutomaticRoomActivation = true; public bool UseRelativePosition; public RoomEvent OnStartedTransition = new RoomEvent(); public RoomEvent OnFinishedTransition = new RoomEvent(); public UnityEvent OnExitedAllRooms = new UnityEvent(); ProCamera2DNumericBoundaries _numericBoundaries; NumericBoundariesSettings _defaultNumericBoundariesSettings; bool _transitioning; Vector3 _newPos; float _newSize; Coroutine _transitionRoutine; private int _currentRoomID = -1; override protected void Awake() { base.Awake(); _numericBoundaries = ProCamera2D.GetComponent(); _defaultNumericBoundariesSettings = _numericBoundaries.Settings; OriginalSize = ProCamera2D.ScreenSizeInWorldCoordinates.y / 2; ProCamera2D.AddPositionOverrider(this); ProCamera2D.AddSizeOverrider(this); } void Start() { var instanceID = GetInstanceID(); var count = 0; foreach (var room in Rooms) { room.InternalID = instanceID + count; count++; } StartCoroutine(TestRoomRoutine()); if (TransitionInstanlyOnStart) { var targetPos = ProCamera2D.TargetsMidPoint; if (!UseTargetsMidPoint && TriggerTarget != null) targetPos = TriggerTarget.position; var startingRoom = ComputeCurrentRoom(targetPos); if (startingRoom != -1) EnterRoom(startingRoom, false); } } protected override void OnDestroy() { base.OnDestroy(); if(ProCamera2D == null) return; ProCamera2D.RemovePositionOverrider(this); ProCamera2D.RemoveSizeOverrider(this); } #region IPositionOverrider implementation public Vector3 OverridePosition(float deltaTime, Vector3 originalPosition) { if (!enabled) return originalPosition; return _transitioning ? _newPos : originalPosition; } public int POOrder { get { return _poOrder; } set { _poOrder = value; } } int _poOrder = 1001; #endregion #region ISizeOverrider implementation public float OverrideSize(float deltaTime, float originalSize) { if (!enabled) return originalSize; return _transitioning ? _newSize : originalSize; } public int SOOrder { get { return _soOrder; } set { _soOrder = value; } } int _soOrder = 3001; #endregion /// /// Manually test to see if the target(s) is inside a room /// public void TestRoom() { var targetPos = ProCamera2D.TargetsMidPoint; if (!UseTargetsMidPoint && TriggerTarget != null) targetPos = TriggerTarget.position; var roomToEnter = ComputeCurrentRoom(targetPos); if (roomToEnter != -1 && _currentRoomIndex != roomToEnter) { EnterRoom(roomToEnter); } if (roomToEnter == -1 && _currentRoomIndex != -1) { ExitRoom(); } } /// /// Returns what room the target currently is. Useful for when you set the AutomaticRoomActivation to false /// /// The current room /// Target position public int ComputeCurrentRoom(Vector3 targetPos) { int roomToEnter = -1; for (int i = 0; i < Rooms.Count; i++) { if (Utils.IsInsideRectangle( Rooms[i].Dimensions.x + (UseRelativePosition ? _transform.position.x : 0), Rooms[i].Dimensions.y + (UseRelativePosition ? _transform.position.y : 0), Rooms[i].Dimensions.width, Rooms[i].Dimensions.height, Vector3H(targetPos), Vector3V(targetPos))) { roomToEnter = i; } } return roomToEnter; } /// /// Enter a room. Only use when the AutomaticRoomActivation is set to false. /// /// The room number on the list /// Use a camera movement transition /// Forces the transition to the room (even if the current room is the same) public void EnterRoom(int roomIndex, bool useTransition = true, bool forceEntrance = false) { if (roomIndex < 0 || roomIndex > Rooms.Count - 1) throw new System.Exception("Can't find room with index: " + roomIndex); if (!forceEntrance && Rooms[roomIndex].InternalID == _currentRoomID) return; _previousRoomIndex = _currentRoomIndex; _currentRoomIndex = roomIndex; _currentRoomID = Rooms[roomIndex].InternalID; if (OnStartedTransition != null) OnStartedTransition.Invoke(roomIndex, _previousRoomIndex); TransitionToRoom(Rooms[_currentRoomIndex], useTransition); } /// /// Enter a room. Only use when the AutomaticRoomActivation is set to false. /// /// The room ID /// Use a camera movement transition /// Forces the transition to the room (even if the current room is the same) public void EnterRoom(string roomID, bool useTransition = true, bool forceEntrance = false) { var foundIndex = Rooms.FindIndex(room => room.ID == roomID); if (foundIndex < 0) throw new System.Exception("Can't find room with ID: " + roomID); EnterRoom(foundIndex, useTransition, forceEntrance); } /// /// Exit current room. Only use when the AutomaticRoomActivation is set to false. /// public void ExitRoom() { _currentRoomIndex = -1; _currentRoomID = -1; if (RestoreOnRoomExit) { if (OnStartedTransition != null) OnStartedTransition.Invoke(_currentRoomIndex, _previousRoomIndex); if (_transitionRoutine != null) StopCoroutine(_transitionRoutine); _transitionRoutine = StartCoroutine(TransitionRoutine(_defaultNumericBoundariesSettings, OriginalSize, RestoreDuration, RestoreEaseType)); } if (OnExitedAllRooms != null) OnExitedAllRooms.Invoke(); } /// /// Add a new room /// /// Room horizontal position /// Room vertical position /// Room width /// Room height /// Transition duration /// Transition ease type /// If set to true the camera will scale to fit the room /// If set to true the camera will scale to the zoomScale /// Zoom scale public void AddRoom( float roomX, float roomY, float roomWidth, float roomHeight, float transitionDuration = 1f, EaseType transitionEaseType = EaseType.EaseInOut, bool scaleToFit = false, bool zoom = false, float zoomScale = 1.5f, string id = "") { var newRoom = new Room() { ID = id, Dimensions = new Rect(roomX, roomY, roomWidth, roomHeight), TransitionDuration = transitionDuration, TransitionEaseType = transitionEaseType, ScaleCameraToFit = scaleToFit, Zoom = zoom, ZoomScale = zoomScale, InternalID = GetInstanceID() + Rooms.Count }; Rooms.Add(newRoom); } /// /// Removes a specific room from the list. /// /// Room name / ID public void RemoveRoom(string roomName) { var room = Rooms.Find((Room obj) => obj.ID == roomName); if (room != null) Rooms.Remove(room); else Debug.LogWarning(roomName + " not found in the Rooms list."); } /// /// Sets the default numeric boundaries settings (i.e. The boundaries to use when not inside any room). /// /// The numeric boundaries settings. /// You can grab them from the NumericBoundaries extension, or manually create them public void SetDefaultNumericBoundariesSettings(NumericBoundariesSettings settings) { _defaultNumericBoundariesSettings = settings; } /// /// Returns a room by its ID. From this you can get all the room properties such as dimensions, zoom, etc. /// /// The room ID public Room GetRoom(string roomID) { return Rooms.Find((Room obj) => obj.ID == roomID); } /// /// Returns an appropriate camera size for the specified rect representing a room /// /// The room rect public float GetCameraSizeForRoom(Rect roomRect) { var scaleFactorW = roomRect.width / ProCamera2D.ScreenSizeInWorldCoordinates.x; var scaleFactorH = roomRect.height / ProCamera2D.ScreenSizeInWorldCoordinates.y; if (scaleFactorW < scaleFactorH) return roomRect.width / ProCamera2D.GameCamera.aspect / 2f; else return roomRect.height / 2f; } IEnumerator TestRoomRoutine() { yield return new WaitForEndOfFrame(); var waitForSeconds = new WaitForSeconds(UpdateInterval); var waitForSecondsRealtime = new WaitForSecondsRealtime(UpdateInterval); while (true) { if (AutomaticRoomActivation) TestRoom(); if(ProCamera2D.IgnoreTimeScale) yield return waitForSecondsRealtime; else yield return waitForSeconds; } } void TransitionToRoom(Room room, bool useTransition = true) { // Stop any previous transition if (_transitionRoutine != null) StopCoroutine(_transitionRoutine); // Numeric boundaries var numericBoundariesSettings = new NumericBoundariesSettings() { UseNumericBoundaries = true, UseTopBoundary = true, TopBoundary = (room.Dimensions.y + (UseRelativePosition ? _transform.position.y : 0)) + room.Dimensions.height / 2, UseBottomBoundary = true, BottomBoundary = (room.Dimensions.y + (UseRelativePosition ? _transform.position.y : 0)) - room.Dimensions.height / 2, UseLeftBoundary = true, LeftBoundary = (room.Dimensions.x + (UseRelativePosition ? _transform.position.x : 0)) - room.Dimensions.width / 2, UseRightBoundary = true, RightBoundary = (room.Dimensions.x + (UseRelativePosition ? _transform.position.x : 0)) + room.Dimensions.width / 2 }; // Size var targetSize = ProCamera2D.ScreenSizeInWorldCoordinates.y / 2f; var cameraSizeForRoom = GetCameraSizeForRoom(room.Dimensions); if (room.ScaleCameraToFit) { targetSize = cameraSizeForRoom; } else if (room.Zoom && OriginalSize * room.ZoomScale <= cameraSizeForRoom) { targetSize = OriginalSize * room.ZoomScale; } else if (cameraSizeForRoom < targetSize) { targetSize = cameraSizeForRoom; } // Move camera "manually" _transitionRoutine = StartCoroutine(TransitionRoutine(numericBoundariesSettings, targetSize, useTransition ? room.TransitionDuration : 0f, room.TransitionEaseType)); } IEnumerator TransitionRoutine(NumericBoundariesSettings numericBoundariesSettings, float targetSize, float transitionDuration = 1f, EaseType transitionEaseType = EaseType.EaseOut) { _transitioning = true; // Disable the current numeric boundaries _numericBoundaries.UseNumericBoundaries = false; // Size var initialSize = ProCamera2D.ScreenSizeInWorldCoordinates.y / 2f; //Position var initialCamPosH = Vector3H(ProCamera2D.LocalPosition); var initialCamPosV = Vector3V(ProCamera2D.LocalPosition); // Transition var t = 0f; while (t <= 1.0f) { // Prevents a NaN error on some platforms if (transitionDuration < float.Epsilon) t = 1.1f; else if (ProCamera2D.DeltaTime > float.Epsilon) t += ProCamera2D.DeltaTime / transitionDuration; // Size _newSize = transitionDuration > 0 ? Utils.EaseFromTo(initialSize, targetSize, t, transitionEaseType) : targetSize; // Position var targetPosH = ProCamera2D.CameraTargetPositionSmoothed.x; var targetPosV = ProCamera2D.CameraTargetPositionSmoothed.y; LimitToNumericBoundaries( ref targetPosH, ref targetPosV, targetSize * ProCamera2D.GameCamera.aspect, targetSize, numericBoundariesSettings); var newPosH = Utils.EaseFromTo(initialCamPosH, targetPosH, t, transitionEaseType); var newPosV = Utils.EaseFromTo(initialCamPosV, targetPosV, t, transitionEaseType); _newPos = VectorHVD(newPosH, newPosV, 0); if(transitionDuration > 0) yield return ProCamera2D.GetYield(); } if (transitionDuration == 0) yield return ProCamera2D.GetYield(); if (ProCamera2D.UpdateType == UpdateType.FixedUpdate && ProCamera2D.IgnoreTimeScale) yield return new WaitForFixedUpdate(); _transitioning = false; _numericBoundaries.Settings = numericBoundariesSettings; _transitionRoutine = null; if (OnFinishedTransition != null) OnFinishedTransition.Invoke(_currentRoomIndex, _previousRoomIndex); _previousRoomIndex = _currentRoomIndex; } void LimitToNumericBoundaries( ref float horizontalPos, ref float verticalPos, float halfCameraWidth, float halfCameraHeight, NumericBoundariesSettings numericBoundaries) { if (numericBoundaries.UseLeftBoundary && horizontalPos - halfCameraWidth < numericBoundaries.LeftBoundary) horizontalPos = numericBoundaries.LeftBoundary + halfCameraWidth; else if (numericBoundaries.UseRightBoundary && horizontalPos + halfCameraWidth > numericBoundaries.RightBoundary) horizontalPos = numericBoundaries.RightBoundary - halfCameraWidth; if (numericBoundaries.UseBottomBoundary && verticalPos - halfCameraHeight < numericBoundaries.BottomBoundary) verticalPos = numericBoundaries.BottomBoundary + halfCameraHeight; else if (numericBoundaries.UseTopBoundary && verticalPos + halfCameraHeight > numericBoundaries.TopBoundary) verticalPos = numericBoundaries.TopBoundary - halfCameraHeight; } #if UNITY_EDITOR override protected void DrawGizmos() { base.DrawGizmos(); UnityEditor.Handles.color = EditorPrefsX.GetColor(PrefsData.RoomsColorKey, PrefsData.RoomsColorValue); for (int i = 0; i < Rooms.Count; i++) { // Room border var rect = Rooms[i].Dimensions; rect.x -= rect.width / 2f - (UseRelativePosition ? transform.position.x : 0); rect.y -= rect.height / 2f - (UseRelativePosition ? transform.position.y : 0); Vector3[] rectangleCorners = { VectorHVD(rect.position.x, rect.position.y, 0), // Bottom Left VectorHVD(rect.position.x + rect.width, rect.position.y, 0), // Bottom Right VectorHVD(rect.position.x + rect.width, rect.position.y + rect.height, 0), // Top Right VectorHVD(rect.position.x, rect.position.y + rect.height, 0) // Top Left }; UnityEditor.Handles.DrawSolidRectangleWithOutline(rectangleCorners, Color.clear, Color.white); } } #endif } }