1 year ago

#388819

test-img

Alex

C# Unity EditorTool Frame-Rate Issues

I'm writing a tool which gives the user quick access to all their prefabs, and easy ways to place them in the scene. I've used it for hours without many problems. A performance bug crept in overnight - and now the GUI FPS is slowed to a crawl, no matter what I comment out. I can replace the entire OnToolGUI() function with a single GUI.Button() call and that button will have a reaction delay upwards of 1s. UI interactions like clicks still happen immediately. After moving the script to a few other projects, the same bug cropped up after a few seconds or minutes of use. Here's the whole thing - at this point I'd appreciate just knowing what happens when somebody else uses it, but any suggestions on this weird frame-rate issue would be great.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEditor.Experimental.SceneManagement;

[EditorTool("Placer Tool")]
class Placer : EditorTool
{
    bool verbose = true;
    GUIContent m_IconContent;
    public override GUIContent toolbarIcon { get { return m_IconContent; } }

    Texture2D magnetIcon;
    Texture2D gridIcon;
    Texture2D toolIcon;
    float grid = 1f;
    bool snapToGrid = false;
    bool forceStickySurfaces = false;
    bool editorVisible = true;
    List<string> filters = new List<string>();
    List<string> currentFilters = new List<string>();
    Texture2D whiteTexture;
    Texture2D backgroundTexture;

    void OnEnable()
    {
        whiteTexture = Swatch( new Color( 1,1,1,1f ) );
        backgroundTexture = Swatch( new Color( 0.24f, 0.24f, 0.24f, 1f ) );
        magnetIcon = MagnetIcon;
        gridIcon = GridIcon;
        toolIcon = ToolIcon;

        m_IconContent = new GUIContent()
        {
            image = toolIcon,
            text = "Placer Tool",
            tooltip = "Placer Tool"
        };

        GetStyles();
        GetPreviewMaterial();
        GetPlaceables();
        UpdateFilterResults();
    }
    
    public override void OnToolGUI( EditorWindow window )
    {
        // verbose = false;

        if ( !(window is SceneView sceneView) ) return;
        else
        {
            CheckForNewResources();
            GetMouseInfo();
            Handles.BeginGUI();
            DrawGUI( window );
            Handles.EndGUI();

            if( MouseIsInSceneView() )
            {                
                if( Event.current.button == 0 && GUI.Button( new Rect( mouseInfo.screenPos.x - 16, mouseInfo.screenPos.y - 16, 32,32 ), "", GUIStyle.none ) )
                { // Detecting clicks is done by drawing a button under the mouse - using the default method caused unnecessary object selections
                    if( Event.current.control && mouseInfo.collider != null )
                    { // Destroy object
                        Undo.RegisterFullObjectHierarchyUndo( mouseInfo.collider.gameObject, "Unplace "+mouseInfo.collider.gameObject.name );
                        DestroyImmediate( mouseInfo.collider.gameObject );
                    }
                    else if( selectedIndex > -1 )
                    { // Create object
                        previewMaterial.color = Color.green;
                        GameObject newObj = PrefabUtility.InstantiatePrefab( placeables[selectedIndex].originPrefab, mouseInfo.scene ) as GameObject;
                        if( mouseInfo.isInPrefab ) StageUtility.PlaceGameObjectInCurrentStage( newObj );
                        Undo.RegisterCreatedObjectUndo( newObj, "Place "+placeables[selectedIndex].originPrefab.name );
                        newObj.transform.position = mouseInfo.worldPos;
                        newObj.transform.rotation = rotation;
                    }
                }
                
                if( Event.current.isScrollWheel && Event.current.type == EventType.ScrollWheel )
                { // Adjusting the primary rotation according to scrollwheel movement
                    primaryRotation += Event.current.delta.y > 0 ? 15 : -15;
                    Event.current.Use();
                }

                // Getting the base rotation from the mouse normal if necessary/possible
                if( forceStickySurfaces && mouseInfo != null ) rotation = Quaternion.FromToRotation( Vector3.up, mouseInfo.worldNormal );
                else rotation = Quaternion.identity;

                if( Event.current.control && mouseInfo.collider != null )
                { // Delete mode - matching the preview mesh position/rotation to the object to be deleted
                    GameObject rootPrefabAtMouse = PrefabUtility.GetOutermostPrefabInstanceRoot( mouseInfo.collider.gameObject );

                    if( rootPrefabAtMouse != null )
                    {
                        previewMaterial.color = new Color(1,0,0,0.3f);
                        rotation = rootPrefabAtMouse.transform.rotation;
                        position = rootPrefabAtMouse.transform.position;
                    }
                }
                else
                { // Standard preview mode - rotation is adjusted using the primary rotation about the Y axis
                    previewMaterial.color = new Color(0,1,0,0.3f);
                    rotation *= Quaternion.Euler( 0, primaryRotation, 0 );
                }

                if( Event.current.type == EventType.Layout && lastRenderedFrame != Time.renderedFrameCount )
                { // Drawing object preview
                    DrawObjectPreview( Camera.current );
                    lastRenderedFrame = Time.renderedFrameCount;
                }
            }
        }
    }

