Search Unity

Editor: Draw Box/Area over Box/Area

Discussion in 'Immediate Mode GUI (IMGUI)' started by Chaosgod_Esper, Apr 6, 2016.

  1. Chaosgod_Esper

    Chaosgod_Esper

    Joined:
    Oct 25, 2012
    Posts:
    295
    hi there :)

    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?

    :)
    thanks for reading.
     
  2. Chaosgod_Esper

    Chaosgod_Esper

    Joined:
    Oct 25, 2012
    Posts:
    295
    still in Need of this :(
     
  3. Chaosgod_Esper

    Chaosgod_Esper

    Joined:
    Oct 25, 2012
    Posts:
    295
    Pushed this Question to the Answers, hopeing for help there :(
     
  4. IzzySoft

    IzzySoft

    Joined:
    Feb 11, 2013
    Posts:
    376
    a mockup/example of what you tried so far might help. (small part of the code)
     
    eses likes this.
  5. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    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.
     
    eses likes this.
  6. Chaosgod_Esper

    Chaosgod_Esper

    Joined:
    Oct 25, 2012
    Posts:
    295
    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):
    Code (csharp):
    1.  
    2.   #region Show The Inspector GUI
    3.    private void ShowControls(){
    4.      ListTilemaps();
    5.      //Show Default Prefab Field
    6.      myTarget.DefaultSpriteObject = (GameObject)EditorGUILayout.ObjectField("Default Prefab:", myTarget.DefaultSpriteObject, typeof(GameObject), true);
    7.      //Get the current Tilemap Index first (if possible)!
    8.      if(myTarget.CurrentCreatedTilemap != null){
    9.        myTarget.IndexTilemap = myTarget.GetTilemapIndexByName(myTarget.CurrentCreatedTilemap.transform.name);
    10.      }
    11.      //Show TilemapList Dropdown
    12.      myTarget.IndexTilemap = EditorGUILayout.Popup("Current selected Tilemap:", myTarget.IndexTilemap, TilemapList.ToArray());
    13.      //Check for new selected Index
    14.      CheckForTilemapIndex();
    15.      //Show Name Input Box for a new Tilemap
    16.      TilemapNewName = EditorGUILayout.TextField("New Tilemap Name:", TilemapNewName);
    17.      //Check if the name is different than Null or Empty signs, and show the Create Button
    18.      if(!String.IsNullOrEmpty(TilemapNewName)){
    19.        if(GUILayout.Button("Create Tilemap \"" + TilemapNewName + "\"")){
    20.          //Call creating Method
    21.          myTarget.CreateTilemap(TilemapNewName);
    22.          //Set the Name back to Empty
    23.          TilemapNewName = "";
    24.          EditorGUI.FocusTextInControl("");
    25.          FillEditorValues();
    26.        }
    27.      }
    28.      //Check if EditorValues are correct
    29.      if(myTarget.DataPerTilemap.Count > 0 && (myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapSizeX != (int)MapSize.x ||
    30.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapSizeY != (int)MapSize.y ||
    31.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX != (int)TileSize.x ||
    32.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY != (int)TileSize.y)){
    33.        FillEditorValues(myTarget.IndexTilemap);
    34.        }
    35.      //This draws a Line to seperate the Controls
    36.      GUILayout.Box(GUIContent.none, GUILayout.Width(Screen.width), GUILayout.Height(2));
    37.      //Check if a Tilemap exists, to show the Tilemap Setups
    38.      if(myTarget.DataPerTilemap.Count > 0){
    39.        //Draw the Default Sprite texture in a Box Area
    40.        GUILayout.BeginArea(new Rect(0, 0, theSprite.width, theSprite.height), theSprite);
    41.      
    42.        //Finish the Sprite texture Area
    43.        GUILayout.EndArea();
    44.        MapSize = EditorGUILayout.Vector2Field("Mapsize:", MapSize);
    45.      
    46.      }
    47.    
    48.    
    49.      GUILayout.Box(GUIContent.none, GUILayout.Width(Screen.width), GUILayout.Height(2));
    50.      //DEBUG Clearing Datas
    51.      if(GUILayout.Button("Clear")){
    52.        myTarget.ClearDatas();
    53.      }
    54.    }
    55.    #endregion
    56.  
    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?
     
  7. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @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...
     
  8. Chaosgod_Esper

    Chaosgod_Esper

    Joined:
    Oct 25, 2012
    Posts:
    295
    not really working, sincei think GUILayout, EditorGUILayout and GUI Components don´t create Rects that can be received by GUILayoutUtility.GetLastRect...
     
  9. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    @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:

    Code (CSharp):
    1. Rect rect = EditorGUILayout.BeginVertical();
    2.  GUILayout.Button("I'm a button");
    3.  EditorGUILayout.EndVertical();
    4.  //This box will cover all controls between the former BeginVertical() & EndVertical()
    5.  GUI.Box(rect, GUIContent.none);
     
  10. Chaosgod_Esper

    Chaosgod_Esper

    Joined:
    Oct 25, 2012
    Posts:
    295
    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.
     
  11. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    @Chaosgod_Esper This actually challenged me, so I had to find a solution :)
    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.

    Code (CSharp):
    1.  
    2.         Rect boxRect = EditorGUILayout.BeginVertical();
    3.         //This draws a Line to separate the Controls
    4.         GUILayout.Box(GUIContent.none, GUILayout.Width(Screen.width), GUILayout.Height(2));
    5.         EditorGUILayout.EndVertical();
    6.         //Now boxRect is exactly the box. so...
    7.         boxRect.y += 2f;
    8.         //Now boxRect will start right under the line, and will be the width of the line, with height 2
    9.         boxRect.height = m_casted.theSprite.texture.height;
    10.         boxRect.width = m_casted.theSprite.texture.width;
    11.         //Might want to move the X of boxRect so that it is more centered. (your choice)
    12.         GUI.DrawTexture(boxRect, m_casted.theSprite.texture);
    13.         //Now the Key Part, add a space the size of the texture, so that any other GUILayout draw calls
    14.         //will be placed below the texture
    15.         GUILayout.Space(boxRect.height);
    16.         //Draw whatever else you want.
    17.         GUILayout.Box(GUIContent.none, GUILayout.Width(Screen.width), GUILayout.Height(2));
    18.  
    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.
     
    Last edited: May 17, 2016
  12. Chaosgod_Esper

    Chaosgod_Esper

    Joined:
    Oct 25, 2012
    Posts:
    295
    And how to draw lines OVERLAYING the texture? :)

    This is my current approach:
    Code (csharp):
    1.  
    2.     if(myTarget.DataPerTilemap.Count > 0){
    3.        //Draw the Default Sprite texture in a Box Area
    4.        GUILayout.BeginArea(new Rect((Screen.width/2)-(theSprite.width/2), 0, theSprite.width, theSprite.height), theSprite);
    5.        //Draw SpriteSize as Red Lines Overlaying the Sprite Area
    6.        Handles.color = Color.red;
    7.        Handles.DrawLine(new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL,0,0), new Vector3(myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL, myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY,0));
    8.        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));
    9.        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));
    10.        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));
    11.        Handles.color = Color.white;
    12.        //Finish the Sprite texture Area
    13.        GUILayout.EndArea();
    14.        //Show the Controls to define the Tilesize in pixels
    15.        GUILayout.BeginHorizontal();
    16.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX = EditorGUILayout.IntField("Tile Size:", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX);
    17.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY = EditorGUILayout.IntField("", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY);
    18.        GUILayout.EndHorizontal();
    19.        //Show the Control to define the Offset from the Bottom of the Sprite
    20.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter = EditorGUILayout.IntField("Offset from Bottom:", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter);
    21.        //Calculate the Border Left and Top
    22.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL = (theSprite.width - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX) / 2;
    23.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT = (theSprite.height - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY) - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter;
    24.      }
    25.  
    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 :/
     
    Last edited: May 17, 2016
  13. Chaosgod_Esper

    Chaosgod_Esper

    Joined:
    Oct 25, 2012
    Posts:
    295
    OK
    Sry for a Doublepost..

    managed to get my working Result with a ScrollView and a Box:
    Code (csharp):
    1.  
    2.     if(myTarget.DataPerTilemap.Count > 0){
    3.        ScrollViewPos1 = EditorGUILayout.BeginScrollView(ScrollViewPos1, false, false, GUILayout.Height((theSprite.height>128)?128:theSprite.height));
    4.        //Draw background Box
    5.        GUILayout.Space(-16);
    6.        GUILayout.Box(TileSizeBox);
    7.        //Draw the Default Sprite texture in a Box Area
    8.        GUILayout.BeginArea(new Rect((Screen.width/2)-(theSprite.width/2), 0, theSprite.width, theSprite.height), theSprite);
    9.        //Draw SpriteSize as Red Lines Overlaying the Sprite Area, to show current Tile Pixel Size
    10.        Handles.color = BoxSizeColor;
    11.        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));
    12.        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));
    13.        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));
    14.        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));
    15.        Handles.color = Color.white;
    16.        //Finish the Sprite texture Area
    17.        GUILayout.EndArea();
    18.        EditorGUILayout.EndScrollView();
    19.        //Show Control to define the LineColor
    20.        BoxSizeColor = EditorGUILayout.ColorField("Box Line Color:", BoxSizeColor);
    21.        //Show the Controls to define the Tilesize in pixels
    22.        GUILayout.BeginHorizontal();
    23.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX = EditorGUILayout.IntField("Tile Size:", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX, GUILayout.Width((Screen.width/4)*3));
    24.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY = EditorGUILayout.IntField("", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY, GUILayout.Width((Screen.width/4)-23));
    25.        GUILayout.EndHorizontal();
    26.        //Show the Control to define the Offset from the Bottom of the Sprite
    27.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter = EditorGUILayout.IntField("Offset from Bottom:", myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter);
    28.        //Calculate the Border Left and Top
    29.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderL = (theSprite.width - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeX) / 2;
    30.        myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileborderT = (theSprite.height - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTilesizeY) - myTarget.DataPerTilemap[myTarget.IndexTilemap].TilemapTileOffsetCenter;
    31.      }
    32.