I’ve been struggling with this for more than 6 months, and it looks like there’s no native solution I could utilise to solve this problem.
so basically I’m working on a project that has pre-programmed and pre-designed menu widgets, and I’m implementing a new layout system for these widgets that would change their layouts based on some factory function I implemented, this function needs another query function to track live widgets in Game Viewport at runtime; here’s how it’s implemented:
void UBidirectionalLayoutSubsystem::UpdateViewportWidgets(TArray<UWidget*>& OutViewportWidgets)
{
if (!OutViewportWidgets.IsEmpty())
OutViewportWidgets.Empty();
if (GEngine == nullptr) return;
for (TObjectIterator<UWidget> Itr; Itr; ++Itr)
{
if (UWidget* Widget = *Itr)
{
if (!Widget->IsRendered() || Widget->GetWorld() != GEngine->GetCurrentPlayWorld())
continue;
OutViewportWidgets.Add(Widget);
}
}
}
It’s not really the implementation I’m struggling with here, but rather, performance. You see, iterating through so many widgets at runtime would heavily impact performance if called on a Tick basis (even with 1 sec interval), so I had to implement an invalidation system where UpdateViewportWidgets ONLY gets called when certain UI-related events are fired; Among such events are FWidgetAdded and FWidgetRemoved events from UGameViewportSubsystem class, which are invoked when a UserWidget is added to or removed from Game Viewport (usually through AddToViewport method), which is good and all, but it’s not serving my case any better, as I want to track all live widgets in Game viewport, not just Top Level widgets, i.e. is there any widget added or removed from any panel slot in the active game viewport?.. etc.
Here’s what I have tried so far :
- I’ve tried simulating such an event by ticking UpdateViewportWidgets and creating a TArray<UWidget> CachedViewportWidgets* property that would only get populated when its number of items is not equal to the resulted array from UpdateViewportWidgets function, then I do basic equality checks on both arrays and broadcast a custom delegate when the cached former array is resized.
Didn’t take long for me to realize how stupid this attempt was, as performance went to ■■■■ when processing a menu of so many children widgets at runtime.
- I’ve also tried implementing an async version of this function through FRunnable interface. I’m not even going to get into how racing conditions were all over the place when I tested this that I ditched this idea all together.
My question is, does the engine provide any method or library that would help me implement such an event?
Thank you for your time reading this, and any help is certainly appreciated.