Accessing elements by string - best or bad practice? Alternatives?

With UI Toolkit I see a lot of code that is commonly considered bad practice when it comes to referencing game objects: finding and accessing visual elements / game objects by string.

In UI Toolkit that’s either the name of the element (eg “#ThisButton”) or class name (eg “.this-button”). For GameObjects the equivalent would be GameObject.Find(“ThisButton”) or GameObject.FindWithTag(“ButtonTag”). We don’t use the latter two, do we not? :wink:

I still see the same reference-by-string issues arising in both scenarios:

  • designer changes an element’s name, code breaks.
  • designer is hesitant to change a style/element name because it might break code.
  • designers become confused by style/element names not matchin expectation or the (recently updated) naming conventions.
  • programmer would really like to refactor a style/element name but isn’t comfortable going through all UXML documents.
  • programmers become confused because while their script identifier names are easily refactored, the element/style names are not and increasingly differ from expectations.

That’s why I feel anxious to (having to) take that reference-by-string route because it seemingly goes against established best practices. I’m just starting with UI Toolkit so I’m interested to hear your experience and thoughts on that matter, and maybe I’ve entirely missed another option of referencing elements in scripts?

Please also share if you have clever solutions or workflow tips that help in UI Toolkit workflows, specifically the link between scripts and UI Documents. I found one right here and am looking for more like these. Thanks!

Hi @CodeSmile ,

we took the reference-by-string route and so far, we are happy with it.
We don’t use any custom components, but rather bundle certain functionality into Views. ViewModels are then bound to Views. ViewModels expose all the dynamic data (e.g. the text of a label) using Observables (with UniRx). All actions (e.g. Button clicks) are bound to a method in the ViewModel.

The following two gists contain the Binding and View/ViewModel stuff:

Regarding the reference-by-string:
We cam up with an annotation to simplify the look-up, so that you can write code like this:

public class MainMenuView : View<MainMenuViewModel>
{
    [Query("root")] private VisualElement _root;
    [Query("start-game")] private Button _startGameButton;
    [Query("continue-game")] private Button _continueButton;
    [Query("abandon-run")] private Button _abandonRunButton;

    // ...
}

@magnetic_scho Thanks for the pointers!
I believe I ran into these UniRx bindings, not sure if they’re the same scripts but I’ll look into it. Though I’m hesitating to add another complex tool to the task.

I’ve also found a comment regarding reference-by-string from half a year ago, that explains the status quo and that (runtime) bindings will improve the workflow.

@CodeSmile :
I can understand that you hesitate to add UniRx. I’d say it depends on whether your underlaying game model has a lot of state changes or not. If so, it makes it much easier in my opinion to propagate these changes back to the UI and also combine various changes.

Regarding the comment:
I’m really curious how the runtime binding API will look like. In think, it’s not only binding a class with some fields to an UXML file. You need to be able to change all the things (text, style, classes, rotation, positioning, etc.) and also have a place to do data conversion (Enum => uss class name, localisation, int => damage class green/orange/red, etc.).

1 Like

@magnetic_scho I’m beginning to like this approach! UniRx isn’t that complex and I’ll have a lot of state changes.

Is the gist code you posted free to use for any purpose (license)? Doesn’t have a license header.

@CodeSmile :
It’s free to use. I might add a license file at some point.

1 Like