NWH Common
Search Results for

    NUI Editor Framework

    NWH User Interface (NUI) is a custom editor framework providing consistent, professional inspectors across all NWH assets.

    Overview

    NUI provides:

    • Collapsible sections with headers
    • Tab-based organization
    • Consistent styling across all NWH packages
    • Tooltips for all fields
    • Custom property drawers
    • Responsive layout

    All NWH component inspectors use NUI for a unified look and feel.

    Benefits

    • User-Friendly - Clear organization with collapsible sections
    • Professional - Consistent appearance across all assets
    • Informative - Tooltips explain every field
    • Efficient - Collapse unused sections to reduce clutter
    • Extensible - Easy to create custom editors

    NUI Components

    NUIEditor

    Base class for custom editors.

    Features:

    • Automatic section grouping
    • Collapsible headers
    • Tab support
    • Tooltip integration
    • Responsive design

    Example Usage:

    using NWH.NUI;
    using UnityEditor;
    
    [CustomEditor(typeof(MyComponent))]
    public class MyComponentEditor : NUIEditor
    {
        public override bool OnInspectorNUI()
        {
            if (!base.OnInspectorNUI())
                return false;
    
            drawer.BeginSubsection("Main Settings");
            drawer.Field("fieldName");
            drawer.EndSubsection();
    
            drawer.BeginSubsection("Advanced");
            drawer.Field("advancedField");
            drawer.EndSubsection();
    
            drawer.EndEditor(this);
            return true;
        }
    }
    

    NUIPropertyDrawer

    Base class for custom property drawers.

    Features:

    • Consistent styling with NUIEditor
    • Tooltip support
    • Flexible layout

    Example:

    using NWH.NUI;
    using UnityEditor;
    using UnityEngine;
    
    [CustomPropertyDrawer(typeof(MyStruct))]
    public class MyStructDrawer : NUIPropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            drawer.BeginProperty(position, property, label);
    
            drawer.Field("field1");
            drawer.Field("field2");
    
            drawer.EndProperty();
        }
    }
    

    Creating Custom Editors

    Basic Editor

    using UnityEditor;
    using NWH.NUI;
    
    [CustomEditor(typeof(VehicleComponent))]
    public class VehicleComponentEditor : NUIEditor
    {
        public override bool OnInspectorNUI()
        {
            if (!base.OnInspectorNUI())
                return false;
    
            // Start drawing
            drawer.BeginSubsection("Physics");
            drawer.Field("mass");
            drawer.Field("drag");
            drawer.EndSubsection();
    
            drawer.BeginSubsection("Visuals");
            drawer.Field("meshRenderer");
            drawer.Field("material");
            drawer.EndSubsection();
    
            drawer.EndEditor(this);
            return true;
        }
    }
    

    Editor with Tabs

    public override bool OnInspectorNUI()
    {
        if (!base.OnInspectorNUI())
            return false;
    
        // Create tabs
        drawer.BeginSubsection("Settings");
    
        int tabIndex = drawer.HorizontalToolbar("settingsTab",
            new string[] { "General", "Physics", "Audio" });
    
        switch (tabIndex)
        {
            case 0:  // General
                drawer.Field("componentName");
                drawer.Field("enabled");
                break;
    
            case 1:  // Physics
                drawer.Field("mass");
                drawer.Field("drag");
                break;
    
            case 2:  // Audio
                drawer.Field("audioSource");
                drawer.Field("volume");
                break;
        }
    
        drawer.EndSubsection();
        drawer.EndEditor(this);
        return true;
    }
    

    Conditional Fields

    drawer.BeginSubsection("Engine");
    
    drawer.Field("engineEnabled");
    
    // Only show engine settings if enabled
    if (drawer.FindProperty("engineEnabled").boolValue)
    {
        drawer.Field("horsePower");
        drawer.Field("maxRPM");
    }
    
    drawer.EndSubsection();
    

    Drawer Methods

    Fields

    // Draw property field
    drawer.Field("propertyName");
    
    // Field with custom label
    drawer.Field("propertyName", "Custom Label");
    
    // Field with tooltip
    drawer.Field("propertyName", true);  // Uses property tooltip
    
    // Nested property
    drawer.Field("parent.child.propertyName");
    

    Buttons

    // Button
    if (drawer.Button("Click Me"))
    {
        // Button clicked
        DoSomething();
    }
    
    // Button with custom size
    if (drawer.Button("Action", 100, 30))
    {
        PerformAction();
    }
    

    Labels and Info

    // Info box
    drawer.Info("This is an information message");
    
    // Warning box
    drawer.Warning("This is a warning");
    
    // Error box
    drawer.Error("This is an error");
    
    // Label
    drawer.Label("Status: Active");
    
    // Header
    drawer.Header("Configuration");
    

    Layout

    // Begin horizontal group
    drawer.BeginHorizontal();
    drawer.Field("fieldA");
    drawer.Field("fieldB");
    drawer.EndHorizontal();
    
    // Space
    drawer.Space(10);
    
    // Separator line
    drawer.Separator();
    

    Subsections

    // Collapsible subsection
    drawer.BeginSubsection("Section Name");
    drawer.Field("field1");
    drawer.Field("field2");
    drawer.EndSubsection();
    
    // Subsection with custom header
    drawer.BeginSubsection("Settings", "settingsExpanded");
    // Content
    drawer.EndSubsection();
    

    Property Attributes

    NUI supports custom attributes for additional control:

    Tooltip Attribute

    [Tooltip("Maximum speed in m/s")]
    public float maxSpeed = 50f;
    

    Automatically displayed in NUI editors.

    Range Attribute

    [Range(0, 100)]
    public float volume = 50f;
    

    Creates slider in inspector.

    Header Attribute

    [Header("Physics Settings")]
    public float mass;
    public float drag;
    

    Creates section header (NUI styling applied automatically).

    Styling

    NUI uses consistent colors and styling:

    Colors

    • Header Background: Dark gray
    • Section Background: Medium gray
    • Field Background: Light gray
    • Info Box: Blue tint
    • Warning Box: Yellow tint
    • Error Box: Red tint

    Fonts

    • Headers: Bold, larger size
    • Labels: Regular, standard size
    • Tooltips: Italic, smaller size

    Spacing

    • Section Padding: 4px
    • Field Spacing: 2px
    • Subsection Margins: 8px

    Best Practices

    1. Group Related Fields: Use subsections to organize related properties
    2. Provide Tooltips: Every public field should have a tooltip
    3. Use Tabs for Complexity: If editor has >3 sections, consider tabs
    4. Conditional Visibility: Hide irrelevant fields based on settings
    5. Clear Labels: Use descriptive field names and custom labels
    6. Consistent Naming: Follow NWH naming conventions
    7. Test Collapsing: Ensure editors work when sections are collapsed

    Example: Complete Component Editor

    using UnityEditor;
    using NWH.NUI;
    
    [CustomEditor(typeof(WheelController))]
    public class WheelControllerEditor : NUIEditor
    {
        public override bool OnInspectorNUI()
        {
            if (!base.OnInspectorNUI())
                return false;
    
            // Tabs for major categories
            int tabIndex = drawer.HorizontalToolbar("mainTab",
                new string[] { "Wheel", "Suspension", "Friction", "Debug" });
    
            switch (tabIndex)
            {
                case 0:  // Wheel
                    DrawWheelTab();
                    break;
    
                case 1:  // Suspension
                    DrawSuspensionTab();
                    break;
    
                case 2:  // Friction
                    DrawFrictionTab();
                    break;
    
                case 3:  // Debug
                    DrawDebugTab();
                    break;
            }
    
            drawer.EndEditor(this);
            return true;
        }
    
        private void DrawWheelTab()
        {
            drawer.BeginSubsection("Wheel Properties");
            drawer.Field("radius");
            drawer.Field("width");
            drawer.Field("mass");
            drawer.EndSubsection();
    
            drawer.BeginSubsection("Visuals");
            drawer.Field("rotatingContainer");
            drawer.Field("nonRotatingContainer");
            drawer.EndSubsection();
        }
    
        private void DrawSuspensionTab()
        {
            drawer.BeginSubsection("Spring");
            drawer.Field("spring.maxForce");
            drawer.Field("spring.maxLength");
            drawer.Field("spring.progressiveness");
            drawer.EndSubsection();
    
            drawer.BeginSubsection("Damper");
            drawer.Field("damper.bumpRate");
            drawer.Field("damper.reboundRate");
            drawer.EndSubsection();
        }
    
        private void DrawFrictionTab()
        {
            drawer.BeginSubsection("Friction");
            drawer.Field("activeFrictionPreset");
    
            if (drawer.Button("Create New Preset"))
            {
                // Create friction preset logic
            }
    
            drawer.EndSubsection();
        }
    
        private void DrawDebugTab()
        {
            drawer.BeginSubsection("Debug Info");
            drawer.Info($"Is Grounded: {(target as WheelController).isGrounded}");
            drawer.Label($"Spring Force: {(target as WheelController).springForce:F1} N");
            drawer.Label($"Slip: {(target as WheelController).slip:F2}");
            drawer.EndSubsection();
        }
    }
    

    Extending NUI

    Custom Drawer Helper

    Create reusable drawer methods:

    public static class NUIExtensions
    {
        public static void CurveField(this NUIDrawer drawer,
            string propertyPath, string label = null)
        {
            SerializedProperty property = drawer.FindProperty(propertyPath);
            EditorGUILayout.CurveField(
                label ?? property.displayName,
                property.animationCurveValue
            );
        }
    }
    
    // Usage
    drawer.CurveField("torqueCurve");
    

    Custom Property Drawer

    [CustomPropertyDrawer(typeof(Wheel))]
    public class WheelDrawer : NUIPropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            drawer.BeginProperty(position, property, label);
    
            drawer.Field("radius");
            drawer.Field("width");
    
            // Custom visualization
            if (drawer.Button("Preview Wheel"))
            {
                PreviewWheel(property);
            }
    
            drawer.EndProperty();
        }
    }
    

    Troubleshooting

    Editor not displaying:

    • Ensure script compiles without errors
    • Check [CustomEditor(typeof(YourClass))] attribute
    • Verify class inherits from NUIEditor

    Fields not showing:

    • Check property name matches exactly (case-sensitive)
    • Ensure property is serializable
    • Verify property is public or has [SerializeField]

    Sections not collapsing:

    • Each subsection needs unique key (second parameter)
    • Check EditorPrefs for saved collapse states

    Tooltips not working:

    • Add [Tooltip("...")] attribute to field
    • Ensure tooltip parameter is true: drawer.Field("field", true)

    API Reference

    For detailed API documentation, see the API reference in the navigation menu for:

    • NWH.NUI.NUIEditor
    • NWH.NUI.NUIDrawer
    • NWH.NUI.NUIPropertyDrawer
    • Edit this page
    In this article
    Back to top Copyright © NWH - Vehicle Physics, Aerodynamics, Dynamic Water Physics