Editor: Draw Box/Area over Box/Area

hi there :slight_smile:

So… i need to create an editor for my Textur Offset, border and slicing. I’m talking about UnityEditor, not ingame.

My idea is:
Put the Texture in a Box and draw dotted lines, colored fields…etc above it…
But it’s not working, since the lines, boxes and Co are drawn below the texture, instead of overlapping it.

So the Question is:
How can i get GUI Elements overlapping specific GUI Elements?

:slight_smile:
thanks for reading.

still in Need of this :frowning:

Pushed this Question to the Answers, hopeing for help there :frowning:

a mockup/example of what you tried so far might help. (small part of the code)

1 Like

In general, without code, you can always use the GUI / Editor GUI functions, and not the layout ones. They use coordinates, so you can just define rects to draw stuff on, and it will draw them one on top of the other.

1 Like

Okay… i tried with BeginArea.
This shows the texture correctly, and allows me to draw lines, Boxes and text above its texture.

Current Code (line 39-43):

  #region Show The Inspector GUI
   private void ShowControls(){
     ListTilemaps();
     //Show Default Prefab Field
     myTarget.DefaultSpriteObject = (GameObject)EditorGUILayout.ObjectField("Default Prefab:", myTarget.DefaultSpriteObject, typeof(GameObject), true);
     //Get the current Tilemap Index first (if possible)!
     if(myTarget.CurrentCreatedTilemap != null){
       myTarget.IndexTilemap = myTarget.GetTilemapIndexByName(myTarget.CurrentCreatedTilemap.transform.name);
     }
     //Show TilemapList Dropdown
     myTarget.IndexTilemap = EditorGUILayout.Popup("Current selected Tilemap:", myTarget.IndexTilemap, TilemapList.ToArray());
     //Check for new selected Index
     CheckForTilemapIndex();
     //Show Name Input Box for a new Tilemap
     TilemapNewName = EditorGUILayout.TextField("New Tilemap Name:", TilemapNewName);
     //Check if the name is different than Null or Empty signs, and show the Create Button
     if(!String.IsNullOrEmpty(TilemapNewName)){
       if(GUILayout.Button("Create Tilemap \"" + TilemapNewName + "\"")){
         //Call creating Method
         myTarget.CreateTilemap(TilemapNewName);
         //Set the Name back to Empty
         TilemapNewName = "";
         EditorGUI.FocusTextInControl("");
         FillEditorValues();
       }
     }
     //Check if EditorValues are correct
     if(myTarget.DataPerTilemap.Count > 0 && (myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapSizeX != (int)MapSize.x ||
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapSizeY != (int)MapSize.y ||
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX != (int)TileSize.x ||
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY != (int)TileSize.y)){
       FillEditorValues(myTarget.IndexTilemap);
       }
     //This draws a Line to seperate the Controls
     GUILayout.Box(GUIContent.none, GUILayout.Width(Screen.width), GUILayout.Height(2));
     //Check if a Tilemap exists, to show the Tilemap Setups
     if(myTarget.DataPerTilemap.Count > 0){
       //Draw the Default Sprite texture in a Box Area
       GUILayout.BeginArea(new Rect(0, 0, theSprite.width, theSprite.height), theSprite);
      
       //Finish the Sprite texture Area
       GUILayout.EndArea();
       MapSize = EditorGUILayout.Vector2Field("Mapsize:", MapSize);
      
     }
    
    
     GUILayout.Box(GUIContent.none, GUILayout.Width(Screen.width), GUILayout.Height(2));
     //DEBUG Clearing Datas
     if(GUILayout.Button("Clear")){
       myTarget.ClearDatas();
     }
   }
   #endregion

Problem with BeginArea is, that it is positioned on the whole Inspector, and not like other Editor Controls below the Last drawn…
So… is there a way to set the Area Position correctly?

@Chaosgod_Esper

Hi, didn’t read the code, but…

I think beginArea is meant to be used to place layout elements into absolute position, instead of after previous elements.

But shouldn’t you be able to set the position of BeginArea by setting it’s rect first two parameters (x and y position)?

Maybe you can work it out using: GUILayoutUtility.GetLastRect ?

Well, anyway, these were just ideas of the top of my head…

not really working, sincei think GUILayout, EditorGUILayout and GUI Components don´t create Rects that can be received by GUILayoutUtility.GetLastRect…

@Chaosgod_Esper . I’m still not 100% clear on what you wish to draw and where.

In general, what you can do, is to use BeginVertical() & BeginHorizontal() (in EditorGUILayout). Those return a rect value that is the size of the entire section that was defined (see the example in the docs). So you can do something like this:

Rect rect = EditorGUILayout.BeginVertical();
 GUILayout.Button("I'm a button");
 EditorGUILayout.EndVertical();
 //This box will cover all controls between the former BeginVertical() & EndVertical()
 GUI.Box(rect, GUIContent.none);
4 Likes

What i´m trying to do is:

Draw a Texture/Sprite.texture inside the Inspector, within a Box/Area. So i can draw another Area or Lines or Dots, or Text - above it.

