Multiple players accesing a variable globally defined widget

I made a UI that includes a timer, but the widget shows for only up to 1 agent at a time. I think it is because the WidgetsTimer is defined outside the CreateUI function. So when all agents call CreateUI it will return this globally defined widget, but only for 1 agent for whatever reason.

How to show the widget for all players. while mainting my timer functionality where it counts down on the screen?

team_selector := class(creative_device):
    var WidgetsTimer<private>:text_block = text_block{DefaultTextColor:=NamedColors.White}

    UIMessageTimer<private><localizes>(Time:string) : message = "{Time}"
    var Timer<private>:int = 75

.
.

# Spawn the StarTimer function to start a countdown
OnBegin<override>()<suspends>:void={
        spawn{StartTimer()}

.
.

CreateUI<private>(Agent:agent):canvas={
...
set Slots += array{             
            canvas_slot:
                Widget := WidgetsTimer
                ...

        }
...
return canvas{Slots:= Slots}
# Start UI Timer
    StartTimer<private>()<suspends>:void={
        loop:
            set Timer -= 1
            WidgetsTimer.SetText(UIMessageTimer(IntToTime(Timer)))
            Sleep(1.0)
            if(Timer = 0):
                break
    }

For extra detail, here is the entire CreateUI function. I had the same issue with WidgetsTeam and WidgetsCount, but it was fixed by defining them INSIDE CreateUI, rather than globally. But I think I have to keep the Timer global because of the way it counts down using a global variable int.

# Create the UI
    CreateUI<private>(Agent:agent):canvas={
        Print("creating uUI")
        AgentTeam := TeamManager.GetTeam(Agent)

        var WidgetsTeam:[]text_block = array{text_block{},text_block{},text_block{},text_block{}}
        var WidgetsCount:[]text_block = array{text_block{},text_block{},text_block{},text_block{}}
        
        for(X -> Widget:WidgetsCount, TeamWidget := WidgetsTeam[X]):
            TeamCount := TeamManager.GetAgentsInTeam(X + 1).Length
            Widget.SetText(UIMessageCount(TeamCount))
            TeamWidget.SetText(UIMessageTeam(X + 1))
            
            # Set Widget Color
            var Color:color = NamedColors.White
            if(X = AgentTeam - 1, set Color = TeamColors[X]){}             
            Widget.SetTextColor(Color)
            TeamWidget.SetTextColor(Color)
        var Color2:color = NamedColors.White
        if(set Color2 = TeamColors[AgentTeam - 1]){}   

        var Slots:[]canvas_slot = for:
            X -> CountWidget:WidgetsCount
            TeamWidget := WidgetsTeam[X]
            Array : array{
                canvas_slot:
                    Anchors := anchors{Minimum := vector2{X := 0.2 + (0.2 * X), Y := 0.05}, Maximum := vector2{X := 0.2 + (0.2 * X), Y := 0.05}}
                    Alignment := vector2{X := 0.5, Y := 1.0}
                    SizeToContent := true
                    Widget := TeamWidget
                canvas_slot:
                    Anchors := anchors{Minimum := vector2{X := 0.2 + (0.2 * X), Y := 0.05}, Maximum := vector2{X := 0.2 + (0.2 * X), Y := 0.05}}
                    Alignment := vector2{X := 0.5, Y := 0.0}
                    SizeToContent := true
                    Widget := CountWidget
            }
        do: 
            Array

        set Slots += array{
            canvas_slot:
                Anchors := anchors{Minimum := vector2{X := 0.0, Y := 0.0}, Maximum := vector2{X := 1.0, Y := 0.1}}
                Alignment := vector2{X := 0.5, Y := 0.5}
                SizeToContent := true
                Widget := color_block {DefaultColor:=NamedColors.Black, DefaultOpacity:=0.7}                
            canvas_slot:
                Anchors := anchors{Minimum := vector2{X := 0.075, Y := 0.05}, Maximum := vector2{X := 0.075, Y := 0.05}}
                Alignment := vector2{X := 0.5, Y := 0.5}
                SizeToContent := true
                Widget := WidgetsTimer # text_block{DefaultText := UITest}# WidgetsTimer
        }
        Print("slots {Slots.Length}")

        return canvas{Slots:= Slots}
    }

Just a guess, but does the specifier restrict it to one agent?

which specifier

I’m pretty sure that’s what private does. Once its called from playerUI it can no longer be accessed anywhere else.

What if you made multiple versions of the timer?
I don’t know how this would work in code but in devices my thought is you set a timer, that then triggers a relay to push out a start event to an array of timers so that there is 1 timer per player in the game.

To do it in code I think you may want to look into passing additional information by wrapping a subscribe function.
https://forums.unrealengine.com/t/guide-to-event-subscribing-with-additional-parameters-handler-functions/793925

If you figure this out I’d be very curious to know how

1 Like

I fixed it by giving every player their own widget, instead of refering to a global one

1 Like

Was about to say Mark’s last tutorial had a solution there too. Glad you got it working!

Make a UI class that contains your widgets, spawn + assign a new class for each player.

2 Likes

I face the same issue. Spawning separate widgets solved the presentation issue but still all players are accessing the same global variable, in your example that would be “var Timer” with effect that loop would decrease the timer faster than intended e.g. if there are two players, then Timer would decrease by 2 every seconds.

I know this is seriously janky but could you have the timer decrease at (1/P)*1 sec where P is players?
In theory having your global variable scale to the player count.

This is the correct approach

it’s over for me, I have the same problem and i transferred the canvas method + show/remove into another class, but now I have to transfer all the widgets, the problem is: i work with them inside the device class… How do I even edit all the players widgets