using UnityEngine; using UnityEditor; using UnityEngine.UIElements; using UnityEditor.UIElements; namespace KindRetargeting { [CustomEditor(typeof(PropLocationController))] public class PropLocationControllerEditor : BaseRetargetingEditor { private const string CommonUssPath = "Assets/Scripts/Streamingle/StreamingleControl/Editor/UXML/StreamingleCommon.uss"; private VisualElement offsetContainer; private VisualElement propListContainer; private PropLocationController controller; public override VisualElement CreateInspectorGUI() { controller = target as PropLocationController; if (controller == null) return new VisualElement(); var root = new VisualElement(); var commonUss = AssetDatabase.LoadAssetAtPath(CommonUssPath); if (commonUss != null) root.styleSheets.Add(commonUss); // 기본 프로퍼티 var iterator = serializedObject.GetIterator(); iterator.NextVisible(true); // skip m_Script while (iterator.NextVisible(false)) { var field = new PropertyField(iterator.Copy()); root.Add(field); } // 오프셋 조정 섹션 root.Add(new Label("오프셋 조정") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginTop = 10 } }); offsetContainer = new VisualElement(); root.Add(offsetContainer); // 부착된 프랍 목록 섹션 root.Add(new Label("부착된 프랍 목록") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginTop = 10 } }); propListContainer = new VisualElement(); root.Add(propListContainer); // 프랍 위치 이동 버튼 root.Add(new Label("프랍 위치 이동") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginTop = 10 } }); var btnRow = new VisualElement { style = { flexDirection = FlexDirection.Row, marginTop = 4 } }; var headBtn = new Button(() => controller.MoveToHead()) { text = "머리로 이동" }; headBtn.style.flexGrow = 1; headBtn.style.height = 30; headBtn.style.marginRight = 2; btnRow.Add(headBtn); var leftBtn = new Button(() => controller.MoveToLeftHand()) { text = "왼손으로 이동" }; leftBtn.style.flexGrow = 1; leftBtn.style.height = 30; leftBtn.style.marginRight = 2; btnRow.Add(leftBtn); var rightBtn = new Button(() => controller.MoveToRightHand()) { text = "오른손으로 이동" }; rightBtn.style.flexGrow = 1; rightBtn.style.height = 30; btnRow.Add(rightBtn); root.Add(btnRow); var detachBtn = new Button(() => { if (Selection.activeGameObject != null) Undo.RecordObject(Selection.activeGameObject.transform, "Detach Prop"); controller.DetachProp(); }) { text = "프랍 해제" }; detachBtn.style.height = 30; detachBtn.style.marginTop = 4; root.Add(detachBtn); // 주기적으로 동적 UI 갱신 root.schedule.Execute(() => RebuildDynamicUI()).Every(500); RebuildDynamicUI(); return root; } private void RebuildDynamicUI() { if (controller == null) return; RebuildOffsets(); RebuildPropLists(); } private void RebuildOffsets() { if (offsetContainer == null) return; offsetContainer.Clear(); BuildOffsetSection(offsetContainer, "왼손 오프셋", controller.GetLeftHandOffset()); BuildOffsetSection(offsetContainer, "오른손 오프셋", controller.GetRightHandOffset()); BuildOffsetSection(offsetContainer, "머리 오프셋", controller.GetHeadOffset()); } private void BuildOffsetSection(VisualElement parent, string label, Transform offset) { if (offset == null) return; var foldout = new Foldout { text = label, value = true }; var posField = new Vector3Field("위치") { value = offset.localPosition }; posField.RegisterValueChangedCallback(evt => { Undo.RecordObject(offset, $"Update {label}"); offset.localPosition = evt.newValue; EditorUtility.SetDirty(offset); }); foldout.Add(posField); var rotField = new Vector3Field("회전") { value = offset.localRotation.eulerAngles }; rotField.RegisterValueChangedCallback(evt => { Undo.RecordObject(offset, $"Update {label}"); offset.localRotation = Quaternion.Euler(evt.newValue); EditorUtility.SetDirty(offset); }); foldout.Add(rotField); parent.Add(foldout); } private void RebuildPropLists() { if (propListContainer == null) return; propListContainer.Clear(); BuildPropListSection(propListContainer, "머리 프랍", controller.GetHeadProps()); BuildPropListSection(propListContainer, "왼손 프랍", controller.GetLeftHandProps()); BuildPropListSection(propListContainer, "오른손 프랍", controller.GetRightHandProps()); } private void BuildPropListSection(VisualElement parent, string title, GameObject[] propList) { var box = new VisualElement(); box.style.backgroundColor = new Color(0, 0, 0, 0.1f); box.style.borderTopLeftRadius = box.style.borderTopRightRadius = box.style.borderBottomLeftRadius = box.style.borderBottomRightRadius = 4; box.style.paddingTop = box.style.paddingBottom = box.style.paddingLeft = box.style.paddingRight = 4; box.style.marginBottom = 4; box.Add(new Label(title) { style = { unityFontStyleAndWeight = FontStyle.Bold } }); if (propList.Length > 0) { foreach (var prop in propList) { if (prop == null) continue; var row = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, marginTop = 2 } }; row.Add(new Label(prop.name) { style = { flexGrow = 1 } }); var selectBtn = new Button(() => Selection.activeGameObject = prop) { text = "선택" }; selectBtn.style.width = 60; row.Add(selectBtn); box.Add(row); } } else { box.Add(new Label("부착된 프랍 없음") { style = { color = new Color(0.6f, 0.6f, 0.6f) } }); } parent.Add(box); } } }