    Material previewMaterial;

    void GetPreviewMaterial()
    {
        if( verbose ) Debug.Log( "Creating preview material" );
        previewMaterial = new Material( Shader.Find( "Universal Render Pipeline/Lit" ) );
    }

    GUIStyle boldStyle;
    GUIStyle focussedStyle;
    GUIStyle normalStyle;
    Texture2D transparentBg;
    Texture2D hoverBg;

    void GetStyles()
    {
        if( verbose ) Debug.Log( "Fetching style" );
        if( normalStyle == null || boldStyle == null || focussedStyle == null )
        {
            transparentBg = Swatch( new Color(0,0,0,0) );
            hoverBg = Swatch( new Color(1,1,1,0.25f) );

            normalStyle = new GUIStyle( GUIStyle.none );
            normalStyle.fontStyle = FontStyle.Normal;
            normalStyle.alignment = TextAnchor.MiddleCenter;

            normalStyle.normal.textColor = Color.Lerp( Color.gray, Color.white, 0.5f );
            normalStyle.normal.background = transparentBg;
            normalStyle.hover.background = hoverBg;
            normalStyle.hover.textColor = Color.white;
        
            boldStyle = new GUIStyle( normalStyle );
            boldStyle.fontStyle = FontStyle.Bold;
        
            focussedStyle = new GUIStyle( boldStyle );
            
            focussedStyle.normal.textColor = Color.white;
            focussedStyle.normal.background = hoverBg;
            focussedStyle.hover.background = hoverBg;
        }
    }

    int lastRenderedFrame;
    MousePositionInfo mouseInfo;

    public void GetMouseInfo()
    {
        mouseInfo = EditorMouseInfo;

        if( mouseInfo != null && snapToGrid )
        {
            mouseInfo.worldPos = Vector3Int.RoundToInt( mouseInfo.worldPos/grid );
            mouseInfo.worldPos *= grid;
        }
        
        if( mouseInfo != null ) position = mouseInfo.worldPos;
    }

    List<Placeable> placeables = new List<Placeable>();
    int selectedIndex = -1;

    public void GetPlaceables()
    {
        if( verbose ) Debug.Log( "Fetching prefabs" );

        List<GameObject> allGos = new List<GameObject>();
        placeables.Clear();
        filters.Clear();
        filters.Add( clearFilterSymbol );

        string head = Application.dataPath;
        List<string> subPaths = new List<string>( System.IO.Directory.GetDirectories( head ) );
        bool allDiscovered = false;

        // Find all subidrectories within the asset folder
        while( allDiscovered == false )
        {
            List<string> newSubPaths = new List<string>();
            allDiscovered = true;
            foreach( string subPath in subPaths )
            {
                string[] subDirectories = System.IO.Directory.GetDirectories( subPath );

                if( subDirectories.GetLength(0) > 0 )
                foreach( string subDirectory in subDirectories )
                if( subPaths.Contains( subDirectory ) == false )
                {
                    newSubPaths.Add( subDirectory );
                    allDiscovered = false;
                }
            } 
            subPaths.AddRange( newSubPaths );
        }
 
        // Iterate list in reverse so each gameobject records its full path to the filter list
        for( int s = subPaths.Count-1; s >= 0; s-- )
        {
            string resourcePath = subPaths[s].Replace( head+"\\", "" );
            resourcePath = "Assets\\"+resourcePath;
            List<string> pathFilters = new List<string>( resourcePath.Split( "\\".ToCharArray()) );
            pathFilters.Remove( "Assets" );

            string[] guids = AssetDatabase.FindAssets("t:prefab",new string[]{ resourcePath });
            bool addFiltersForPath = false;

            foreach( string guid in guids )
            {
                GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>( AssetDatabase.GUIDToAssetPath( guid ) );

                if( go != null && allGos.Contains( go ) == false )
                {
                    placeables.Add( new Placeable( pathFilters, go ) );
                    allGos.Add( go );
                    addFiltersForPath = true;
                }
            }

            if( addFiltersForPath )
            foreach( string filter in pathFilters )
            if( filters.Contains( filter ) == false )
            {
                filters.Add( filter );
            }
        }
    }

    int lastResourceCount = 0;

    void CheckForNewResources()
    {
        int newResourceCount = Resources.LoadAll<GameObject>("").GetLength(0);

        if( lastResourceCount != newResourceCount )
        {
            if( verbose ) Debug.Log( "New prefab list requested by file change" );

            GetPlaceables();
            UpdateFilterResults();
            lastResourceCount = newResourceCount;
        }
    }

    Quaternion rotation = Quaternion.identity;
    Vector3 position = Vector3.zero;
    float primaryRotation = 0;
    Mesh previewMesh;

    void GetPreviewMesh()
    {
        if( verbose ) Debug.Log( "New preview mesh being generated" );

        if( previewMesh == null ) previewMesh = new Mesh();
        MeshFilter[] meshFilters = placeables[selectedIndex].originPrefab.GetComponentsInChildren<MeshFilter>();

        if( meshFilters.GetLength(0) > 1 )
        {
            CombineInstance[] combines = new CombineInstance[meshFilters.GetLength(0)];

            for( int m = 0; m < meshFilters.GetLength(0); m++ )
            { // SuperSimplify removes submesh data from the existing mesh, the result of which is given as a combine
                combines[m].mesh = SuperSimplify( meshFilters[m].sharedMesh );
                combines[m].transform = Matrix4x4.TRS( meshFilters[m].transform.localPosition, meshFilters[m].transform.localRotation, Vector3.one );
            }

            previewMesh.CombineMeshes( combines, true, true, false );
            previewMesh = SuperSimplify( previewMesh );
        }
        else if( meshFilters.GetLength(0) == 1 ) previewMesh = SuperSimplify( meshFilters[0].sharedMesh );
        else previewMesh.Clear( false );
    }

    void DrawObjectPreview( Camera cam )
    { // Uses Graphics.DrawMesh which generates garbage. This is mostly handled by the in-built GC; this method is still sub-optimal
        if( selectedIndex > -1 && previewMaterial != null )
        Graphics.DrawMesh( previewMesh, position, rotation, previewMaterial, LayerMask.NameToLayer( "Default" ) );
    }

    // All GUI below this point
    Rect widgetRect = new Rect( 0,0,0,0 );
    float lineHeight = 20;
    float panelWidth = 128;
    int accentThickness = 4;
    enum FilterMode { Any, All };
    FilterMode filterMode = FilterMode.Any;
    string gridString = "0.25";
    Color backgroundColor = new Color( 0.2f,0.2f,0.2f,0.6f );
    string clearFilterSymbol = "⊗";

    void DrawGUI( EditorWindow window )
    {
        lineHeight = 20;
        widgetRect = new Rect( 0,0,lineHeight,lineHeight );

        if( DoWidget( Widget.Button, false, "", "Toggle Placer UI", null, toolIcon ) ) editorVisible = !editorVisible;

        if( editorVisible )
        {
            DrawToolControls( window );
            DrawFilterSelect( window );
            DrawPlaceableSelect( window );
        }
    }

    void DrawToolControls( EditorWindow window )
    {
        widgetRect.x = widgetRect.x + widgetRect.width;
        widgetRect.y = 0;
        widgetRect.width = panelWidth * 0.2f;

        if( DoWidget( Widget.Toggle, forceStickySurfaces, "", "Toggle stick to surface to normal", null, magnetIcon ) ) forceStickySurfaces = !forceStickySurfaces;

        widgetRect.x += widgetRect.width;
        if( DoWidget( Widget.Toggle, snapToGrid, "", "Toggle grid snap", null, gridIcon ) ) snapToGrid = !snapToGrid;
        widgetRect.x += widgetRect.width;

        widgetRect.width = panelWidth - ( widgetRect.x + lineHeight );
        GUI.DrawTexture( new Rect( widgetRect.position+Vector2.left,widgetRect.size+(Vector2.right*2) ), backgroundTexture );
        gridString = GUI.TextField( widgetRect, gridString );

        if( gridString != "" )
        {
            try{ grid = Mathf.Clamp( float.Parse( gridString ), 0.0000001f, Mathf.Infinity ); }
            catch{}
        }
        widgetRect.x = panelWidth - lineHeight;
        widgetRect.width = lineHeight;

        if( DoWidget( Widget.Repeat, false, "||", "", null ) )
        {
            panelWidth = Event.current.mousePosition.x + lineHeight/2;
        }

        panelWidth = Mathf.Clamp( panelWidth, 120, 1024 );
    }

    void DrawFilterSelect( EditorWindow window )
    {
        widgetRect.y = 0;
        widgetRect.x += widgetRect.width;

        widgetRect.width = boldStyle.CalcSize( new GUIContent( "Any" ) ).x + 8;
        if( DoWidget( Widget.Button, false, filterMode.ToString(), "Require all filters / Require any filters" ) ) filterMode = (FilterMode)1-(int)filterMode;
        widgetRect.x += widgetRect.width;

        for( int f = 0; f < filters.Count; f++ )
        {
            string filter = filters[f];
            bool isCurrent = currentFilters.Contains( filter );

            widgetRect.width = boldStyle.CalcSize( new GUIContent( filter ) ).x + 8;

            if( DoWidget( Widget.Filter, isCurrent, filter, "", f==0?null:new Color[]{ FilterColor(f) } ) )
            {
                if( filter == clearFilterSymbol ) currentFilters.Clear();
                else if( isCurrent ) currentFilters.Remove( filter );
                else currentFilters.Add( filter );

                UpdateFilterResults();
            }

            widgetRect.x += widgetRect.width;

            if( widgetRect.x + widgetRect.width > ( window.position.width - panelWidth ) )
            {
                widgetRect.x = panelWidth;
                widgetRect.y += lineHeight;
            } 
        }

        widgetRect.height = lineHeight;
    }

    public Vector2 placeableScrollPos = Vector2.zero;
    float placeableLineHeight = 0.75f;

