using UnityEditor; using UnityEngine; namespace UniVRM10 { [CustomEditor(typeof(VRM10SpringBoneJoint))] [CanEditMultipleObjects] class VRM10SpringBoneJointEditor : Editor { private VRM10SpringBoneJoint m_target; private SerializedProperty m_stiffnessForceProp; private SerializedProperty m_gravityPowerProp; private SerializedProperty m_gravityDirProp; private SerializedProperty m_dragForceProp; private SerializedProperty m_jointRadiusProp; private SerializedProperty m_drawColliderProp; private Vrm10Instance m_root; void OnEnable() { if (target == null) { return; } m_target = (VRM10SpringBoneJoint)target; m_stiffnessForceProp = serializedObject.FindProperty(nameof(VRM10SpringBoneJoint.m_stiffnessForce)); m_gravityPowerProp = serializedObject.FindProperty(nameof(VRM10SpringBoneJoint.m_gravityPower)); m_gravityDirProp = serializedObject.FindProperty(nameof(VRM10SpringBoneJoint.m_gravityDir)); m_dragForceProp = serializedObject.FindProperty(nameof(VRM10SpringBoneJoint.m_dragForce)); m_jointRadiusProp = serializedObject.FindProperty(nameof(VRM10SpringBoneJoint.m_jointRadius)); m_drawColliderProp = serializedObject.FindProperty(nameof(VRM10SpringBoneJoint.m_drawCollider)); m_root = m_target.GetComponentInParent(); } static bool m_showJoints; static bool m_showColliders; static bool m_showJointSettings; public override void OnInspectorGUI() { serializedObject.Update(); /// /// spring 情報。readonly。mouse click による hierarchy 参照補助 /// EditorGUI.BeginDisabledGroup(true); var isLastTail = ShowSpringInfo(); EditorGUI.EndDisabledGroup(); EditorGUILayout.PropertyField(m_drawColliderProp, new GUIContent("DrawColliders")); if (isLastTail) { EditorGUILayout.HelpBox("末端 joint です。下記の設定は使用されません。", MessageType.Info); } // // joint // m_showJointSettings = EditorGUILayout.Foldout(m_showJointSettings, "Joint Settings"); if (m_showJointSettings) { LimitBreakSlider(m_stiffnessForceProp, 0.0f, 4.0f, 0.0f, Mathf.Infinity); LimitBreakSlider(m_gravityPowerProp, 0.0f, 2.0f, 0.0f, Mathf.Infinity); EditorGUILayout.PropertyField(m_gravityDirProp); EditorGUILayout.PropertyField(m_dragForceProp); EditorGUILayout.Space(); LimitBreakSlider(m_jointRadiusProp, 0.0f, 0.5f, 0.0f, Mathf.Infinity); } if (serializedObject.ApplyModifiedProperties()) { if (Application.isPlaying) { if (m_root != null) { m_root.Runtime.SpringBone.SetJointLevel(m_target.transform, m_target.Blittable); } } } } /// /// - Jointの所属するSpringの情報を表示する /// - Springに関連付けられたColliderの情報を表示する /// /// 末端 bool ShowSpringInfo() { if (m_root == null) { EditorGUILayout.HelpBox("no vrm-1.0", MessageType.Warning); return false; } EditorGUILayout.ObjectField("Vrm-1.0", m_root, typeof(Vrm10Instance), true, null); var found = m_root.SpringBone.FindJoint(m_target); if (!found.HasValue) { EditorGUILayout.HelpBox("This joint not belongs any spring", MessageType.Warning); return false; } var (spring, i) = found.Value; m_showJoints = EditorGUILayout.Foldout(m_showJoints, $"Springs[{i}]({spring.Name})"); int? jointIndex = default; // joints for (int j = 0; j < spring.Joints.Count; ++j) { var joint = spring.Joints[j]; if (m_showJoints) { var label = $"Joints[{j}]"; if (joint == m_target) { label += "★"; } EditorGUILayout.ObjectField(label, joint, typeof(VRM10SpringBoneJoint), true, null); } if (joint == m_target) { jointIndex = j; } } m_showColliders = EditorGUILayout.Foldout(m_showColliders, "ColliderGroups"); if (m_showColliders && found.HasValue) { // collider groups for (int j = 0; j < spring.ColliderGroups.Count; ++j) { var group = spring.ColliderGroups[j]; EditorGUILayout.LabelField($"ColliderGroups[{j}]({group.Name})"); for (int k = 0; k < group.Colliders.Count; ++k) { var collider = group.Colliders[k]; var label = $"Colliders[{k}]"; EditorGUILayout.ObjectField(label, collider, typeof(VRM10SpringBoneCollider), true, null); } } } return jointIndex == (spring.Joints.Count - 1); } /// /// スライダーと数値入力で限界値の違う、所謂「限界突破スライダー」を作成する /// `EditorGUILayout.PropertyField` の代替として利用する /// private static void LimitBreakSlider(SerializedProperty property, float sliderLeft, float sliderRight, float numberLeft, float numberRight) { var label = new GUIContent(property.displayName); var currentValue = property.floatValue; var rect = EditorGUILayout.GetControlRect(); EditorGUI.BeginProperty(rect, label, property); rect = EditorGUI.PrefixLabel(rect, label); // slider { EditorGUI.BeginChangeCheck(); var sliderRect = rect; sliderRect.width -= 55.0f; rect.xMin += rect.width - 50.0f; var clampedvalue = Mathf.Clamp(currentValue, sliderLeft, sliderRight); var sliderValue = GUI.HorizontalSlider(sliderRect, clampedvalue, sliderLeft, sliderRight); if (EditorGUI.EndChangeCheck()) { property.floatValue = sliderValue; } } // number { EditorGUI.BeginChangeCheck(); var numberValue = Mathf.Clamp(EditorGUI.FloatField(rect, currentValue), numberLeft, numberRight); if (EditorGUI.EndChangeCheck()) { property.floatValue = numberValue; } } EditorGUI.EndProperty(); } void OnSceneGUI() { if (m_root == null) { return; } var found = m_root.SpringBone.FindJoint(m_target); if (!found.HasValue) { return; } var (spring, i) = found.Value; if (spring.Joints.Count > 0 && spring.Joints[0] != null) { var label = $"Springs[{i}]"; if (!string.IsNullOrEmpty(spring.Name)) { label = spring.Name; } Handles.Label(spring.Joints[0].transform.position, label); } } } }