using System; using System.Text; using ADOFAIRenderer.Core; using TMPro; using UnityEngine; using UnityEngine.UI; namespace ADOFAIRenderer.UI { public class DebugMonitorUI : MonoBehaviour { // --- Progress Bar UI Configuration Constants --- private const float BarWidth = 800f; private const float BarHeight = 28f; private const float BarPosX = 0f; private const float BarPosY = -420f; // Y position relative to screen center (Reference: 1920x1080) private const int BarTextFontSize = 16; // 눈에 띄게 증가된 텍스트 크기 private static readonly Color BarBackgroundColor = new(0.08f, 0.08f, 0.1f, 1.0f); private static readonly Color BarFillColor = new(0.0f, 0.8f, 1.0f, 1.0f); // Sleek cyan/blue private static readonly Color BarTextColor = new(1.0f, 1.0f, 1.0f, 1.0f); // --- Progress Bar Outline Configuration Constants --- private static readonly Color BarOutlineColor = Color.white; // 대비되는 흰색 윤곽선 private static readonly Vector2 BarOutlineDistance = new(3.0f, -3.0f); // 3px의 굵고 직관적인 윤곽선 두께 private TextMeshProUGUI _text = null!; private GameObject _progressBarObj = null!; private GameObject _progressBarFillObj = null!; private RectTransform _fillRect = null!; private TextMeshProUGUI _progressText = null!; private static TMP_FontAsset GetNanumSquareNeoBdFontAsset() { TMP_FontAsset? font = null; if (RenderMod.FontBundle != null) { try { font = RenderMod.FontBundle.LoadAsset("NanumSquareNeoOTF-Bd SDF"); } catch { // 로딩 실패 } } // 폴백 처리: 에셋 번들에서 실패 시 게임 자체 NanumSquareNeo 또는 내장 TMP SDF 폰트 사용 if (font == null) { font = RDString.fontData.fontTMP; } if (font == null) { // ReSharper disable once Unity.UnknownResource font = Resources.Load("Fonts & Materials/LiberationSans SDF"); } if (font != null) return font; var fonts = Resources.FindObjectsOfTypeAll(); if (fonts.Length > 0) font = fonts[0]; return font!; } private void Start() { // UI 레이어(5) 설정 gameObject.layer = 5; // Canvas 설정 var canvas = gameObject.AddComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.sortingOrder = 9999; // 해상도 다이내믹 스케일링 설정 (Reference: 1920x1080) var scaler = gameObject.AddComponent(); scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; scaler.referenceResolution = new Vector2(1920, 1080); scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; scaler.matchWidthOrHeight = 0.5f; gameObject.AddComponent(); // 배경 패널 생성 var panelObj = new GameObject("BackgroundPanel"); panelObj.transform.SetParent(transform, false); panelObj.layer = 5; var image = panelObj.AddComponent(); image.color = new Color(0.08f, 0.08f, 0.1f, 0.88f); // 약간 더 고급스러운 다크톤 아크릴 감성 var panelRect = panelObj.GetComponent(); panelRect.anchorMin = new Vector2(1, 1); panelRect.anchorMax = new Vector2(1, 1); panelRect.pivot = new Vector2(1, 1); panelRect.anchoredPosition = new Vector2(-30, -30); panelRect.sizeDelta = new Vector2(340, 250); // 텍스트 생성 var textObj = new GameObject("MonitorText"); textObj.transform.SetParent(panelObj.transform, false); textObj.layer = 5; _text = textObj.AddComponent(); _text.richText = true; _text.fontSize = 16; // 13에서 16으로 증가 _text.alignment = TextAlignmentOptions.TopLeft; // TMPro 텍스트 잘림 현상 예방 _text.textWrappingMode = TextWrappingModes.Normal; _text.overflowMode = TextOverflowModes.Overflow; // NanumSquareNeo-Bd SDF 폰트 로드 및 적용 var font = GetNanumSquareNeoBdFontAsset(); _text.font = font; var textRect = textObj.GetComponent(); textRect.anchorMin = Vector2.zero; textRect.anchorMax = Vector2.one; textRect.offsetMin = new Vector2(20, 20); // 좀 더 넉넉하고 패딩 여백 textRect.offsetMax = new Vector2(-20, -20); // --- Progress Bar UI Setup --- // 1. Progress Bar Container _progressBarObj = new GameObject("ProgressBar"); _progressBarObj.transform.SetParent(transform, false); _progressBarObj.layer = 5; var bgImage = _progressBarObj.AddComponent(); bgImage.color = BarBackgroundColor; // 프로그레스 바 흰색 윤곽선 컴포넌트 추가 var bgOutline = _progressBarObj.AddComponent(); bgOutline.effectColor = BarOutlineColor; bgOutline.effectDistance = BarOutlineDistance; var barRect = _progressBarObj.GetComponent(); barRect.anchorMin = new Vector2(0.5f, 0.5f); barRect.anchorMax = new Vector2(0.5f, 0.5f); barRect.pivot = new Vector2(0.5f, 0.5f); barRect.anchoredPosition = new Vector2(BarPosX, BarPosY); barRect.sizeDelta = new Vector2(BarWidth, BarHeight); // 2. Progress Bar Fill _progressBarFillObj = new GameObject("ProgressBarFill"); _progressBarFillObj.transform.SetParent(_progressBarObj.transform, false); _progressBarFillObj.layer = 5; var fillImage = _progressBarFillObj.AddComponent(); fillImage.color = BarFillColor; _fillRect = _progressBarFillObj.GetComponent(); _fillRect.anchorMin = new Vector2(0, 0); _fillRect.anchorMax = new Vector2(0, 1); _fillRect.pivot = new Vector2(0, 0.5f); _fillRect.anchoredPosition = Vector2.zero; _fillRect.sizeDelta = new Vector2(0, 0); // width will be updated dynamically in Update() // 3. Progress Bar Text var progressTextObj = new GameObject("ProgressBarText"); progressTextObj.transform.SetParent(_progressBarObj.transform, false); progressTextObj.layer = 5; _progressText = progressTextObj.AddComponent(); _progressText.richText = true; _progressText.fontSize = BarTextFontSize; // 14에서 18로 증가 _progressText.alignment = TextAlignmentOptions.Center; _progressText.font = font; _progressText.color = BarTextColor; // 프로그레스 바 내부 텍스트 아웃라인 추가 (진행율 가독성 확보) var textOutline = progressTextObj.AddComponent(); textOutline.effectColor = new Color(0f, 0f, 0f, 0.9f); textOutline.effectDistance = new Vector2(1.5f, -1.5f); var textRectBar = progressTextObj.GetComponent(); textRectBar.anchorMin = Vector2.zero; textRectBar.anchorMax = Vector2.one; textRectBar.offsetMin = Vector2.zero; textRectBar.offsetMax = Vector2.zero; } private void Update() { if (!_text) return; var sb = new StringBuilder(); sb.AppendLine("ADOFAI Renderer Realtime Monitor"); sb.AppendLine(); sb.AppendLine($"• Rendered Progress: {(RenderManager.Instance ? RenderManager.Instance.frameCount : 0)} frames"); var queueCount = 0; if (RenderManager.Instance) { lock (RenderManager.Instance.ImageQueue) { queueCount = RenderManager.Instance.ImageQueue.Count; } } sb.AppendLine($"• Queued Buffers: {queueCount} / {RenderManager.MaxQueuedFrames} frames"); sb.AppendLine(RenderManager.Instance && RenderManager.Instance.isThrottling ? "• Status: THROTTLING (Safe Mode)" : "• Status: NORMAL (Encoding)"); sb.AppendLine(); sb.AppendLine($"• Throttling Controls: {(RenderManager.Instance ? RenderManager.Instance.throttleCount : 0)} times"); sb.AppendLine($"• Readback Hard Failures: {(RenderManager.Instance ? RenderManager.Instance.readbackFailureCount : 0)} times"); sb.AppendLine($"• Render Skip-Retry: {(RenderManager.Instance ? RenderManager.Instance.skipCount : 0)} / {RenderManager.MaxRenderRetry}"); var memoryMb = GC.GetTotalMemory(false) / (1024.0 * 1024.0); sb.AppendLine($"• Mono Heap Memory: {memoryMb:F2} MB"); _text.text = sb.ToString(); // Update Progress Bar var progress = 0f; var currentFloor = 0; var totalFloors = 0; if (scrController.instance && ADOBase.lm && ADOBase.lm.listFloors != null) { currentFloor = scrController.instance.currFloor ? scrController.instance.currFloor.seqID : 0; totalFloors = ADOBase.lm.listFloors.Count - 1; if (totalFloors > 0) { progress = Mathf.Clamp01((float)currentFloor / totalFloors); } } if (_fillRect) _fillRect.sizeDelta = new Vector2(BarWidth * progress, 0); if (_progressText) _progressText.text = $"{progress * 100f:F1}% ({currentFloor}/{totalFloors})"; } } }