using UnityEngine; using UnityEditor; using UnityEngine.UIElements; using UnityEditor.UIElements; [CustomEditor(typeof(ItemController))] public class ItemControllerEditor : Editor { private const string UxmlPath = "Assets/Scripts/Streamingle/StreamingleControl/Editor/UXML/ItemControllerEditor.uxml"; private const string CommonUssPath = "Assets/Scripts/Streamingle/StreamingleControl/Editor/UXML/StreamingleCommon.uss"; private VisualElement itemsContainer; private Label itemsTitleLabel; private ItemController controller; public override VisualElement CreateInspectorGUI() { controller = (ItemController)target; var root = new VisualElement(); var commonUss = AssetDatabase.LoadAssetAtPath(CommonUssPath); if (commonUss != null) root.styleSheets.Add(commonUss); var uxml = AssetDatabase.LoadAssetAtPath(UxmlPath); if (uxml != null) uxml.CloneTree(root); BuildItemGroupsSection(root); root.schedule.Execute(UpdatePlayModeState).Every(200); Undo.undoRedoPerformed += OnUndoRedo; root.RegisterCallback(_ => Undo.undoRedoPerformed -= OnUndoRedo); return root; } private void OnUndoRedo() { if (controller == null) return; serializedObject.Update(); RebuildItemList(); } #region Item Groups List private void BuildItemGroupsSection(VisualElement root) { var section = root.Q("itemGroupsSection"); if (section == null) return; var header = new VisualElement(); header.AddToClassList("list-header"); itemsTitleLabel = new Label($"Item Groups ({controller.itemGroups.Count})"); itemsTitleLabel.AddToClassList("list-title"); header.Add(itemsTitleLabel); var addBtn = new Button(AddItemGroup) { text = "+ 그룹 추가" }; addBtn.AddToClassList("list-add-btn"); header.Add(addBtn); section.Add(header); itemsContainer = new VisualElement(); section.Add(itemsContainer); RebuildItemList(); } private void RebuildItemList() { if (itemsContainer == null || controller == null) return; itemsContainer.Clear(); if (itemsTitleLabel != null) itemsTitleLabel.text = $"Item Groups ({controller.itemGroups.Count})"; if (controller.itemGroups.Count == 0) { var empty = new Label("아이템 그룹이 없습니다. '+ 그룹 추가' 버튼을 눌러 추가하세요."); empty.AddToClassList("list-empty"); itemsContainer.Add(empty); return; } for (int i = 0; i < controller.itemGroups.Count; i++) { itemsContainer.Add(CreateItemGroupElement(i)); } } private VisualElement CreateItemGroupElement(int index) { var group = controller.itemGroups[index]; bool isActive = Application.isPlaying && group.IsActive(); var item = new VisualElement(); item.AddToClassList("list-item"); if (isActive) item.AddToClassList("list-item--active"); // Header row var headerRow = new VisualElement(); headerRow.AddToClassList("list-item-header"); var indexLabel = new Label($"{index + 1}"); indexLabel.AddToClassList("list-index"); headerRow.Add(indexLabel); var nameField = new TextField(); nameField.value = group.groupName; nameField.AddToClassList("list-name-field"); int idx = index; nameField.RegisterValueChangedCallback(evt => { Undo.RecordObject(target, "Rename Item Group"); controller.itemGroups[idx].groupName = evt.newValue; EditorUtility.SetDirty(target); }); headerRow.Add(nameField); if (isActive) { var activeLabel = new Label("[Active]"); activeLabel.AddToClassList("list-active-label"); headerRow.Add(activeLabel); } // Reorder buttons var upBtn = new Button(() => SwapItems(idx, idx - 1)) { text = "\u25B2" }; upBtn.AddToClassList("list-reorder-btn"); upBtn.SetEnabled(index > 0); headerRow.Add(upBtn); var downBtn = new Button(() => SwapItems(idx, idx + 1)) { text = "\u25BC" }; downBtn.AddToClassList("list-reorder-btn"); downBtn.SetEnabled(index < controller.itemGroups.Count - 1); headerRow.Add(downBtn); var deleteBtn = new Button(() => DeleteItem(idx)) { text = "X" }; deleteBtn.AddToClassList("list-delete-btn"); headerRow.Add(deleteBtn); item.Add(headerRow); // Fields var fields = new VisualElement(); fields.AddToClassList("list-fields"); var listProp = serializedObject.FindProperty("itemGroups"); var elementProp = listProp.GetArrayElementAtIndex(index); var objectsField = new PropertyField(elementProp.FindPropertyRelative("itemObjects"), "Objects"); fields.Add(objectsField); item.Add(fields); return item; } private void AddItemGroup() { Undo.RecordObject(target, "Add Item Group"); controller.itemGroups.Add(new ItemController.ItemGroup("New Item Group")); EditorUtility.SetDirty(target); serializedObject.Update(); RebuildItemList(); } private void DeleteItem(int index) { var group = controller.itemGroups[index]; if (EditorUtility.DisplayDialog("그룹 삭제", $"그룹 '{group.groupName}'을(를) 삭제하시겠습니까?", "삭제", "취소")) { Undo.RecordObject(target, "Delete Item Group"); controller.itemGroups.RemoveAt(index); EditorUtility.SetDirty(target); serializedObject.Update(); RebuildItemList(); } } private void SwapItems(int a, int b) { Undo.RecordObject(target, "Reorder Item Groups"); (controller.itemGroups[a], controller.itemGroups[b]) = (controller.itemGroups[b], controller.itemGroups[a]); EditorUtility.SetDirty(target); serializedObject.Update(); RebuildItemList(); } #endregion #region Play Mode State private void UpdatePlayModeState() { if (itemsContainer == null || controller == null) return; if (!Application.isPlaying) return; for (int i = 0; i < itemsContainer.childCount && i < controller.itemGroups.Count; i++) { var item = itemsContainer[i]; bool isActive = controller.itemGroups[i].IsActive(); if (isActive && !item.ClassListContains("list-item--active")) item.AddToClassList("list-item--active"); else if (!isActive && item.ClassListContains("list-item--active")) item.RemoveFromClassList("list-item--active"); } } #endregion }