Files
beyond/Assets/Scripts/Editor/TrinketUIGenerator.cs

315 lines
16 KiB
C#

#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using TMPro;
using Invector.vItemManager;
using System.Collections.Generic;
namespace Beyond
{
public class TrinketUIGenerator : MonoBehaviour
{
[MenuItem("Beyond/Generate Trinket UI")]
public static void CreateTrinketUI()
{
try
{
// 1. Create Dedicated Canvas
GameObject canvasObj = new GameObject("Trinket_UI_Canvas");
Canvas c = canvasObj.AddComponent<Canvas>();
c.renderMode = RenderMode.ScreenSpaceOverlay;
c.sortingOrder = 99;
CanvasScaler scaler = canvasObj.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
scaler.matchWidthOrHeight = 0.5f; // Balanced scaling
canvasObj.AddComponent<GraphicRaycaster>();
// Add Controller
TrinketUIController uiController = canvasObj.AddComponent<TrinketUIController>();
uiController.mainCanvas = c;
//uiController.trinketType = bItemType.Trinkets;
// 2. Main Panel Background
GameObject mainPanel = CreateUIObject("Main_Panel", canvasObj.transform);
Image panelImg = mainPanel.AddComponent<Image>();
panelImg.color = new Color(0.05f, 0.05f, 0.05f, 0.95f);
StretchToFill(mainPanel.GetComponent<RectTransform>());
// 3. Layout Container (The Parent of Left/Right)
GameObject contentContainer = CreateUIObject("Content_Container", mainPanel.transform);
StretchToFill(contentContainer.GetComponent<RectTransform>());
RectTransform containerRect = contentContainer.GetComponent<RectTransform>();
// Add margins so it doesn't touch screen edges
containerRect.offsetMin = new Vector2(100, 100);
containerRect.offsetMax = new Vector2(-100, -100);
HorizontalLayoutGroup hLayout = contentContainer.AddComponent<HorizontalLayoutGroup>();
hLayout.childControlWidth = true;
hLayout.childControlHeight = true;
hLayout.childForceExpandWidth = true; // FORCE 50/50 SPLIT
hLayout.childForceExpandHeight = true;
hLayout.spacing = 50;
// =========================================================
// LEFT SIDE: EQUIP AREA
// =========================================================
GameObject leftPanel = CreateUIObject("Left_EquipArea", contentContainer.transform);
leftPanel.AddComponent<Image>().color = new Color(1, 1, 1, 0.05f);
bEquipArea equipArea = leftPanel.AddComponent<bEquipArea>();
equipArea.equipSlots = new List<bEquipSlot>();
equipArea.itemTypes = new List<bItemType>() { bItemType.Trinkets };
// Vertical Layout for the Left Panel itself
VerticalLayoutGroup leftVL = leftPanel.AddComponent<VerticalLayoutGroup>();
leftVL.padding = new RectOffset(20, 20, 20, 20);
leftVL.spacing = 20;
leftVL.childControlHeight = true;
leftVL.childControlWidth = true;
leftVL.childForceExpandHeight = false;
// Title
TMP_Text titleTxt = CreateTMPObject("Equip_Title", leftPanel.transform, "Equipped Trinkets");
titleTxt.rectTransform.sizeDelta = new Vector2(0, 60); // Fixed height for title
// References (Hidden Text)
equipArea.displayNameText = CreateTextObject("EA_Name", "", leftPanel.transform);
equipArea.displayDescriptionText = CreateTextObject("EA_Desc", "", leftPanel.transform);
// Slot Container (Centered)
GameObject slotsContainer = CreateUIObject("Slots_Container", leftPanel.transform);
// Make slots container take up remaining space but center its children
LayoutElement slotsLE = slotsContainer.AddComponent<LayoutElement>();
slotsLE.flexibleHeight = 1;
HorizontalLayoutGroup slotsLayout = slotsContainer.AddComponent<HorizontalLayoutGroup>();
slotsLayout.childAlignment = TextAnchor.MiddleCenter;
slotsLayout.spacing = 40;
slotsLayout.childControlWidth = false; // Don't stretch slots
slotsLayout.childControlHeight = false;
// Create 3 Equip Slots
for (int i = 0; i < 3; i++)
{
GameObject slotObj = CreateUIObject($"EquipSlot_{i}", slotsContainer.transform);
Image slotImg = slotObj.AddComponent<Image>();
slotImg.color = new Color(0.2f, 0.2f, 0.2f, 1f);
slotObj.GetComponent<RectTransform>().sizeDelta = new Vector2(120, 120);
slotObj.AddComponent<CanvasGroup>();
bEquipSlot equipSlot = slotObj.AddComponent<bEquipSlot>();
equipSlot.itemType = new List<bItemType>() { bItemType.Trinkets };
GameObject iconObj = CreateUIObject("Icon", slotObj.transform);
StretchToFill(iconObj.GetComponent<RectTransform>());
Image iconImg = iconObj.AddComponent<Image>();
iconImg.raycastTarget = false;
iconImg.color = Color.clear; // Relies on bItemSlot to turn alpha back to 1
equipSlot.icon = iconImg;
equipArea.equipSlots.Add(equipSlot);
}
// =========================================================
// RIGHT SIDE: ITEM WINDOW
// =========================================================
GameObject rightPanel = CreateUIObject("Right_ItemWindow", contentContainer.transform);
rightPanel.AddComponent<Image>().color = new Color(1, 1, 1, 0.05f);
bItemWindow itemWindow = rightPanel.AddComponent<bItemWindow>();
// Vertical Layout for Right Panel
VerticalLayoutGroup rightVL = rightPanel.AddComponent<VerticalLayoutGroup>();
rightVL.padding = new RectOffset(20, 20, 20, 20);
rightVL.spacing = 20;
rightVL.childControlWidth = true;
rightVL.childControlHeight = true;
// 1. Scroll View (Takes up flexible space)
GameObject scrollView = CreateUIObject("Scroll_View", rightPanel.transform);
LayoutElement svLE = scrollView.AddComponent<LayoutElement>();
svLE.flexibleHeight = 1; // Take available space
ScrollRect scrollRect = scrollView.AddComponent<ScrollRect>();
GameObject viewport = CreateUIObject("Viewport", scrollView.transform);
StretchToFill(viewport.GetComponent<RectTransform>());
viewport.AddComponent<Mask>();
viewport.AddComponent<Image>().color = new Color(1,1,1,0.01f);
GameObject content = CreateUIObject("Content", viewport.transform);
RectTransform contentRT = content.GetComponent<RectTransform>();
contentRT.anchorMin = new Vector2(0, 1);
contentRT.anchorMax = new Vector2(1, 1);
contentRT.pivot = new Vector2(0.5f, 1);
GridLayoutGroup grid = content.AddComponent<GridLayoutGroup>();
grid.cellSize = new Vector2(100, 100);
grid.spacing = new Vector2(15, 15);
ContentSizeFitter csf = content.AddComponent<ContentSizeFitter>();
csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
scrollRect.content = contentRT;
scrollRect.viewport = viewport.GetComponent<RectTransform>();
itemWindow.contentWindow = contentRT;
// 2. Details Panel (Fixed Height at bottom)
GameObject detailsPanel = CreateUIObject("Details_Panel", rightPanel.transform);
LayoutElement detailsLE = detailsPanel.AddComponent<LayoutElement>();
detailsLE.minHeight = 300;
detailsLE.preferredHeight = 300;
VerticalLayoutGroup detailsLayout = detailsPanel.AddComponent<VerticalLayoutGroup>();
detailsLayout.spacing = 5;
detailsLayout.childControlHeight = true;
detailsLayout.childForceExpandHeight = false;
// Details Components (Auto-Sizing Text)
itemWindow.displayNameText = CreateTMPObject("Name_Text", detailsPanel.transform, "Item Name");
itemWindow.displayNameText.fontStyle = FontStyles.Bold;
itemWindow.displayTypeText = CreateTextObject("Type_Text", "Item Type", detailsPanel.transform);
itemWindow.displayAttributesText = CreateTextObject("Attributes_Text", "Stats...", detailsPanel.transform);
itemWindow.displayAmountText = CreateTextObject("Amount_Text", "", detailsPanel.transform);
// Description
GameObject descContainer = CreateUIObject("Desc_Container", detailsPanel.transform);
LayoutElement descLE = descContainer.AddComponent<LayoutElement>();
descLE.flexibleHeight = 1; // Description expands to fill gap
descContainer.AddComponent<VerticalLayoutGroup>();
itemWindow.displayDescriptionText = CreateTMPObject("Desc_Text", descContainer.transform, "Description...");
itemWindow.displayDescriptionText.alignment = TextAlignmentOptions.TopLeft;
itemWindow.descriptionTransform = descContainer.GetComponent<RectTransform>();
// Submit Button
GameObject btnObj = CreateUIObject("Submit_Button", detailsPanel.transform);
LayoutElement btnLE = btnObj.AddComponent<LayoutElement>();
btnLE.minHeight = 50;
btnLE.preferredHeight = 50;
btnObj.AddComponent<Image>().color = new Color(0, 0.6f, 0, 1);
Button submitBtn = btnObj.AddComponent<Button>();
TMP_Text btnText = CreateTMPObject("Btn_Text", btnObj.transform, "EQUIP");
StretchToFill(btnText.rectTransform);
// Serialization
SerializedObject so = new SerializedObject(itemWindow);
so.Update();
SerializedProperty propBtn = so.FindProperty("button");
if (propBtn != null && btnObj != null) propBtn.objectReferenceValue = btnObj;
SerializedProperty propBtnText = so.FindProperty("submitButtonText");
if (propBtnText != null && btnText != null) propBtnText.objectReferenceValue = btnText;
SerializedProperty propConsumeBtn = so.FindProperty("consumeButton");
if (propConsumeBtn != null && btnObj != null) propConsumeBtn.objectReferenceValue = btnObj;
// Need to assign Image for serialization, even if we don't use it in UI layout much
GameObject dummyImg = CreateUIObject("DummyImg", detailsPanel.transform);
dummyImg.SetActive(false); // Hide it
itemWindow.displayItemImage = dummyImg.AddComponent<Image>();
so.ApplyModifiedProperties();
// =========================================================
// TEMPLATE SLOT PREFAB
// =========================================================
GameObject slotTemplate = CreateUIObject("Slot_Template_Prefab", canvasObj.transform);
Image templateBg = slotTemplate.AddComponent<Image>();
templateBg.color = new Color(0.3f, 0.3f, 0.3f);
slotTemplate.AddComponent<CanvasGroup>();
bItemSlot itemSlotScript = slotTemplate.AddComponent<bItemSlot>();
GameObject tIcon = CreateUIObject("Icon", slotTemplate.transform);
StretchToFill(tIcon.GetComponent<RectTransform>());
itemSlotScript.icon = tIcon.AddComponent<Image>();
itemSlotScript.icon.raycastTarget = false;
itemSlotScript.icon.color = Color.clear;
GameObject tCheck = CreateUIObject("CheckIcon", slotTemplate.transform);
RectTransform checkRT = tCheck.GetComponent<RectTransform>();
checkRT.anchorMin = new Vector2(1,1);
checkRT.anchorMax = new Vector2(1,1);
checkRT.sizeDelta = new Vector2(25,25);
checkRT.anchoredPosition = new Vector2(-10, -10);
itemSlotScript.checkIcon = tCheck.AddComponent<Image>();
itemSlotScript.checkIcon.color = Color.green;
tCheck.SetActive(false);
itemWindow.slotPrefab = itemSlotScript;
slotTemplate.SetActive(false);
// Final Wiring
equipArea.itemPicker = itemWindow;
uiController.trinketEquipArea = equipArea;
uiController.trinketItemWindow = itemWindow;
c.enabled = false;
Selection.activeGameObject = canvasObj;
Debug.Log("Trinket UI (Layout Fixed) Created Successfully.");
}
catch (System.Exception ex)
{
Debug.LogError("Error generating UI: " + ex.Message + "\n" + ex.StackTrace);
}
}
// --- Helpers ---
private static GameObject CreateUIObject(string name, Transform parent)
{
GameObject go = new GameObject(name);
go.AddComponent<RectTransform>();
if(parent != null) go.transform.SetParent(parent, false);
return go;
}
private static TMP_Text CreateTMPObject(string name, Transform parent, string text)
{
GameObject go = CreateUIObject(name, parent);
TMP_Text tmp = go.AddComponent<TextMeshProUGUI>();
if (tmp != null)
{
tmp.text = text;
tmp.alignment = TextAlignmentOptions.Center;
tmp.color = Color.white;
// AUTO SIZING IS KEY for fixing your issue
tmp.enableAutoSizing = true;
tmp.fontSizeMin = 12;
tmp.fontSizeMax = 72;
}
return tmp;
}
private static Text CreateTextObject(string name, string text, Transform parent)
{
GameObject go = CreateUIObject(name, parent);
Text txt = go.AddComponent<Text>();
txt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
if(txt.font == null) txt.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
txt.text = text;
txt.alignment = TextAnchor.MiddleCenter;
txt.color = Color.white;
txt.resizeTextForBestFit = true; // Auto size for legacy text
txt.resizeTextMinSize = 10;
txt.resizeTextMaxSize = 60;
return txt;
}
private static void StretchToFill(RectTransform rt)
{
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = Vector2.zero;
rt.offsetMax = Vector2.zero;
}
}
}
#endif