Like, Drawing the Sprite, Setting border values, and show Lines above the Texture that illustrate the borders by given values.

@Chaosgod_Esper This actually challenged me, so I had to find a solution :slight_smile:
The key in mixing GUI / GUILayout calls, is to have proper spacing applied to the layout elements.
If you look , you have a GUILayout.Space() function, that does the trick. Here is sample code I used to get a texture displayed in the inspector between two lines.

        Rect boxRect = EditorGUILayout.BeginVertical();
        //This draws a Line to separate the Controls
        GUILayout.Box(GUIContent.none, GUILayout.Width(Screen.width), GUILayout.Height(2));
        EditorGUILayout.EndVertical();
        //Now boxRect is exactly the box. so...
        boxRect.y += 2f;
        //Now boxRect will start right under the line, and will be the width of the line, with height 2
        boxRect.height = m_casted.theSprite.texture.height;
        boxRect.width = m_casted.theSprite.texture.width;
        //Might want to move the X of boxRect so that it is more centered. (your choice)
        GUI.DrawTexture(boxRect, m_casted.theSprite.texture);
        //Now the Key Part, add a space the size of the texture, so that any other GUILayout draw calls
        //will be placed below the texture
        GUILayout.Space(boxRect.height);
        //Draw whatever else you want.
        GUILayout.Box(GUIContent.none, GUILayout.Width(Screen.width), GUILayout.Height(2));

This resulted in the following inspector :

boxRect will have the coordinates of the texture, so you can use that as a reference to draw anything using GUI calls on top of it.

And how to draw lines OVERLAYING the texture? :slight_smile:

This is my current approach:

    if(myTarget.DataPerTilemap.Count > 0){
       //Draw the Default Sprite texture in a Box Area
       GUILayout.BeginArea(new Rect((Screen.width/2)-(theSprite.width/2), 0, theSprite.width, theSprite.height), theSprite);
       //Draw SpriteSize as Red Lines Overlaying the Sprite Area
       Handles.color = Color.red;
       Handles.DrawLine(new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL,0,0), new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY,0));
       Handles.DrawLine(new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX,0,0), new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY,0));
       Handles.DrawLine(new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT, 0), new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT, 0));
       Handles.DrawLine(new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY, 0), new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY, 0));
       Handles.color = Color.white;
       //Finish the Sprite texture Area
       GUILayout.EndArea();
       //Show the Controls to define the Tilesize in pixels
       GUILayout.BeginHorizontal();
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX = EditorGUILayout.IntField("Tile Size:", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX);
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY = EditorGUILayout.IntField("", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY);
       GUILayout.EndHorizontal();
       //Show the Control to define the Offset from the Bottom of the Sprite
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter = EditorGUILayout.IntField("Offset from Bottom:", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter);
       //Calculate the Border Left and Top
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL = (theSprite.width - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX) / 2;
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT = (theSprite.height - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY) - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter;
     }

But cause of the BeginArea, it is wrong positioned:

For sure, i could set the Position of the Area Rect by myself… But that won´t be dynamic… and when i want to change things, the Position is wrong again :confused:

OK
Sry for a Doublepost…

managed to get my working Result with a ScrollView and a Box:

    if(myTarget.DataPerTilemap.Count > 0){
       ScrollViewPos1 = EditorGUILayout.BeginScrollView(ScrollViewPos1, false, false, GUILayout.Height((theSprite.height>128)?128:theSprite.height));
       //Draw background Box
       GUILayout.Space(-16);
       GUILayout.Box(TileSizeBox);
       //Draw the Default Sprite texture in a Box Area
       GUILayout.BeginArea(new Rect((Screen.width/2)-(theSprite.width/2), 0, theSprite.width, theSprite.height), theSprite);
       //Draw SpriteSize as Red Lines Overlaying the Sprite Area, to show current Tile Pixel Size
       Handles.color = BoxSizeColor;
       Handles.DrawLine(new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL,myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT,0), new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT,0));
       Handles.DrawLine(new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX,myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT,0), new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT,0));
       Handles.DrawLine(new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT, 0), new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT, 0));
       Handles.DrawLine(new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY, 0), new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT+myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY, 0));
       Handles.color = Color.white;
       //Finish the Sprite texture Area
       GUILayout.EndArea();
       EditorGUILayout.EndScrollView();
       //Show Control to define the LineColor
       BoxSizeColor = EditorGUILayout.ColorField("Box Line Color:", BoxSizeColor);
       //Show the Controls to define the Tilesize in pixels
       GUILayout.BeginHorizontal();
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX = EditorGUILayout.IntField("Tile Size:", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX, GUILayout.Width((Screen.width/4)*3));
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY = EditorGUILayout.IntField("", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY, GUILayout.Width((Screen.width/4)-23));
       GUILayout.EndHorizontal();
       //Show the Control to define the Offset from the Bottom of the Sprite
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter = EditorGUILayout.IntField("Offset from Bottom:", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter);
       //Calculate the Border Left and Top
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL = (theSprite.width - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX) / 2;
       myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT = (theSprite.height - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY) - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter;
     }