| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 | using UnityEngine;using UnityEngine.UI;using UnityEngine.EventSystems;using System.Collections;using TMPro;#if UNITY_EDITOR && UNITY_2021_1_OR_NEWERusing Screen = UnityEngine.Device.Screen; // To support Device Simulator on Unity 2021.1+#endif// Manager class for the debug popupnamespace IngameDebugConsole{	public class DebugLogPopup : MonoBehaviour, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler	{		private RectTransform popupTransform;		// Dimensions of the popup divided by 2		private Vector2 halfSize;		// Background image that will change color to indicate an alert		private Image backgroundImage;		// Canvas group to modify visibility of the popup		private CanvasGroup canvasGroup;#pragma warning disable 0649		[SerializeField]		private DebugLogManager debugManager;		[SerializeField]		private TextMeshProUGUI newInfoCountText;		[SerializeField]		private TextMeshProUGUI newWarningCountText;		[SerializeField]		private TextMeshProUGUI newErrorCountText;		[SerializeField]		private Color alertColorInfo;		[SerializeField]		private Color alertColorWarning;		[SerializeField]		private Color alertColorError;#pragma warning restore 0649		// Number of new debug entries since the log window has been closed		private int newInfoCount = 0, newWarningCount = 0, newErrorCount = 0;		private Color normalColor;		private bool isPopupBeingDragged = false;		private Vector2 normalizedPosition;		// Coroutines for simple code-based animations		private IEnumerator moveToPosCoroutine = null;		public bool IsVisible { get; private set; }		private void Awake()		{			popupTransform = (RectTransform) transform;			backgroundImage = GetComponent<Image>();			canvasGroup = GetComponent<CanvasGroup>();			normalColor = backgroundImage.color;			halfSize = popupTransform.sizeDelta * 0.5f;			Vector2 pos = popupTransform.anchoredPosition;			if( pos.x != 0f || pos.y != 0f )				normalizedPosition = pos.normalized; // Respect the initial popup position set in the prefab			else				normalizedPosition = new Vector2( 0.5f, 0f ); // Right edge by default		}		public void NewLogsArrived( int newInfo, int newWarning, int newError )		{			if( newInfo > 0 )			{				newInfoCount += newInfo;				newInfoCountText.text = newInfoCount.ToString();			}			if( newWarning > 0 )			{				newWarningCount += newWarning;				newWarningCountText.text = newWarningCount.ToString();			}			if( newError > 0 )			{				newErrorCount += newError;				newErrorCountText.text = newErrorCount.ToString();			}			if( newErrorCount > 0 )				backgroundImage.color = alertColorError;			else if( newWarningCount > 0 )				backgroundImage.color = alertColorWarning;			else				backgroundImage.color = alertColorInfo;		}		private void ResetValues()		{			newInfoCount = 0;			newWarningCount = 0;			newErrorCount = 0;			newInfoCountText.text = "0";			newWarningCountText.text = "0";			newErrorCountText.text = "0";			backgroundImage.color = normalColor;		}		// A simple smooth movement animation		private IEnumerator MoveToPosAnimation( Vector2 targetPos )		{			float modifier = 0f;			Vector2 initialPos = popupTransform.anchoredPosition;			while( modifier < 1f )			{				modifier += 4f * Time.unscaledDeltaTime;				popupTransform.anchoredPosition = Vector2.Lerp( initialPos, targetPos, modifier );				yield return null;			}		}		// Popup is clicked		public void OnPointerClick( PointerEventData data )		{			// Hide the popup and show the log window			if( !isPopupBeingDragged )				debugManager.ShowLogWindow();		}		// Hides the log window and shows the popup		public void Show()		{			canvasGroup.blocksRaycasts = true;			canvasGroup.alpha = debugManager.popupOpacity;			IsVisible = true;			// Reset the counters			ResetValues();			// Update position in case resolution was changed while the popup was hidden			UpdatePosition( true );		}		// Hide the popup		public void Hide()		{			canvasGroup.blocksRaycasts = false;			canvasGroup.alpha = 0f;			IsVisible = false;			isPopupBeingDragged = false;		}		public void OnBeginDrag( PointerEventData data )		{			isPopupBeingDragged = true;			// If a smooth movement animation is in progress, cancel it			if( moveToPosCoroutine != null )			{				StopCoroutine( moveToPosCoroutine );				moveToPosCoroutine = null;			}		}		// Reposition the popup		public void OnDrag( PointerEventData data )		{			Vector2 localPoint;			if( RectTransformUtility.ScreenPointToLocalPointInRectangle( debugManager.canvasTR, data.position, data.pressEventCamera, out localPoint ) )				popupTransform.anchoredPosition = localPoint;		}		// Smoothly translate the popup to the nearest edge		public void OnEndDrag( PointerEventData data )		{			isPopupBeingDragged = false;			UpdatePosition( false );		}		// There are 2 different spaces used in these calculations:		// RectTransform space: raw anchoredPosition of the popup that's in range [-canvasSize/2, canvasSize/2]		// Safe area space: Screen.safeArea space that's in range [safeAreaBottomLeft, safeAreaTopRight] where these corner positions		//                  are all positive (calculated from bottom left corner of the screen instead of the center of the screen)		public void UpdatePosition( bool immediately )		{			Vector2 canvasRawSize = debugManager.canvasTR.rect.size;			// Calculate safe area bounds			float canvasWidth = canvasRawSize.x;			float canvasHeight = canvasRawSize.y;			float canvasBottomLeftX = 0f;			float canvasBottomLeftY = 0f;			if( debugManager.popupAvoidsScreenCutout )			{#if UNITY_EDITOR || UNITY_ANDROID || UNITY_IOS				Rect safeArea = Screen.safeArea;				int screenWidth = Screen.width;				int screenHeight = Screen.height;				canvasWidth *= safeArea.width / screenWidth;				canvasHeight *= safeArea.height / screenHeight;				canvasBottomLeftX = canvasRawSize.x * ( safeArea.x / screenWidth );				canvasBottomLeftY = canvasRawSize.y * ( safeArea.y / screenHeight );#endif			}			// Calculate safe area position of the popup			// normalizedPosition allows us to glue the popup to a specific edge of the screen. It becomes useful when			// the popup is at the right edge and we switch from portrait screen orientation to landscape screen orientation.			// Without normalizedPosition, popup could jump to bottom or top edges instead of staying at the right edge			Vector2 pos = canvasRawSize * 0.5f + ( immediately ? new Vector2( normalizedPosition.x * canvasWidth, normalizedPosition.y * canvasHeight ) : ( popupTransform.anchoredPosition - new Vector2( canvasBottomLeftX, canvasBottomLeftY ) ) );			// Find distances to all four edges of the safe area			float distToLeft = pos.x;			float distToRight = canvasWidth - distToLeft;			float distToBottom = pos.y;			float distToTop = canvasHeight - distToBottom;			float horDistance = Mathf.Min( distToLeft, distToRight );			float vertDistance = Mathf.Min( distToBottom, distToTop );			// Find the nearest edge's safe area coordinates			if( horDistance < vertDistance )			{				if( distToLeft < distToRight )					pos = new Vector2( halfSize.x, pos.y );				else					pos = new Vector2( canvasWidth - halfSize.x, pos.y );				pos.y = Mathf.Clamp( pos.y, halfSize.y, canvasHeight - halfSize.y );			}			else			{				if( distToBottom < distToTop )					pos = new Vector2( pos.x, halfSize.y );				else					pos = new Vector2( pos.x, canvasHeight - halfSize.y );				pos.x = Mathf.Clamp( pos.x, halfSize.x, canvasWidth - halfSize.x );			}			pos -= canvasRawSize * 0.5f;			normalizedPosition.Set( pos.x / canvasWidth, pos.y / canvasHeight );			// Safe area's bottom left coordinates are added to pos only after normalizedPosition's value			// is set because normalizedPosition is in range [-canvasWidth / 2, canvasWidth / 2]			pos += new Vector2( canvasBottomLeftX, canvasBottomLeftY );			// If another smooth movement animation is in progress, cancel it			if( moveToPosCoroutine != null )			{				StopCoroutine( moveToPosCoroutine );				moveToPosCoroutine = null;			}			if( immediately )				popupTransform.anchoredPosition = pos;			else			{				// Smoothly translate the popup to the specified position				moveToPosCoroutine = MoveToPosAnimation( pos );				StartCoroutine( moveToPosCoroutine );			}		}	}}
 |