📜 ⬆️ ⬇️

As I stopped worrying and started cutting rectangles in Unity correctly

In my previous article I promised to tell my way of working with rectangles . Developing OneLine , I wrote several extensions of the Rect class, which significantly simplify the work with the GUI. Now I have isolated them into a separate library: RectEx .


Details under the cut.


The essence of the problem


When we write PropertyDrawer in Unity, we are forced to use the GUI class (instead of GUILayout), which means working with hand marking. The code is cluttered with the set of new Rect(...) and rect.y += rect.height + 5 , complicated for reading and changes. When magic numbers are mixed in (further examples from the Internet), the code becomes so inert that each new change is perceived by the programmer as a mockery from the game designer.


For a long time I put up with the problem, just trying not to do too bad things. But when I started developing OneLine , I also wrote a number of extensions for the Rect class, simplifying the routine work.


How do people do it


On the Internet, I found many ways to cut rectangles in tutorials and source codes on githubs. Next comes a small selection. Will you find your favorite among them? If not, write in the comments of your version, I will add to the article.


I found the rules at my own discretion in order to remove all unnecessary and make them clearer.


Preparing rectangles in advance


The official version of the documentation
 // Calculate rects var amountRect = new Rect (position.x, position.y, 30, position.height); var unitRect = new Rect (position.x+35, position.y, 50, position.height); var nameRect = new Rect (position.x+90, position.y, position.width-90, position.height); // Draw fields - passs GUIContent.none to each so they are drawn without labels EditorGUI.PropertyField (amountRect, property.FindPropertyRelative ("amount"), GUIContent.none); EditorGUI.PropertyField (unitRect, property.FindPropertyRelative ("unit"), GUIContent.none); EditorGUI.PropertyField (nameRect, property.FindPropertyRelative ("name"), GUIContent.none); 

The source is here .


Another option from a very beautiful tutorial.
 Rect minRect = new Rect(position.x, position.y, position.width * 0.4f - 5, position.height); Rect mirroredRect = new Rect(position.x + position.width * position.y, position.width * 0.2f, position.height); Rect maxRect = new Rect(position.x + position.width * 0.6f + 5, position.y, position.width * 0.4f - 5, position.height); 

The source is here .


All the same, but with sugar
 var firstRect = new Rect(position){ width = position.width / 2 }; var secondRect = new Rect(position){ x = position.x + position.width / 2, width = position.width / 2 }; EditorGUI.PropertyField(firstRect, property.FindPropertyRelative("first")); EditorGUI.PropertyField(secondRect, property.FindPropertyRelative("second")); 

The source is here .


Well, tutorials are good when they are taught to do one thing, and do not contain all the best practices . Specifically, these teach ponakidit more magic numbers.


The same, but without magic numbers
 float curveWidth = 50; var sliderRect = new Rect (rect.x, rect.y, rect.width - curveWidth, rect.height) EditorGUI.Slider (sliderRect, scale, min, max, label); var curveRect = new Rect (rect.width - curveWidth, rect.y, curveWidth, rect.height); EditorGUI.PropertyField (curveRect, curve, GUIContent.none); 

The source is here .


This code is hard to read in cases where a large number of properties are drawn.


This code is hard to maintain. Even if we draw three properties and suddenly you need to add a fourth / fifth.


However, there is a better way!


One rectangle: drew => moved


An example from decompiled Unity libraries
 float count = labels.Length; float space = 2; float width = (position.width - (count - 1) * space) / count; position.width = num2; for (int i = 0; i < count; i++){ EditorGUI.PropertyField(position, properties[i], labels[i]); position.x += count + space; } 

Source here


Another example from Unity
 public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label) { Rect position = rect; float height = EditorGUIUtility.singleLineHeight; float space = EditorGUIUtility.standardVerticalSpacing; position.height = height; var property = prop.FindPropertyRelative("m_NormalColor"); var property2 = prop.FindPropertyRelative("m_HighlightedColor"); var property3 = prop.FindPropertyRelative("m_PressedColor"); var property4 = prop.FindPropertyRelative("m_DisabledColor"); var property5 = prop.FindPropertyRelative("m_ColorMultiplier"); var property6 = prop.FindPropertyRelative("m_FadeDuration"); EditorGUI.PropertyField(position, property); position.y += height + space; EditorGUI.PropertyField(position, property2); position.y += height + space; EditorGUI.PropertyField(position, property3); position.y += height + space; EditorGUI.PropertyField(position, property4); position.y += height + space; EditorGUI.PropertyField(position, property5); position.y += height + space; EditorGUI.PropertyField(position, property6); } 

The source is here .


This time the code is read much better, and it will be somewhat easier to maintain it. However, everything looks so good only as long as all the fields in the class have the same size on the screen (in the first example, the width is divided into equal parts, in the second example - the height).


This code is much more complicated if you need to draw elements of different sizes.


How to do it with RectEx


RectEx adds several methods that extend the Rect class, but two are most useful: Column and Row .


Why such strange names?


At first I called them SplitVertically and SplitHorizontally. It turned out too long, inconvenient, but still not read.


I tried SplitV and SplitH. It turned out shorter and more convenient. However, you constantly forget what each of them does? One cuts horizontal lines, the other - vertical. Or does one return a horizontal column, the other a vertical one?


Mathematics, as it were, gentlemen of Vector-Column and Vector-Line (both words with a big one, because gentlemen have two surnames) came to the rescue as always. Looking at rect.Row(5) you immediately realize that the method returns a row, and rect.Column(5) a column.


Demonstrations follow.


We cut into three equal parts with vertical lines
 var rects = rect.Row(3); EditorGUI.PropertyField(rects[0], property.FindPropertyRelative("first")); EditorGUI.PropertyField(rects[1], property.FindPropertyRelative("second")); EditorGUI.PropertyField(rects[2], property.FindPropertyRelative("third")); 

We cut into three equal parts horizontal lines

I added i++ to make it easier to swap lines.


 var rects = rect.Column(3); int i = 0; EditorGUI.PropertyField(rects[i++], property.FindPropertyRelative("first")); EditorGUI.PropertyField(rects[i++], property.FindPropertyRelative("second")); EditorGUI.PropertyField(rects[i++], property.FindPropertyRelative("third")); 

Elements of different sizes

In this example, we pass the relative weights to the Column method, on the basis of which we obtain: the second element is two times the first, and the third is three.


 var rects = rect.Column(new float[]{1, 2, 3}); EditorGUI.PropertyField(rects[0], property.FindPropertyRelative("first")); EditorGUI.PropertyField(rects[1], property.FindPropertyRelative("second")); EditorGUI.PropertyField(rects[2], property.FindPropertyRelative("third")); 

For clarity, I drew two symmetrical pictures in which I tried to show an example of using the Raw and Column methods (clickable pictures).


Using the Row Method


Using the Column method

The second picture is just the first transposed: the rows became columns. But I decided that it was worth drawing it.



Where to get?


Current version: v0.1.0 .
You can try on githaba . The readme describes the other methods.


')

Source: https://habr.com/ru/post/340858/


All Articles