0
\$\begingroup\$

I am coding my UI in Unity C# with the UI Toolkit, and as I write more and more my code is getting quite annoying in terms of one particular issue.

I find myself repeating many similar lines of code but having to change the name of the VisualElement object I am referring to manually each time which is annoying and wasteful. If I change a VisualElement name, I also have to do a find/replace to get all the lines I described it and be sure I don't pick up anything else by mistake.

For example, if I'm putting together a few VisualElement objects, it might look like:

        VisualElement visualElement1 = new VisualElement();
        visualElement1.style.width = new Length(100, LengthUnit.Percent);
        visualElement1.style.height = 500;
        visualElement1.style.flexDirection = FlexDirection.Row;
        visualElement1.pickingMode = PickingMode.Ignore;
        visualElement1.style.position = Position.Absolute;
        visualElement1.style.bottom = 200;

        VisualElement visualElement2 = new VisualElement();
        visualElement2.style.width = visualElement2.style.height = 500;
        visualElement2.style.marginLeft = 150;
        visualElement2.style.backgroundColor = Color.white;
        visualElement2.style.opacity = 0.6f;
        visualElement2.style.borderBottomLeftRadius = visualElement2.style.borderBottomRightRadius = visualElement2.style.borderTopLeftRadius = visualElement2.style.borderTopRightRadius = 200;

        visualElement2.Add(visualElement1);

So much of the code becomes repetitive in style that it would be much faster to copy and paste some settings lines from one element to the next rather than having to change the names or type them from scratch.

For example, what I would like to do is this:

        VisualElement *vePtr;

        VisualElement visualElement1 = new VisualElement();
        *vePtr = &visualElement1;
        *vePtr.style.width = new Length(100, LengthUnit.Percent);
        *vePtr.style.height = 500;
        *vePtr.style.flexDirection = FlexDirection.Row;
        *vePtr.pickingMode = PickingMode.Ignore;
        *vePtr.style.position = Position.Absolute;
        *vePtr.style.bottom = 200;

        VisualElement visualElement2 = new VisualElement();
        *vePtr = &visualElement2;
        *vePtr.style.width = *vePtr.style.height = 500;
        *vePtr.style.marginLeft = 150;
        *vePtr.style.backgroundColor = Color.white;
        *vePtr.style.opacity = 0.6f;
        *vePtr.style.borderBottomLeftRadius = *vePtr.style.borderBottomRightRadius = *vePtr.style.borderTopLeftRadius = *vePtr.style.borderTopRightRadius = 200;

        visualElement2.Add(visualElement1);

If I could use a pointer like this and just reassign the pointer after creating each new element, then I can copy and paste my settings lines from one element to another very quickly and I don't have to re-write the whole line or change the name of the object it's referring to over over. If I change an object's name, I only have to do it once or twice as well.

For example, if I want the long border radius statement on Element2 to also apply to Element1, I can easily just copy and paste the line to where Element1 was created and change 200 to whatever I need, without having to rewrite or edit the whole line.

The problem is this doesn't actually work. I am told "Pointers and fixed size buffers can only be used in an unsafe context" and "Cannot take the address of, get the size of, or declare a pointer to a managed type."

Is there some way to accomplish what I am trying to do above and make my life a bit easier or more flexible?

Thanks for any ideas.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

First of all, you don't need a pointer here, because VisualElement is a class, which means that every variable of type VisualElement is already a reference. For example:

 VisualElement visualElement1 = new VisualElement();
 VisualElement visualElement2 = visualElement1;

Now both variables visualElement1 and visualElement2 point to the same object. When you make a change through one of those references, then the change will be visible in the other.

But this does not really solve the actual problem, which is a whole lot of redundant code.

When you have a lot of objects which you want to have the same style, then it is a good idea to use a StyleSheet for that purpose. Setting visualElement.style.* directly via code should only be done when you have a good reason to calculate that property at runtime.

The same applies to layouting. When you feel the need to do a lot of layouting by calculating positions and dimensions of UI elements via C# code, then that's usually a sign that you could benefit from learning more about the automatic layouting capabilities of UIElements. I have no idea what you are actually trying to achieve, but unless it's something really unusual, it probably can be done with the layout engine. When you have a specific use-case where you are unsure how it can be done automatically, please open a new question.

And if you want to define templates for certain repeating UI elements, but don't want to use the visual editor, then you can define the UI in XML code instead of C# code.

OK, but what if all of this fails and you really, really have to use C# to set certain UI properties on multiple elements? In that case it is a good way to do that via a helper-method:

public static void SetColorForTeam(VisualElement element, PlayerTeam team) {
    element.color = team.brightColor;
    element.backgroundColor = team.darkColor;
    element.borderLeftColor = team.brightColor;
    element.borderRightColor = team.brightColor;
    element.borderTopColor = team.brightColor;
    element.borderBottomColor = team.brightColor;
}

...
SetColorForTeam(nameTagLocal, localPlayer.team);
SetColorForTeam(scoreDisplayLocal, localPlayer.team);
SetColorForTeam(nameTagOpponent, opponentPlayer.team);
SetColorForTeam(scoreDisplayOpponent, opponentPlayer.team);
\$\endgroup\$
8
  • \$\begingroup\$ Thanks Philipp. Is there any way to make a reference into a new separate object? ie. if you have VisualElement veTmp = new VisualElement(); veTmp.style.width=500; VisualElement ve1 = new VisualElement(); ve1 = veTmp; veTmp.style.width = 400; VisualElement ve2 = new VisualElement(); ve2 = veTmp; I suspect that won't work either but I wonder if it's possible to "copy over" from veTmp to ve1 and ve2 so they each get their own widths here but I can keep modifying veTmp to get the coding simplicity I'm looking for. Or could I build a function to copy over like that? Thanks again. \$\endgroup\$
    – mike
    Commented Mar 31, 2022 at 10:45
  • \$\begingroup\$ Unfortunately most of the code that's annoying me is custom stuff for laying out interface, so style sheets and those types of helper functions are not as directly helpful. I just want to be able to use the same "name" for most lines like veTmp or vePtr as it would make it much faster to write/edit if possible. I have certainly implemented functions like you describe in other places where I'm making many of the same things and that does help with that. Thanks. \$\endgroup\$
    – mike
    Commented Mar 31, 2022 at 10:48
  • 1
    \$\begingroup\$ @mike When you feel the need to do a lot of layouting via code, then that's usually a sign that you could benefit from learning more about the automatic layouting capabilities of UIElements. I have no idea what you are actually trying to achieve, but unless it's something really unusual, it probably can be done with the layout engine. When you have a specific use-case where you are unsure how it can be done automatically, please open a new question. \$\endgroup\$
    – Philipp
    Commented Mar 31, 2022 at 10:52
  • \$\begingroup\$ @mike But if you really want to copy all the properties from one element to another, then ve1.style = ve2.style could work. \$\endgroup\$
    – Philipp
    Commented Mar 31, 2022 at 10:54
  • \$\begingroup\$ Thanks Philipp. My UI must be built programmatically as it features things like user profiles and other things that must build on the fly. I have experimented with the UI Builder and it's far too rudimentary and unstable for my purposes. Programmatic is the only solution and is working well. It's just a bit annoying to code this way. I'll try the Temp approach then and if that doesn't work I'll stick with what I'm doing. Thanks again. \$\endgroup\$
    – mike
    Commented Mar 31, 2022 at 22:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .