1 year ago
#388819
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