    void DrawPlaceableSelect( EditorWindow window )
    {
        if( this.filterResults.Contains( null ) ) UpdateFilterResults();

        widgetRect.position = new Vector2(0, lineHeight);
        widgetRect.width = panelWidth;
        widgetRect.height = lineHeight*placeableLineHeight;

        Rect scrollWindow = new Rect( widgetRect.position, new Vector2( panelWidth, window.position.height-(widgetRect.y+widgetRect.height) ) );

        Rect scrollContent = new Rect( widgetRect );
        scrollContent.width -= 16;
        scrollContent.height = lineHeight*placeableLineHeight * filterResults.Count;
        
        placeableScrollPos = GUI.BeginScrollView( scrollWindow, placeableScrollPos, scrollContent );

        for( int p = 0; p < filterResults.Count; p++ )
        {
            int originalIndex = filterResultIndexes[p];
            List<Color> filterColors = new List<Color>();

            foreach( string filter in filterResults[p].filters )
            {
                filterColors.Add( FilterColor( filters.IndexOf( filter ) ) );
            }

            GUIStyle selectedStyle = selectedIndex == originalIndex ? boldStyle : normalStyle;

            if( DoWidget( Widget.Prefab, selectedIndex == originalIndex, filterResults[p].originPrefab.name, "", filterColors.ToArray() ) )
            {
                selectedIndex = originalIndex;
                GetPreviewMesh();
            }

            widgetRect.y += widgetRect.height;
        }

        GUI.EndScrollView();
    }

    List<Placeable> filterResults = new List<Placeable>();
    List<int> filterResultIndexes = new List<int>();

    void UpdateFilterResults()
    {
        if( verbose ) Debug.Log( "Updating filter results" );
        filterResults.Clear();
        filterResultIndexes.Clear();

        for( int p = 0; p < placeables.Count; p++ )
        {
            bool isInFilter = filterMode == FilterMode.Any ? ContainsAny( currentFilters, placeables[p].filters ) : ContainsAll( placeables[p].filters, currentFilters );

            if( currentFilters.Count == 0 || isInFilter )
            {
                filterResults.Add( placeables[p] );
                filterResultIndexes.Add( p );
            }
        }
    }

    // Items below this point are utilities to the above functions

    bool MouseIsInSceneView()
    {
        if( SceneView.currentDrawingSceneView != null && SceneView.currentDrawingSceneView.camera != null && mouseInfo != null )
        {
            Vector2 mouseUV = SceneView.currentDrawingSceneView.camera.ScreenToViewportPoint( mouseInfo.screenPos );

            return ( mouseUV.x > 0 && mouseUV.y > 0 && mouseUV.x < 1 && mouseUV.y < 1 );
        }
        else return false;
    }

    Vector3 EditorMousePos { get
    {  
        Vector3 mousePosition = Event.current.mousePosition;
        mousePosition.y = SceneView.currentDrawingSceneView.camera.pixelHeight - mousePosition.y;
        mousePosition = SceneView.currentDrawingSceneView.camera.ScreenToWorldPoint(mousePosition);
        mousePosition.y = -mousePosition.y;
        return mousePosition;
    }}

    MousePositionInfo EditorMouseInfo { get
    {
        PrefabStage stage = PrefabStageUtility.GetCurrentPrefabStage();
        bool inPrefab = stage != null;
        Scene scene = inPrefab ? stage.scene : EditorSceneManager.GetActiveScene();
        PhysicsScene physics = PhysicsSceneExtensions.GetPhysicsScene(scene);
        Vector3 mousePosition = Event.current.mousePosition;
        Ray ray = HandleUtility.GUIPointToWorldRay(mousePosition);
        RaycastHit hitInfo;
        LayerMask mask = LayerMask.GetMask( "Default" );
        bool hit = physics.Raycast( ray.origin, ray.direction, out hitInfo, 1024, mask, QueryTriggerInteraction.Ignore );

        if( hit )  return new MousePositionInfo( mousePosition, hitInfo.point, hitInfo.normal, hitInfo.collider, scene, inPrefab );
        else return new MousePositionInfo( mousePosition, RayToPlane( ray ), Vector3.up, null, scene, inPrefab );
    }}
    
    Texture2D Swatch( Color color )
    {
        Texture2D swatch = new Texture2D(1,1);
        swatch.SetPixel( 0,0,color );
        swatch.Apply();
        return swatch;
    } 

