Variable doesn't seem to change in a loop

Hi, I have a problem with a variable that it’s called on a loop in the OnBegin, for some reason it doesn’t seem to change even when I change the value in other functions.

In this example I have a TeleportNow var that defaults to false, on the OnBegin it checks in a loop every second if the variable is true, the problem is that never happens even when I change it to true in another function. (the loop in the OnBegin is the only way to make these devices work)
image

In your example code you are doing this:

spawn_info := class(creative_device):
    var TeleportNow<public>:logic = false

    OnBegin<override>()<suspends>:void =
        # PROBLEM: You're creating a new instance of spawn_info when you intended to
        # manipulate the values on the current instance instead.
        test:spawn_info = spawn_info{}
        loop:
            # Since you're referencing "test.TeleportNow" which was just constructed, the
            # value will always be false (the default for the TeleportNow variable).
            if (test.TeleportNow = true):
                #...
spawn_info := class(creative_device):
    var TeleportNow<public>:logic = false

    OnBegin<override>()<suspends>:void =
        # >>DELETE THE BELOW LINE
        # test:spawn_info = spawn_info{}
        loop:
            # Change from "test.TeleportNow" to "TeleportNow"
            if (TeleportNow = true):
                #...

Thank you for the response! I did that but for some reason the value stays false, if you can help me it would be really helpful, I’ve been trying to fix this for at least 2 days or 15-20 hours in the editor.

This is the cleaned up and updated code of the spawn_info class:

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

spawn_info := class(creative_device):
    var TeleportNow<public> : logic = false
    var Map : int = 0
    
    #Spawns for the map
    #MAP: Farm (temporary name)
    @editable
    Spawns_Farm: []teleporter_device := array{}

    OnBegin<override>()<suspends> : void =
        loop:
            Sleep(1.0)
            if (TeleportNow = true):
                for (Index -> Player:GetPlayspace().GetPlayers()):
                    if (Map = 0):
                        if (Players := Player, Farm := Spawns_Farm[Index]):
                            Farm.Teleport(Players)
                set TeleportNow = false

    #This is being called in another class called game_info
    TeleportToMap<public>(SelectedMap:int) : void =
        set Map = SelectedMap
        set TeleportNow = true

Here is the function that calls it after the countdown ends in the game_info class:

StartGame<public>() : void =
        Print("Selecting Roles")
        SelectRoles()

        Print("Selecting Map")
        Selection := GetRandomInt(0, 0)

        Print("Teleporting the players to the Map")
        var info : spawn_info = spawn_info{}
        info.TeleportToMap(Selection)

As I said before the value of TeleportNow remains false and doesn’t want to change. Other than that I would want to know what’s the best way to call a variable/function in another class without creating a new instance if that’s possible. Thank you again for the help!

Looking at the second piece of code I suspect it is the same problem. Here you are creating a new instance of the spawn_info which you are calling TeleportToMap(…) on, instead of referencing the instance you want to manipulate.

StartGame<public>() : void =
        Print("Selecting Roles")
        SelectRoles()

        Print("Selecting Map")
        Selection := GetRandomInt(0, 0)

        Print("Teleporting the players to the Map")
        var info : spawn_info = spawn_info{} # <-- Constructor creating a new instance
        info.TeleportToMap(Selection) # <-- Invoking TeleportToMap on the new instance, not the one you want.

Where is the StartGame<public>():void function located? If it is on another Verse device then what you need to do is something like this.

start_game_device := class(creative_device):
    @editable
    SpawnInfo:spawn_info = spawn_info{}

    StartGame<public>():void =
        #...
        SpawnInfo.TeleportToMap(Selection)

Then from UEFN you need to

  1. Compile verse (Ctrl+Shift+B)
  2. Select your StartGameDevice in the world
  3. Find the SpawnInfo property in the details panel
  4. Select your SpawnInfo instance from the drop down

Thanks for the response, I can say that I tried it and it still didn’t want to change the variable, I’ll try to be more in depth this time, it’s not the best code but I tried:

StartGame is called here in the class countdown_timer:

using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /Verse.org/Colors }

MakeCountdownTimer<constructor><public>(MaxTime : float, InPlayer : agent) := countdown_timer:
    RemainingTime := MaxTime
    MaybePlayerUI := option{GetPlayerUI[player[InPlayer]]}

countdown_timer := class(creative_device):
    @editable
    GameInfo : game_info = game_info{}

    RunCountdown<private>()<suspends> : void =
        loop:
            Sleep(TimerTickPeriod)
            set RemainingTime -= TimerTickPeriod
            UpdateUI()

            var PlayerCount : int = 0
            for (PlayerNumber : GetPlayspace().GetPlayers()):
                set PlayerCount += 1
            var Waiting : countdown_timer_global = countdown_timer_global{}
            if (PlayerCount < 1):
                set RemainingTime = 0.0

            if (RemainingTime <= 0.0):
                if (PlayerCount >= 1):
                    GameInfo.StartGame() #HERE

                    RemainingTimeTextBlock.SetText(GameStartedText())
                    Sleep(2.0)
                
                Canvas.RemoveWidget(RemainingTimeTextBlock)

                if (UI := MaybePlayerUI?):
                    UI.RemoveWidget(Canvas)
                
                if (PlayerCount < 1):
                    Waiting.WaitingForPlayers()
                    Print("There are {PlayerCount} players, not enough to start the game.")

                break
    
    UpdateUI<private>() : void =
        if (IntTime := Int[RemainingTime]):
            RemainingTimeTextBlock.SetText(RemainingTimeText(IntTime))

