using System.Collections.Generic; using System.Numerics; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using VS.Sim; using Vector2 = UnityEngine.Vector2; using Vector3 = UnityEngine.Vector3; // 메인 화면 — 원작 GamePlayUI 배치(1080×1920): 상단 좌 프로필 · 상단 우 코인/초당/티켓 + 세로 버튼 · 좌 하이프 막대 · 하단 채팅 · 후원 받기 · 하단 5버튼 namespace VS.View { public partial class GameApp { RectTransform stage, roomRect; Image roomBg, idolImg, idolShadow; readonly Image[] props = new Image[11]; readonly int[] propStage = new int[11]; Text coinText, gpsText, ticketText, nameText, rankText, followerText, viewText, donateAmount, hypeLabel, boostText; Image followerFill, hypeFill; RectTransform hypeBar, chatContent, verticalButtons; Button donateBtn, newHouseBtn, afkBtn; Image donateDot; GameObject hypeGlow; int idolFrame; float idolTimer, blinkTimer, poseTimer; // 방 소품 자리 (방 영역 정규 좌표 x, y, 크기) — 클론 배치(원작 아트 좌표 아님) static readonly float[,] Slot = { { 0.20f, 0.20f, 0.30f }, { 0.82f, 0.20f, 0.27f }, { 0.13f, 0.56f, 0.24f }, { 0.33f, 0.13f, 0.17f }, { 0.64f, 0.30f, 0.32f }, { 0.66f, 0.47f, 0.20f }, { 0.88f, 0.45f, 0.19f }, { 0.50f, 0.06f, 0.48f }, { 0.50f, 0.88f, 0.20f }, { 0.42f, 0.22f, 0.14f }, { 0f, 0f, 0f }, }; void BuildHud() { // 무대(방 + 스트리머) — 탭 영역 StreamRoot = UI.Rect("Stream", Root).Fill(0, TabH, 0, 0); var tapArea = UI.Img(StreamRoot, "Stage", Art.Square, new Color(0, 0, 0, 0), true); stage = tapArea.rectTransform.Fill(0, 0, 0, 0); var et = tapArea.gameObject.AddComponent(); var down = new EventTrigger.Entry { eventID = EventTriggerType.PointerDown }; down.callback.AddListener(e => OnStagePointerDown(((PointerEventData)e).position)); et.triggers.Add(down); var up = new EventTrigger.Entry { eventID = EventTriggerType.PointerUp }; up.callback.AddListener(e => OnStagePointerUp()); et.triggers.Add(up); roomBg = UI.Img(stage, "Room", null, Color.white); roomRect = roomBg.rectTransform.Place(new Vector2(0, 0), new Vector2(1, 1), Vector2.zero, Vector2.zero); roomRect.offsetMin = new Vector2(0, 560); roomRect.offsetMax = new Vector2(0, 0); for (int i = 0; i < 10; i++) { var im = UI.Img(roomRect, "Prop" + i, null, Color.white); im.preserveAspect = true; im.rectTransform.anchorMin = im.rectTransform.anchorMax = new Vector2(Slot[i, 0], Slot[i, 1]); im.rectTransform.sizeDelta = new Vector2(1080 * Slot[i, 2], 1080 * Slot[i, 2]); props[i] = im; } idolShadow = UI.Img(roomRect, "IdolShadow", Art.SoftCircle, new Color(0, 0, 0, 0.25f)); idolShadow.rectTransform.Place(new Vector2(0.5f, 0.1f), new Vector2(0.5f, 0.1f), Vector2.zero, new Vector2(420, 90)); idolImg = UI.Img(roomRect, "Idol", null, Color.white); idolImg.preserveAspect = true; idolImg.rectTransform.Place(new Vector2(0.5f, 0.1f), new Vector2(0.5f, 0.1f), new Vector2(0, 270), new Vector2(460, 644)); hypeGlow = UI.Img(roomRect, "HypeGlow", Art.SoftCircle, new Color(1f, 0.55f, 0.85f, 0.35f)).gameObject; ((RectTransform)hypeGlow.transform).Place(new Vector2(0.5f, 0.1f), new Vector2(0.5f, 0.1f), new Vector2(0, 330), new Vector2(900, 900)); hypeGlow.transform.SetSiblingIndex(idolImg.transform.GetSiblingIndex()); hypeGlow.SetActive(false); var hud = UI.Rect("Hud", StreamRoot).Fill(); BuildTopLeft(hud); BuildTopRight(hud); BuildHypeBar(hud); BuildBottom(hud); BuildBattleScreen(); BuildTabs(); FxLayer = UI.Rect("Fx", Root).Fill(); PopupLayer = UI.Rect("Popups", Root).Fill(); RebuildStage(); } void BuildTopLeft(RectTransform hud) { var card = UI.Img(hud, "StreamerProfile", Skin.Round, new Color(0.17f, 0.19f, 0.32f, 0.92f)); card.rectTransform.Place(new Vector2(0, 1), new Vector2(0, 1), new Vector2(36, -40), new Vector2(560, 210), new Vector2(0, 1)); var av = UI.Img(card.transform, "Avatar", Skin.ProfileFrame, Pal.Pink); av.rectTransform.Place(new Vector2(0, 0.5f), new Vector2(0, 0.5f), new Vector2(105, 0), new Vector2(170, 170)); var avIn = UI.Img(av.transform, "Face", Skin.ProfileInner, Color.white); avIn.rectTransform.Fill(12, 12, 12, 12); var mask = avIn.gameObject.AddComponent(); mask.showMaskGraphic = true; var face = UI.Img(avIn.transform, "IdolFace", null, Color.white); face.preserveAspect = true; face.rectTransform.Place(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0, -150), new Vector2(330, 462)); avatarFace = face; nameText = UI.Txt(card.transform, "Name", "", 42, Color.white, TextAnchor.MiddleLeft, true); nameText.rectTransform.Place(new Vector2(0, 1), new Vector2(1, 1), new Vector2(206, -40), new Vector2(-230, 56), new Vector2(0, 0.5f)); var rk = UI.Img(card.transform, "Rank", Skin.S("Label_Parallelogram_Purple") ?? Skin.Pill, Skin.On ? Color.white : Pal.Purple); rk.rectTransform.Place(new Vector2(0, 0.5f), new Vector2(0, 0.5f), new Vector2(206, 4), new Vector2(200, 50), new Vector2(0, 0.5f)); rankText = UI.Txt(rk.transform, "T", "", 30, Color.white); rankText.rectTransform.Fill(6, 2, 6, 2); var bar = UI.Bar(card.transform, "BarFollower", new Color32(230, 232, 246, 255), Pal.Pink, out followerFill); bar.rectTransform.Place(new Vector2(0, 0), new Vector2(1, 0), new Vector2(206, 40), new Vector2(-236, 40), new Vector2(0, 0.5f)); followerText = UI.Txt(bar.transform, "T", "", 24, Color.white, TextAnchor.MiddleCenter, true); followerText.rectTransform.Fill(4, 0, 4, 0); // FrameView (팔로워 아이콘 + 수) var fv = UI.Img(hud, "FrameView", Skin.Counter, Skin.On ? Skin.CounterTint : new Color(0.15f, 0.1f, 0.25f, 0.65f)); fv.rectTransform.Place(new Vector2(0, 1), new Vector2(0, 1), new Vector2(36, -266), new Vector2(300, 70), new Vector2(0, 1)); var fvIcon = UI.Icon(fv.transform, "Icon", Skin.Icon("view"), Color.white); fvIcon.rectTransform.Place(new Vector2(0, 0.5f), new Vector2(0, 0.5f), new Vector2(36, 0), new Vector2(64, 64)); viewText = UI.Txt(fv.transform, "T", "", 36, Color.white, TextAnchor.MiddleLeft); viewText.rectTransform.Fill(80, 0, 12, 0); boostText = UI.Txt(hud, "Boosts", "", 30, Color.white, TextAnchor.UpperLeft, true); boostText.rectTransform.Place(new Vector2(0, 1), new Vector2(0, 1), new Vector2(40, -350), new Vector2(600, 170), new Vector2(0, 1)); } Image avatarFace; Text MakeCounter(RectTransform hud, string name, string icon, float y, out Image frame) { frame = UI.Img(hud, name, Skin.Counter, Skin.On ? Skin.CounterTint : new Color(0.15f, 0.1f, 0.25f, 0.7f)); frame.rectTransform.Place(new Vector2(1, 1), new Vector2(1, 1), new Vector2(-190, y), new Vector2(330, 80), new Vector2(1, 1)); var ic = UI.Icon(frame.transform, "Icon", Skin.Icon(icon), Color.white); ic.rectTransform.Place(new Vector2(0, 0.5f), new Vector2(0, 0.5f), new Vector2(30, 0), new Vector2(84, 84)); var t = UI.Txt(frame.transform, "T", "0", 40, Color.white, TextAnchor.MiddleRight); t.rectTransform.Fill(80, 0, 22, 0); return t; } void BuildTopRight(RectTransform hud) { coinText = MakeCounter(hud, "FrameCoin", "coin", -40, out _); gpsText = MakeCounter(hud, "FrameCoin/s", "coin", -130, out _); ticketText = MakeCounter(hud, "FrameTicket", "ticket", -220, out var tf); var tb = tf.gameObject.AddComponent