    Mesh SuperSimplify( Mesh mesh )
    {
        Mesh newMesh = new Mesh();

        newMesh.vertices = mesh.vertices;
        newMesh.normals = mesh.normals;
        newMesh.triangles = mesh.triangles;
        newMesh.uv = mesh.uv;

        return newMesh;
    }

    Color FilterColor( int filterIndex )
    {
        float hue = (float)filterIndex/filters.Count;
        hue += filterIndex%2==0?0.5f:0;
        hue %= 1;
        return Color.HSVToRGB( hue,0.7f,0.8f );
    }
    
    Vector3 RayToPlane( Ray ray )
    {
        Plane hPlane = new Plane(Vector3.up, Vector3.zero);
        float distance = 0;
        if( hPlane.Raycast(ray, out distance) ) return ray.GetPoint( distance );
        else if( hPlane.Raycast(new Ray( ray.origin, -ray.direction ), out distance) ) return ray.GetPoint( distance );
        else return ray.origin;
    }

    Texture2D ToolIcon{ get
    {
        Vector2 editorIconSize = EditorGUIUtility.GetIconSize();
        int max = editorIconSize == Vector2.zero ? 16 : Mathf.Max( Mathf.RoundToInt( editorIconSize.x ), Mathf.RoundToInt( editorIconSize.y ) );
        Texture2D newIcon = new Texture2D(max,max);

        for( int x = 0; x < max; x++ ){
        for( int y = 0; y < max; y++ ){
            bool set = false;

            Vector2 direction = new Vector2(x,y) - new Vector2( (max/2f)-0.5f,(max/2f)-0.5f);
            float range = direction.magnitude;
            float angle = Vector2.SignedAngle( Vector2.up, direction );
            angle = angle < 0 ? 360-angle : angle;

            if( range <= max/2 ) 
            {
                // Rosette
                if( range > ( max/2 ) - ( max/16 ) ) set = true;
                // Hammer head
                if( x > (max-y) && y < ( x+(max/2) ) && y > ( x-(max/2) ) && x < (max-y+(max/2) )-2 ) set = true;
                // Hammer handle
                if( Mathf.Abs(x-y) < max/8 && x < (max-y+(max/2) ) ) set = true;
            }

            newIcon.SetPixel( x,y,set?Color.Lerp( Color.white,Color.gray,0.5f ) : new Color(0,0,0,0) );
        }}
        newIcon.Apply();

        return newIcon;
    }}

    Texture2D MagnetIcon{ get
    {
        Vector2 editorIconSize = EditorGUIUtility.GetIconSize();
        int max = editorIconSize == Vector2.zero ? 16 : Mathf.Max( Mathf.RoundToInt( editorIconSize.x ), Mathf.RoundToInt( editorIconSize.y ) );
        Texture2D newIcon = new Texture2D(max,max);

        for( int x = 0; x < max; x++ ){
        for( int y = 0; y < max; y++ ){
            bool set = false;

            Vector2 direction = new Vector2(x,y) - new Vector2( (max/2f)-0.5f,(max/2f)-0.5f);
            float range = direction.magnitude;
            float angle = Vector2.Angle( Vector2.one, direction );

            float outRadius = ( max/2f );
            float inRadius = (max/4f);

            if( range < outRadius && !( x < max/3f && y < max/3f ) ) 
            {
                // Rosette
                if( range > inRadius )
                {
                    if( angle < 120 ) set = true;
                    else if( angle < 160 && (x+y)%2==1 ) set = true;
                }
            }

            newIcon.SetPixel( x,y,set?Color.Lerp( Color.white,Color.gray,0.5f ) : new Color(0,0,0,0) );
        }}
        newIcon.Apply();

        return newIcon;
    }}