The StartGame function now looks something like this: (it’s in the game_info class)

using { /Fortnite.com/Devices }
using { /Fortnite.com/Playspaces }
using { /Verse.org/Native }
using { /Verse.org/Random }
using { /Verse.org/Simulation }
using { /Fortnite.com/Game }
using { /Fortnite.com/Characters }
using { /UnrealEngine.com/Temporary/Diagnostics }

game_info := class<concrete>(creative_device):
    @editable
    SpawnInfo : spawn_info = spawn_info{}

    StartGame<public>() : void =
        Print("Selecting Roles")
        SelectRoles()

        Print("Selecting Map")
        Selection := GetRandomInt(0, 0)

        Print("Teleporting the players to the Map")
        SpawnInfo.TeleportToMap(Selection)

The TeleportToMap function is another class callled spawn_info and I already sent it but I’ll send it again:

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

spawn_info := class(creative_device):
    var TeleportNow<public> : logic = false
    var Map : int = 0

    @editable
    Spawns_Farm: []teleporter_device := array{}

    OnBegin<override>()<suspends> : void =
        loop:
            Sleep(1.0)
            if (TeleportNow = true):
                Print("test3")
                for (Index -> Player:GetPlayspace().GetPlayers()):
                    if (Map = 0):
                        if (Players := Player, Farm := Spawns_Farm[Index]):
                            Farm.Teleport(Players)
                set TeleportNow = false

    TeleportToMap<public>(SelectedMap:int) : void =
        set Map = SelectedMap
        set TeleportNow = true
        if (TeleportNow = true):
            Print("true")
        else:
            Print("false")

The code doesn’t get past the “if (TeleportNow = true)” because TeleportNow still remains false even tho TeleportToMap is in the same class, can it be a problem if it’s in on the OnBegin and in a loop? I’ve been trying to fix it for so long that I almost lost any hope :confused:

I took the code you provided and paired it down to just the essentials to get things working. Below is a recap of the setup.

Code

countdown_timer := class(creative_device):
    @editable
    GameInfo:game_info = game_info{}

    OnBegin<override>()<suspends>:void =
        Print("CountdownTimer.OnBegin")
        RunCountdown()

    RunCountdown<private>()<suspends> : void =
        Print("CountdownTimer.RunCountdown")
        loop:
            Sleep(2.0)
            PlayerCount := GetPlayspace().GetPlayers().Length

            if (PlayerCount >= 1):
                GameInfo.StartGame() #HERE
                break
game_info := class(creative_device):
    @editable
    SpawnInfo:spawn_info = spawn_info{}

    StartGame<public>():void =
        Print("GameInfo.StartGame")
        SpawnInfo.TeleportToMap(1)
spawn_info := class<concrete>(creative_device):
    var TeleportNow<public>:logic = false
    var Map:int = 0

    OnBegin<override>()<suspends> : void =
        loop:
            Sleep(1.0)
            if (TeleportNow = true):
                Print("Teleporting now to Map #{Map}")
                set TeleportNow = false

    TeleportToMap<public>(SelectedMap:int) : void =
        Print("SpawnInfo.TeleportToMap({SelectedMap})")
        set Map = SelectedMap
        set TeleportNow = true

Device setup in scene

Playing

Details

  • Each of the devices is set up to chain into one another: countdown_timer → game_info-> spawn_info.
  • CountDownTimer.OnBegin runs first, triggering RunCountdown()
  • At the same time SpawnInfo.OnBegin runs, which puts it into the loop waiting for “TeleportNow” to be true
  • After 2s CountDownTimer.RunCountdown wakes up, checks the player count, then triggers GameInfo.StartGame()
  • GameInfo.StartGame() sets TeleportNow to true on SpawnInfo
  • At the next check SpawnInfo.OnBegin sees that TeleportNow is true and prints “Teleporting now to Map #{Map}”.

Next steps

I have a few debugging suggestions/ideas that might help to figure out what part of your code isn’t working.

  1. You could make a separate device that has a reference to SpawnInfo. In the OnBegin have it call SpawnInfo.TeleportToMap(0). This way you can make sure things are mostly working there and your “print3” gets into the log. Once that is working you could work your way out, moving the OnBegin into GameInfo, which would then trigger SpawnInfo. Continue until you figure out where it started breaking.
  2. Based on some of the previous code snippets I suspect there might be a few other hidden cases around where Verse device classes are being constructed anew in code instead of drag/dropped from the content browser. Creating instances of classes works for regular Verse classes but it doesn’t work for Verse Devices. So any time that is happening it is going to cause problems.
  3. I would check the logs for any Verse errors that are present. I think that if you forget to hook up @editable properties for devices then the device might fail while your game will still run.

Let me know how that goes. Hope this helps in some way.

3 Likes

Thank you, with your instructions and advices I was able to find the problem, I put the teleport on the OnBegin because it didn’t want to work outside of it but with the help/fixes that you provided it now works even in other functions. Other than that by referencing the classes I’m now able to get the variables that I need.

The playercount method that you provided is really simple too and didn’t think about it :laughing:.

Thank you again for taking so much time just for me, with your help I’m now able to continue my project and now I understand how Verse works far better than before!