- BackgroundSceneLoaderWindow: OnGUI → CreateGUI (Toolbar + ToolbarSearchField) - PropBrowserWindow: OnGUI → CreateGUI (Toolbar + ToolbarSearchField) - StreamingleCommon.uss: 브라우저 공통 스타일 추가 (그리드/리스트/뷰토글/액션바/상태바) - excludeFromWeb 상태 새로고침 시 보존 수정 - 삭제된 배경 리소스 정리 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
186 lines
8.8 KiB
C#
186 lines
8.8 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEngine.UIElements;
|
|
using UnityEditor.UIElements;
|
|
|
|
namespace KindRetargeting
|
|
{
|
|
[CustomEditor(typeof(LimbWeightController))]
|
|
public class LimbWeightControllerEditor : BaseRetargetingEditor
|
|
{
|
|
private const string CommonUssPath = "Assets/Scripts/Streamingle/StreamingleControl/Editor/UXML/StreamingleCommon.uss";
|
|
|
|
SerializedProperty maxDistance;
|
|
SerializedProperty minDistance;
|
|
SerializedProperty weightSmoothSpeed;
|
|
SerializedProperty middleWeightMultiplier;
|
|
SerializedProperty hipsMinDistance;
|
|
SerializedProperty hipsMaxDistance;
|
|
SerializedProperty props;
|
|
SerializedProperty characterRoot;
|
|
SerializedProperty groundHipsMinHeight;
|
|
SerializedProperty groundHipsMaxHeight;
|
|
SerializedProperty footHeightMinThreshold;
|
|
SerializedProperty footHeightMaxThreshold;
|
|
SerializedProperty enableLeftArmIK;
|
|
SerializedProperty enableRightArmIK;
|
|
SerializedProperty chairSeatHeightOffset;
|
|
|
|
protected override void OnEnable()
|
|
{
|
|
base.OnEnable();
|
|
if (serializedObject == null) return;
|
|
|
|
maxDistance = serializedObject.FindProperty("maxDistance");
|
|
minDistance = serializedObject.FindProperty("minDistance");
|
|
weightSmoothSpeed = serializedObject.FindProperty("weightSmoothSpeed");
|
|
middleWeightMultiplier = serializedObject.FindProperty("middleWeightMultiplier");
|
|
hipsMinDistance = serializedObject.FindProperty("hipsMinDistance");
|
|
hipsMaxDistance = serializedObject.FindProperty("hipsMaxDistance");
|
|
props = serializedObject.FindProperty("props");
|
|
characterRoot = serializedObject.FindProperty("characterRoot");
|
|
groundHipsMinHeight = serializedObject.FindProperty("groundHipsMinHeight");
|
|
groundHipsMaxHeight = serializedObject.FindProperty("groundHipsMaxHeight");
|
|
footHeightMinThreshold = serializedObject.FindProperty("footHeightMinThreshold");
|
|
footHeightMaxThreshold = serializedObject.FindProperty("footHeightMaxThreshold");
|
|
enableLeftArmIK = serializedObject.FindProperty("enableLeftArmIK");
|
|
enableRightArmIK = serializedObject.FindProperty("enableRightArmIK");
|
|
chairSeatHeightOffset = serializedObject.FindProperty("chairSeatHeightOffset");
|
|
}
|
|
|
|
public override VisualElement CreateInspectorGUI()
|
|
{
|
|
if (serializedObject == null || target == null)
|
|
return new VisualElement();
|
|
|
|
var root = new VisualElement();
|
|
|
|
var commonUss = AssetDatabase.LoadAssetAtPath<StyleSheet>(CommonUssPath);
|
|
if (commonUss != null) root.styleSheets.Add(commonUss);
|
|
|
|
// IK 활성화 설정
|
|
var ikFoldout = new Foldout { text = "IK 활성화 설정", value = true };
|
|
ikFoldout.Add(new PropertyField(enableLeftArmIK, "왼팔 IK 활성화"));
|
|
ikFoldout.Add(new PropertyField(enableRightArmIK, "오른팔 IK 활성화"));
|
|
root.Add(ikFoldout);
|
|
|
|
// 거리 기반 가중치 설정
|
|
var distFoldout = new Foldout { text = "거리 기반 가중치 설정", value = true };
|
|
distFoldout.Add(BuildMinMaxRange("거리 범위 (가중치 1 → 0)", minDistance, maxDistance, 0f, 1f));
|
|
root.Add(distFoldout);
|
|
|
|
// 가중치 변화 설정
|
|
var weightFoldout = new Foldout { text = "가중치 변화 설정", value = true };
|
|
weightFoldout.Add(new PropertyField(weightSmoothSpeed, "가중치 변화 속도"));
|
|
root.Add(weightFoldout);
|
|
|
|
// 허리 가중치 설정
|
|
var hipsFoldout = new Foldout { text = "허리 가중치 설정", value = true };
|
|
hipsFoldout.Add(BuildMinMaxRange("허리 거리 범위 (가중치 1 → 0)", hipsMinDistance, hipsMaxDistance, 0f, 1f));
|
|
root.Add(hipsFoldout);
|
|
|
|
// 바닥 기준 히프 보정 설정
|
|
var groundFoldout = new Foldout { text = "바닥 기준 히프 보정 설정", value = true };
|
|
groundFoldout.Add(BuildMinMaxRange("바닥 기준 히프 높이 범위 (가중치 0 → 1)", groundHipsMinHeight, groundHipsMaxHeight, 0f, 2f));
|
|
root.Add(groundFoldout);
|
|
|
|
// 발 높이 기반 가중치 설정
|
|
var footFoldout = new Foldout { text = "발 높이 기반 가중치 설정", value = true };
|
|
footFoldout.Add(BuildMinMaxRange("발 높이 범위 (가중치 1 → 0)", footHeightMinThreshold, footHeightMaxThreshold, 0.1f, 1f));
|
|
root.Add(footFoldout);
|
|
|
|
// 의자 앉기 높이 설정
|
|
var chairFoldout = new Foldout { text = "의자 앉기 높이 설정", value = true };
|
|
chairFoldout.Add(new PropertyField(chairSeatHeightOffset, "좌석 높이 오프셋"));
|
|
root.Add(chairFoldout);
|
|
|
|
// 참조 설정
|
|
var refFoldout = new Foldout { text = "참조 설정", value = true };
|
|
refFoldout.Add(new PropertyField(props, "프랍 오브젝트"));
|
|
refFoldout.Add(new PropertyField(characterRoot, "캐릭터 루트"));
|
|
root.Add(refFoldout);
|
|
|
|
// 변경 감지
|
|
root.TrackSerializedObjectValue(serializedObject, so =>
|
|
{
|
|
if (target == null) return;
|
|
MarkDirty();
|
|
var script = (LimbWeightController)target;
|
|
var retargeting = script.GetComponent<CustomRetargetingScript>();
|
|
if (retargeting != null)
|
|
EditorUtility.SetDirty(retargeting);
|
|
});
|
|
|
|
return root;
|
|
}
|
|
|
|
private VisualElement BuildMinMaxRange(string label, SerializedProperty minProp, SerializedProperty maxProp, float limitMin, float limitMax)
|
|
{
|
|
var container = new VisualElement { style = { marginBottom = 4 } };
|
|
if (minProp == null || maxProp == null)
|
|
{
|
|
container.Add(new HelpBox($"'{label}' 프로퍼티를 찾을 수 없습니다.", HelpBoxMessageType.Warning));
|
|
return container;
|
|
}
|
|
container.Add(new Label(label) { style = { marginBottom = 2 } });
|
|
|
|
var row = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center } };
|
|
|
|
var minField = new FloatField { value = minProp.floatValue, style = { width = 50 } };
|
|
var slider = new MinMaxSlider(minProp.floatValue, maxProp.floatValue, limitMin, limitMax);
|
|
slider.style.flexGrow = 1;
|
|
slider.style.marginLeft = slider.style.marginRight = 4;
|
|
var maxField = new FloatField { value = maxProp.floatValue, style = { width = 50 } };
|
|
|
|
// MinMaxSlider → sync fields + properties
|
|
slider.RegisterValueChangedCallback(evt =>
|
|
{
|
|
minProp.floatValue = evt.newValue.x;
|
|
maxProp.floatValue = evt.newValue.y;
|
|
serializedObject.ApplyModifiedProperties();
|
|
minField.SetValueWithoutNotify(evt.newValue.x);
|
|
maxField.SetValueWithoutNotify(evt.newValue.y);
|
|
});
|
|
|
|
// FloatField min → sync slider + property
|
|
minField.RegisterValueChangedCallback(evt =>
|
|
{
|
|
float clamped = Mathf.Clamp(evt.newValue, limitMin, maxProp.floatValue);
|
|
minProp.floatValue = clamped;
|
|
serializedObject.ApplyModifiedProperties();
|
|
slider.SetValueWithoutNotify(new Vector2(clamped, maxProp.floatValue));
|
|
minField.SetValueWithoutNotify(clamped);
|
|
});
|
|
|
|
// FloatField max → sync slider + property
|
|
maxField.RegisterValueChangedCallback(evt =>
|
|
{
|
|
float clamped = Mathf.Clamp(evt.newValue, minProp.floatValue, limitMax);
|
|
maxProp.floatValue = clamped;
|
|
serializedObject.ApplyModifiedProperties();
|
|
slider.SetValueWithoutNotify(new Vector2(minProp.floatValue, clamped));
|
|
maxField.SetValueWithoutNotify(clamped);
|
|
});
|
|
|
|
// Track external changes (undo, etc.)
|
|
container.TrackPropertyValue(minProp, p =>
|
|
{
|
|
minField.SetValueWithoutNotify(p.floatValue);
|
|
slider.SetValueWithoutNotify(new Vector2(p.floatValue, maxProp.floatValue));
|
|
});
|
|
container.TrackPropertyValue(maxProp, p =>
|
|
{
|
|
maxField.SetValueWithoutNotify(p.floatValue);
|
|
slider.SetValueWithoutNotify(new Vector2(minProp.floatValue, p.floatValue));
|
|
});
|
|
|
|
row.Add(minField);
|
|
row.Add(slider);
|
|
row.Add(maxField);
|
|
container.Add(row);
|
|
|
|
return container;
|
|
}
|
|
}
|
|
}
|