    Texture2D GridIcon{ get
    {
        Vector2 editorIconSize = EditorGUIUtility.GetIconSize();
        int max = editorIconSize == Vector2.zero ? 16 : Mathf.Max( Mathf.RoundToInt( editorIconSize.x ), Mathf.RoundToInt( editorIconSize.y ) );
        Texture2D newIcon = new Texture2D(max,max);

        for( int x = 0; x < max; x++ ){
        for( int y = 0; y < max; y++ ){
            bool set = false;

            Vector2 direction = new Vector2(x,y) - new Vector2( (max/2f)-0.5f,(max/2f)-0.5f);
            float range = direction.magnitude;
            float angle = Vector2.Angle( Vector2.one, direction );

            if( (x+(max/3)) % ( max/2 ) < 2 || (y+(max/3)) % ( max/2 ) < 2 ) set = true;

            newIcon.SetPixel( x,y,set?Color.Lerp( Color.white,Color.gray,0.5f ) : new Color(0,0,0,0) );
        }}
        newIcon.Apply();

        return newIcon;
    }}

    bool ContainsAny( List<string> values, List<string> query )
    {
        foreach( string strA in query )
        if( values.Contains( strA ) ) return true;

        return false;
    }

    bool ContainsAll( List<string> values, List<string> query )
    {
        foreach( string strA in query )
        if( values.Contains( strA ) == false ) return false;

        return true;
    }

    class MousePositionInfo
    {
        public Vector3 screenPos;
        public Vector3 worldPos;
        public Vector3 worldNormal;
        public Collider collider;
        public Scene scene;
        public bool isInPrefab;

        public MousePositionInfo( Vector3 screenPos, Vector3 worldPos, Vector3 worldNormal, Collider collider, Scene scene, bool inPrefab )
        {
            this.screenPos = screenPos;
            this.worldPos = worldPos;
            this.worldNormal = worldNormal;
            this.collider = collider;
            this.scene = scene;
            this.isInPrefab = inPrefab;

        }
    }
    
    class Placeable
    {
        public List<string> filters;
        public GameObject originPrefab;

        public Placeable( List<string> filters, GameObject originPrefab )
        {
            this.filters = filters;
            this.originPrefab = originPrefab;
        }
    }
    
    // Items below this point are GUI utilities

    enum Widget { Filter,Prefab,Button,Toggle,Repeat }

    bool DoWidget( Widget type, bool isActive, string widgetText, string widgetTooltip = "", Color[] widgetColors = null, Texture2D widgetIcon = null )
    { // Uses the current widgetRect to draw a widget
        bool iconOnly = ( ( type == Widget.Button || type == Widget.Toggle || type == Widget.Repeat ) && widgetIcon != null );

        GUIStyle widgetStyle = type == Widget.Filter ? boldStyle : normalStyle;
        if( isActive ) widgetStyle = focussedStyle;

        GUIContent widgetContent = new GUIContent()
        {
            image = widgetIcon,
            text = iconOnly ? "" : widgetText,
            tooltip = widgetTooltip
        };

        GUI.DrawTexture( widgetRect, backgroundTexture );
        int colorCount = widgetColors != null ? widgetColors.GetLength(0) : 0;

        if( ( type == Widget.Filter || type == Widget.Prefab ) && widgetColors != null && colorCount > 0 )
        {
            Rect accentRect = new Rect( widgetRect );

            for( int c = 0; c < colorCount; c++ )
            {
                if( type == Widget.Filter )
                {
                    if( isActive == false )
                    {
                        accentRect.y = ( widgetRect.y + widgetRect.height ) - accentThickness;
                        accentRect.height = accentThickness;
                    }
                }
                else
                {
                    accentRect.x = ( widgetRect.x + widgetRect.width ) - ( (c+1) * accentThickness );
                    accentRect.width = accentThickness;
                }

                GUI.color = widgetColors[c];
                GUI.DrawTexture( accentRect, whiteTexture );
            }
        }
        GUI.color = Color.white;

        if( type == Widget.Repeat ) return GUI.RepeatButton( widgetRect, widgetContent, widgetStyle );
        else return GUI.Button( widgetRect, widgetContent, widgetStyle );
    }
}

c#

unity-game-engine

editor

0 Answers

Your Answer

Accepted video resources