Verse Concurrency - Time flow: Everything, everywhere in UEFN, all at once - Unreal Fest 2003

Accompanying code and example project for Verse Concurrency talk at Unreal Fest 2023.
Attached the UEFN project files in .zip form and the talk slides in .pdf form at bottom of this post.
[This will be updated and added to over time as well.]

concurrency_device.verse

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


###############################################################################
# Concurrency example manager device
concurrency_device := class(creative_device):

    @editable
    BlockButton : button_device = button_device{}
    @editable
    SyncButton : button_device = button_device{}
    @editable
    RaceButton : button_device = button_device{}
    @editable
    StopButton : button_device = button_device{}
    @editable
    ResetButton : button_device = button_device{}

    @editable
    MapPointA : map_indicator_device = map_indicator_device{}
    @editable
    MapPointB : map_indicator_device = map_indicator_device{}
    @editable
    MapPointC : map_indicator_device = map_indicator_device{}
    @editable
    MapPointD : map_indicator_device = map_indicator_device{}

    @editable
    PropA : creative_prop = creative_prop{}
    @editable
    PropB : creative_prop = creative_prop{}
    @editable
    PropC : creative_prop = creative_prop{}
    @editable
    PropD : creative_prop = creative_prop{}

    @editable
    ArrivedVFX : vfx_creator_device = vfx_creator_device{}

    Forward2m : vector3    = vector3{X:=200.0, Y:=0.0, Z:=0.0}
    OffsetF2mU1m : vector3 = vector3{X:=200.0, Y:=0.0, Z:=100.0}
    Offset0 : vector3      = vector3{X:=0.0, Y:=0.0, Z:=0.0}
    Arrive1m : float       = 100.0

    ###############################################################################
    # Runs when the device is started in a running game
    OnBegin<override>()<suspends>:void=
        Print("Concurrency device started")
        MoveLogic()

    ###############################################################################
    # Gets the specified player index as a positional object
    PlayerAsPositional(PlayerIdx : int)<transacts><decides>:positional =
        GetPlayspace().GetPlayers()[PlayerIdx].GetFortCharacter[]

    ###############################################################################
    GetPlayerXform(PlayerIdx : int):transform =
        # Assume zeroeth player
        PlayerAsPositional[PlayerIdx].GetTransform() or transform{}

    ###############################################################################
    # Stops any existing `MoveTo()` operating on this prop
    (Prop : creative_prop).MoveStop():void =
        if: # This will cancel any currently running move command on the same prop
            Prop.TeleportTo[Prop.GetTransform()]

    ###############################################################################
    # Teleports to specified transform and keeps any existing scale
    (Prop : creative_prop).TeleportToNoScale(Xform : transform):void =
        XformSameScale := transform:
            Scale       := Prop.GetTransform().Scale
            Rotation    := Xform.Rotation
            Translation := Xform.Translation
        if: # This will cancel any currently running move command on the same prop
            Prop.TeleportTo[XformSameScale]

    ###############################################################################
    # Move to specified offset of positional target adjusting over time as needed.
    # [Extension function that can be called on a `creative_prop` within this `concurrency_device` class.]
    (Prop : creative_prop).MoveToPositional(Target : positional, Offset : vector3, ArriveRadius : float)<suspends>:void =
        var StopOnArrive : logic = false
        defer:
            if: # If not arrived yet stop moving if MoveToPositional() cancelled
                not StopOnArrive?
            then:
                Prop.MoveStop()
        loop:
            # Determine desired position
            TargetXform := Target.GetTransform()
            TargetPos   := TargetXform.Translation
            MovePos     := TargetPos + TargetXform.Rotation.RotateVector(Offset)
            PropPos     := Prop.GetTransform().Translation

            # Set desired rotation to point to destination
            Difference   := MovePos - PropPos
            MoveRotation := MakeRotation(vector3{X:=0.0, Y:=0.0, Z:=1.0}, -ArcTan(Difference.X, Difference.Y))
    
            # If within arrival radius then exit
            DistanceRemaining := Difference.Length()
            if (DistanceRemaining <= ArriveRadius):
                break

            # The farther apart the longer the time to get there [could pass in adjustment]
            TimeToMove := DistanceRemaining * 0.003

            # Would put `race` here directly thouth need to wrap in funtion as bug workaround
            set StopOnArrive = Prop.MoveToPositional_race(MovePos, MoveRotation, TimeToMove)
            if (StopOnArrive?):
                break

    #==============================================================================
    # [Sub function of MoveToPositional() - not to be called directly]
    # Bug workaround: wrap any `race` that isn't at the end of a function in its own function
    (Prop : creative_prop).MoveToPositional_race(MovePos : vector3, MoveRotation : rotation, TimeToMove : float)<suspends>:logic =
        race:
            block:
                Prop.MoveTo(MovePos, MoveRotation, TimeToMove)
                # Only returns if Sleep() below doesn't win the race first.
                return true  # Movement stopped
            Sleep(0.3)  # Update move to location occasionally [could pass in value]
        return false

    ###############################################################################
    ArrivedFanfare()<suspends>:void=
        if (PlayerAgent := GetPlayspace().GetPlayers()[0]):
            ArrivedVFX.SpawnAt(PlayerAgent)
            ArrivedVFX.Enable()

    ###############################################################################
    # Specifies which move patter to use on the props
    MoveLogic()<suspends>:void=
        loop:
            race:
                block:
                    ConcurrencyLogic()
                    ArrivedFanfare()
                block:
                    ResetButton.InteractedWithEvent.Await()
                    ResetProps()
                StopButton.InteractedWithEvent.Await()

    ###############################################################################
    # Moves each prop in serial - one after the other
    ConcurrencyLogic()<suspends>:void=
        Print("Waiting for move button...")
        race:
            block:
                BlockButton.InteractedWithEvent.Await()
                SerialProps()
            block:
                SyncButton.InteractedWithEvent.Await()
                SyncProps()
            block:
                RaceButton.InteractedWithEvent.Await()
                RaceProps()

    ###############################################################################
    # Moves each prop in serial - one after the other
    SerialProps()<suspends>:void=
        Print("Move props in serial [block]...")
        if (PlayerTarget := PlayerAsPositional[0]):
            PropA.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)
            PropB.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)
            PropC.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)
            PropD.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)

    ###############################################################################
    # Moves props concurrently - simultaneously move all
    SyncProps()<suspends>:void=
        Print("Move props simultaneously [sync]...")
        if (PlayerTarget := PlayerAsPositional[0]):
            sync:
                PropA.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)
                PropB.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)
                PropC.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)
                PropD.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)

    ###############################################################################
    # Moves props concurrently and exit when first arrives
    RaceProps()<suspends>:void=
        Print("Move props simultaneously and complete when first arrives [race]...")
        if (PlayerTarget := PlayerAsPositional[0]):
            race:
                PropA.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)
                PropB.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)
                PropC.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)
                PropD.MoveToPositional(PlayerTarget, OffsetF2mU1m, Arrive1m)

    ###############################################################################
    # Resets all the props to their original positions.
    ResetProps():void=
        Print("Resetting props...")
        PropA.TeleportToNoScale(MapPointA.GetTransform())
        PropB.TeleportToNoScale(MapPointB.GetTransform())
        PropC.TeleportToNoScale(MapPointC.GetTransform())
        PropD.TeleportToNoScale(MapPointD.GetTransform())

    ###############################################################################
    # Resets all the props to their original positions.
    (Prop : creative_prop).Move2(Point1 : map_indicator_device, Point2 : map_indicator_device)<suspends>:map_indicator_device=
        PropA.MoveToPositional(Point1, Offset0, Arrive1m)
        PropA.MoveToPositional(Point2, Offset0, Arrive1m)
        Point2

    ###############################################################################
    AsyncArgsSerial()<suspends>:void=
        PropA.Move2(
            PropB.Move2(MapPointC, MapPointD),
            PropC.Move2(MapPointD, MapPointA))

    ###############################################################################
    AsyncArgsConcurrent()<suspends>:void=
        PropA.Move2(
            sync:
                PropB.Move2(MapPointC, MapPointD),
                PropC.Move2(MapPointD, MapPointA)
        )

ConcurrencyUnrealFest2023.zip (2.1 MB)
Verse Concurrency - Unreal Fest 2023 October.pdf (4.6 MB)

6 Likes

Here is the full talk from Unreal Fest 2023 in New Orleans:

It will eventually be turned into more online documentation on Verse Concurrency.

1 Like

Here is an example of an iterated sync over an array:

And here is an example of an iterated race over an array:

1 Like