MoveTo & TeleportTo BUG? : Verse script compiles, runs, doesn't move creative_prop

Dearest Verse people,

With this script I’m trying to snap a prop to Player:
It compiles, no errors, it runs in game, it prints the correct values… and yet, prop doesn’t move.

If anybody has any idea WHY, I’d be super thankful!

In the meantime, I’ll look for a workaround: I hyper commented the code hoping you’ll find something useful in it.

Cheers!

using { /Fortnite.com/Devices }  # Import the devices module from Fortnite for device-related classes and functions
using { /Fortnite.com/Characters }  # Import the characters module from Fortnite for character-related classes and functions
using { /Verse.org/Simulation }  # Import the simulation module from Verse for simulation-related classes and functions
using { /UnrealEngine.com/Temporary/SpatialMath }  # Import the SpatialMath module from Unreal Engine for spatial and mathematical operations

# Define a class with editable prop and volume_device variables
snap_prop_to_player := class(creative_device):  # Define a new class called snap_prop_to_player, inheriting from creative_device

    @editable  # Allow this property to be set from the editor
    PieceOfCode:creative_prop = creative_prop{}  # Define an editable property PieceOfCode of type creative_prop and initialize it

    @editable  # Allow this property to be set from the editor
    VolumeTrigger:volume_device = volume_device{}  # Define an editable property VolumeTrigger of type volume_device and initialize it

    # Define an event handler for when a player enters the volume
    OnBegin<override>()<suspends>:void=  # Override the OnBegin method to define custom behavior when the game starts; <suspends> indicates the method can be paused

        # Subscribe to the volume's EnteredEvent
        VolumeTrigger.AgentEntersEvent.Subscribe(AttachToPlayer)  # Subscribe the AttachToPlayer method to the AgentEntersEvent of the VolumeTrigger

    AttachToPlayer(Agent : agent) : void =  # Define the AttachToPlayer method, which takes an Agent of type agent and returns void
        spawn:  # Use the spawn keyword to start the OnPlayerEntered method asynchronously
            OnPlayerEntered(Agent)  # Call the OnPlayerEntered method with the Agent as a parameter

    # Event handler function for when a player enters the volume
    OnPlayerEntered(InPlayer:agent)<suspends> :void=  # Define the OnPlayerEntered method, which takes an InPlayer of type agent and suspends its execution
        if (FortniteCharacter := InPlayer.GetFortCharacter[]):  # Use an if statement to attempt to get the FortniteCharacter from InPlayer; the := operator assigns the result if successful
            Print("Player entered volume and attached!", ?Duration:=6.0)  # Print a message indicating the player has entered the volume, with the message displayed for 6 seconds
            loop:  # Start a loop block, which runs indefinitely until explicitly broken or interrupted
                Sleep(0.1)  # Pause execution for 0.1 seconds to allow other processes to run
                PlayerTransform := FortniteCharacter.GetTransform()  # Get the Transform object from the FortniteCharacter and assign it to PlayerTransform
                PlayerPosition := PlayerTransform.Translation  # Extract the Translation (position) vector from PlayerTransform and assign it to PlayerPosition
                PlayerRotation := PlayerTransform.Rotation  # Extract the Rotation from PlayerTransform and assign it to PlayerRotation
                
                # Adjust the PlayerPosition with the required offsets
                AdjustedPosition := vector3{X:=PlayerPosition.X, Y:=PlayerPosition.Y - 200.0, Z:=PlayerPosition.Z + 100.0}  # Create an AdjustedPosition vector with the specified offsets

                # Debug print to verify position update
                Print("Moving prop to position: {AdjustedPosition}, Rotation: {PlayerRotation}", ?Duration:=0.1)  # Print the AdjustedPosition and PlayerRotation for debugging, with the message displayed for 0.1 seconds
                
                # Move the PieceOfCode to the AdjustedPosition and PlayerRotation
                spawn { PieceOfCode.MoveTo(AdjustedPosition, PlayerRotation, 0.1) }  # Move the PieceOfCode to the AdjustedPosition and PlayerRotation with a time of 0.1 seconds using the spawn keyword to run asynchronously

FYI, I refactored the script to use TeleportTo… and it compiles, it runs, all prints show that it is working… and yet prop doesn’t move

this is the script refactored to use TeleportTo

using { /Fortnite.com/Devices }  # Import the devices module from Fortnite for device-related classes and functions
using { /Fortnite.com/Characters }  # Import the characters module from Fortnite for character-related classes and functions
using { /Verse.org/Simulation }  # Import the simulation module from Verse for simulation-related classes and functions
using { /UnrealEngine.com/Temporary/SpatialMath }  # Import the SpatialMath module from Unreal Engine for spatial and mathematical operations

# Define a class with editable prop and volume_device variables
snap_prop_to_player := class(creative_device):  # Define a new class called snap_prop_to_player, inheriting from creative_device

    @editable  # Allow this property to be set from the editor
    PieceOfCode:creative_prop = creative_prop{}  # Define an editable property PieceOfCode of type creative_prop and initialize it

    @editable  # Allow this property to be set from the editor
    VolumeTrigger:volume_device = volume_device{}  # Define an editable property VolumeTrigger of type volume_device and initialize it

    # Define an event handler for when a player enters the volume
    OnBegin<override>()<suspends>:void=  # Override the OnBegin method to define custom behavior when the game starts; <suspends> indicates the method can be paused

        # Subscribe to the volume's EnteredEvent
        VolumeTrigger.AgentEntersEvent.Subscribe(AttachToPlayer)  # Subscribe the AttachToPlayer method to the AgentEntersEvent of the VolumeTrigger

    AttachToPlayer(Agent : agent) : void =  # Define the AttachToPlayer method, which takes an Agent of type agent and returns void
        spawn { OnPlayerEntered(Agent) }  # Use the spawn keyword to start the OnPlayerEntered method asynchronously

    # Event handler function for when a player enters the volume
    OnPlayerEntered(InPlayer:agent)<suspends> :void=  # Define the OnPlayerEntered method, which takes an InPlayer of type agent and suspends its execution
        if (FortniteCharacter := InPlayer.GetFortCharacter[]):  # Use an if statement to attempt to get the FortniteCharacter from InPlayer; the := operator assigns the result if successful
            Print("Player entered volume and attached!", ?Duration:=6.0)  # Print a message indicating the player has entered the volume, with the message displayed for 6 seconds
            loop:  # Start a loop block, which runs indefinitely until explicitly broken or interrupted
                Sleep(0.1)  # Pause execution for 0.1 seconds to allow other processes to run
                PlayerTransform := FortniteCharacter.GetTransform()  # Get the Transform object from the FortniteCharacter and assign it to PlayerTransform
                PlayerPosition := PlayerTransform.Translation  # Extract the Translation (position) vector from PlayerTransform and assign it to PlayerPosition
                PlayerRotation := PlayerTransform.Rotation  # Extract the Rotation from PlayerTransform and assign it to PlayerRotation
                
                # Adjust the PlayerPosition with the required offsets
                AdjustedPosition := vector3{X:=PlayerPosition.X, Y:=PlayerPosition.Y - 200.0, Z:=PlayerPosition.Z + 100.0}  # Create an AdjustedPosition vector with the specified offsets

                # Debug print to verify position update
                Print("Moving prop to position: {AdjustedPosition}, Rotation: {PlayerRotation}", ?Duration:=0.1)  # Print the AdjustedPosition and PlayerRotation for debugging, with the message displayed for 0.1 seconds
                
                # Create a new transform for the teleportation
                NewTransform := transform{Translation:=AdjustedPosition, Rotation:=PlayerRotation}  # Create a new transform with the adjusted position and rotation
                
                # Teleport the PieceOfCode to the AdjustedPosition and PlayerRotation
                if (PieceOfCode.TeleportTo[NewTransform]):  # Use the TeleportTo method to teleport the PieceOfCode to the new transform
                    Print("Prop successfully teleported!", ?Duration:=0.1)  # Print a success message

The script is not only compiiing, running and computing correctly… but it seems like it is trying to do something: it is as if prop is indeed being moved, but snaps back into place right after.

I wonder if it is a bug or a “feature” :smiley:

I would start by testing outside of the loop, for example see if you can call MoveTo just on the press of a button or something. I’m currently making a prop movement system and MoveTo functions correctly, so it is most likely your code structure or the reference to the prop you are moving. You can also use race: with the moveTo and put it against a Sleep() with longer duration to see if the moveto is completing in time, like this:

                 Arrived := race:
                        block:
                            BossProp.MoveTo(MovePos, TargetRot, TimeToMove)
                            true #arrived
                        block:
                            Sleep(1.0)
                            true

Btw Im all for commenting but the quantity here makes the code more difficult to read and understand structure

1 Like

Also here is a good tutorial on snapping meshes to player with the vfx powerup:
https://www.youtube.com/watch?v=alJFj42ms9w

1 Like

Thank you @swaggod !

MoveTo indeed functions in cases like moving NPCs, but seems like there’s something, somewhere, in UEFN, not Verse, acting like an “anticheat” of sorts, snapping the Prop back to origin after the transform…

Being stubborn, I’m keeping iterating on the script, trying to make it as minimalist as possible…

for the record, this is where I am at

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

# This class defines a device that makes a cube follow the player
Draggable := class(creative_device):

    @editable
    Cube : creative_prop = creative_prop{}
    @editable
    Delay : float = 1.0  # delay between each movement in seconds
    @editable
    MinDistance : float = 200.0  # minimum distance to maintain from player
    @editable
    VolumeTrigger: volume_device = volume_device{}

    # Runs when the device is started in a running game
    OnBegin<override>()<suspends>:void=
        Print("Device initialized and waiting for player to enter volume.", ?Duration := 6.0)
        VolumeTrigger.AgentEntersEvent.Subscribe(TriggerWakeupAsync)

    # Function to handle the event asynchronously
    TriggerWakeupAsync(InPlayer:agent):void =
        spawn{TriggerWakeup(InPlayer)}

    # Async function that handles the logic when a player enters the volume
    TriggerWakeup(InPlayer:agent)<suspends>:void =
        if (FortCharacter := InPlayer.GetFortCharacter[]):
            Print("Player entered volume and attached!", ?Duration := 6.0)
            FollowPlayer(FortCharacter)

    # Function to make the cube follow the player
    FollowPlayer(Player:fort_character)<suspends>:void=
        loop:
            Sleep(Delay)
            PlayerPosition := Player.GetTransform().Translation
            CubePosition := Cube.GetTransform().Translation
            Direction := NormalizeVector(PlayerPosition - CubePosition)
            DistanceToPlayer := VectorDistance(PlayerPosition, CubePosition)
            if (DistanceToPlayer > MinDistance):
                TargetPosition := PlayerPosition - Direction * MinDistance
                Cube.MoveTo(TargetPosition, Cube.GetTransform().Rotation, 0.1)

    # Function to normalize a vector
    NormalizeVector(Vector:vector3) : vector3 =
        Magnitude := Vector.Length()
        if (Magnitude > 0.0):
            return Vector / Magnitude
        else:
            return Vector

    # Function to calculate the distance between two vectors
    VectorDistance(VectorA:vector3, VectorB:vector3) : float =
        return (VectorA - VectorB).Length()

It compiles, runs in game… still, prop snaps backto original position immediately.

Odd.

I will keep working on it and report my findings here.

… Yet Another wasted weekend :frowning:

To keep track of my misery, this is a barebone version using TeleportTo:

Still, it seems to work but somehow prop seems to snap back to original position

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

# This class defines a device that makes a cube follow the player
FollowPlayerTeleportTo := class(creative_device):

    @editable
    Cube : creative_prop = creative_prop{}
    @editable
    VolumeTrigger: volume_device = volume_device{}

    # Runs when the device is started in a running game
    OnBegin<override>()<suspends>:void=
        Print("Device initialized and waiting for player to enter volume.", ?Duration := 6.0)
        VolumeTrigger.AgentEntersEvent.Subscribe(TriggerWakeupAsync)

    # Function to handle the event asynchronously
    TriggerWakeupAsync(InPlayer:agent):void =
        spawn{TriggerWakeup(InPlayer)}

    # Async function that handles the logic when a player enters the volume
    TriggerWakeup(InPlayer:agent)<suspends>:void =
        if (FortCharacter := InPlayer.GetFortCharacter[]):
            Print("Player entered volume and attached!", ?Duration := 6.0)
            FollowPlayer(FortCharacter)

    # Function to make the cube follow the player
    FollowPlayer(Player:fort_character)<suspends>:void=
        loop:
            PlayerPosition := Player.GetTransform().Translation
            PlayerRotation := Player.GetTransform().Rotation
            if (Cube.TeleportTo[PlayerPosition, PlayerRotation]):
                Print("Cube teleported to player position.", ?Duration := 6.0)
            else:
                Print("Failed to teleport cube.", ?Duration := 6.0)
            Sleep(1.0)  # Adjust the delay as necessary

and this is the super simplified attempt to use MoveTo

Same results: compiles, runs in game, computes… still prop snaps back to original position.

Feels like a hardcoded limitation somewhere in UEFN. Would be nice to know if it is.

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

# This class defines a device that makes a cube follow the player
FollowPlayerMoveTo := class(creative_device):

    @editable
    Cube : creative_prop = creative_prop{}
    @editable
    VolumeTrigger: volume_device = volume_device{}

    # Runs when the device is started in a running game
    OnBegin<override>()<suspends>:void=
        Print("Device initialized and waiting for player to enter volume.", ?Duration := 6.0)
        VolumeTrigger.AgentEntersEvent.Subscribe(TriggerWakeupAsync)

    # Function to handle the event asynchronously
    TriggerWakeupAsync(InPlayer:agent):void =
        spawn{TriggerWakeup(InPlayer)}

    # Async function that handles the logic when a player enters the volume
    TriggerWakeup(InPlayer:agent)<suspends>:void =
        if (FortCharacter := InPlayer.GetFortCharacter[]):
            Print("Player entered volume and attached!", ?Duration := 6.0)
            FollowPlayer(FortCharacter)

    # Function to make the cube follow the player
    FollowPlayer(Player:fort_character)<suspends>:void=
        loop:
            PlayerPosition := Player.GetTransform().Translation
            PlayerRotation := Player.GetTransform().Rotation
            Cube.MoveTo(PlayerPosition, PlayerRotation, 2.0)
            Sleep(1.0)  # Adjust the delay as necessary

Hmm I don’t think its a bug as I can get MoveTo with a creative prop to work fine, but I would suggest your next steps breaking your code down even further. Can you get a prop to moveto your player when the player just presses a button? Can you get Moveto to work in a separate project?

@swaggod YES! It does work in other cases:

  • It works in other projects where I’m using it to move NPCs (where I trigger move to at a much slower tick, 1 or 3 seconds)
  • It works in the current project as long as I don’t introduce triggering it via volume (for example, this “works”)
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /Verse.org/Verse }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }

# This class defines a device that makes a cube follow the player while maintaining a minimum distance
cube_follow_player_device := class(creative_device):

    @editable
    Cube : creative_prop = creative_prop{}
    @editable
    Delay : float = 0.1  # delay between each movement in seconds
    @editable
    MoveSpeed : float = 0.01  # movement speed
    @editable
    MinDistance : float = 3.0  # minimum distance to maintain from player

    # Runs when the device is started in a running game
    OnBegin<override>()<suspends>:void=
        AllPlayers := GetPlayspace().GetPlayers()
        # Check if there is at least one player
        if (Player := AllPlayers[0]):
            FollowPlayer(Player)

    # Function to make the cube follow the player
    FollowPlayer(Player:player)<suspends>:void=
        loop:
            Sleep(Delay)
            # Get the player's character and position
            if (FortniteCharacter := Player.GetFortCharacter[]):
                PlayerPosition := FortniteCharacter.GetTransform().Translation
                CubePosition := Cube.GetTransform().Translation
                Direction := NormalizeVector(PlayerPosition - CubePosition)
                DistanceToPlayer := VectorDistance(PlayerPosition, CubePosition)
                Print("PlayerPosition: {PlayerPosition}, CubePosition: {CubePosition}, DistanceToPlayer: {DistanceToPlayer}")
                # Check if the cube is farther than the minimum distance
                if (DistanceToPlayer > MinDistance):
                    TargetPosition := PlayerPosition - Direction * MinDistance
                    Print("TargetPosition: {TargetPosition}")
                    InterpolateMoveCube(CubePosition, TargetPosition)

    # Function to normalize a vector
    NormalizeVector(Vector:vector3) : vector3 =
        Magnitude := Vector.Length()
        if (Magnitude > 0.0):
            return Vector / Magnitude
        else:
            return Vector

    # Function to calculate the distance between two vectors
    VectorDistance(VectorA:vector3, VectorB:vector3) : float =
        return (VectorA - VectorB).Length()

    # Function to interpolate the cube's movement towards the target position
    InterpolateMoveCube(StartPos:vector3, EndPos:vector3)<suspends>:void=
        var InterpTime : float = 0.0
        var Duration : float = 1.0  # Total time to interpolate based on speed
        loop:
            Sleep(0.1)
            set InterpTime += 0.1 * MoveSpeed
            if (InterpTime >= Duration):
                Cube.MoveTo(EndPos, Cube.GetTransform().Rotation, 0.1)
                return
            var InterpPosition : vector3 = Lerp(StartPos, EndPos, InterpTime / Duration)
            # Call MoveTo with a small delay to ensure movement
            MoveCube(InterpPosition)
            Print("InterpPosition: {InterpPosition}")

    MoveCube(Position:vector3)<suspends>:void=
        Cube.MoveTo(Position, Cube.GetTransform().Rotation, 0.1)```


and as soon as I introduce triggering via volume, it doesn't ! So weird!

NOT WORKING :

… NOT WORKING WHEN I introduce triggering on entering volume:

# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
    Print("Device initialized and waiting for player to enter volume.", ?Duration := 6.0)
    VolumeTrigger.AgentEntersEvent.Subscribe(OnPlayerEnter)

# Function to handle the event when a player enters the volume
OnPlayerEnter(InPlayer:agent):void =
    spawn{HandlePlayerEnter(InPlayer)}

HandlePlayerEnter(InPlayer:agent)<suspends>:void =
    if (FortCharacter := InPlayer.GetFortCharacter[]):
        Print("Player entered volume and attached!", ?Duration := 6.0)
        FollowPlayer(FortCharacter)

# Function to make the cube follow the player
FollowPlayer(Player:fort_character)<suspends>:void=
    loop:
        Sleep(Delay)
        # Get the player's character and position
        PlayerPosition := Player.GetTransform().Translation
        CubePosition := Cube.GetTransform().Translation
        Direction := NormalizeVector(PlayerPosition - CubePosition)
        DistanceToPlayer := VectorDistance(PlayerPosition, CubePosition)
        Print("PlayerPosition: {PlayerPosition}, CubePosition: {CubePosition}, DistanceToPlayer: {DistanceToPlayer}")
        # Check if the cube is farther than the minimum distance
        if (DistanceToPlayer > MinDistance):
            TargetPosition := PlayerPosition - Direction * MinDistance
            Print("TargetPosition: {TargetPosition}")
            InterpolateMoveCube(CubePosition, TargetPosition)

# Function to normalize a vector
NormalizeVector(Vector:vector3) : vector3 =
    Magnitude := Vector.Length()
    if (Magnitude > 0.0):
        return Vector / Magnitude
    else:
        return Vector

# Function to calculate the distance between two vectors
VectorDistance(VectorA:vector3, VectorB:vector3) : float =
    return (VectorA - VectorB).Length()

# Function to interpolate the cube's movement towards the target position
InterpolateMoveCube(StartPos:vector3, EndPos:vector3)<suspends>:void=
    var InterpTime : float = 0.0
    var Duration : float = 1.0  # Total time to interpolate based on speed
    loop:
        Sleep(0.1)
        set InterpTime += 0.1 * MoveSpeed
        if (InterpTime >= Duration):
            Cube.MoveTo(EndPos, Cube.GetTransform().Rotation, 0.1)
            return
        var InterpPosition : vector3 = Lerp(StartPos, EndPos, InterpTime / Duration)
        # Call MoveTo with a small delay to ensure movement
        MoveCube(InterpPosition)
        Print("InterpPosition: {InterpPosition}")

MoveCube(Position:vector3)<suspends>:void=
    Cube.MoveTo(Position, Cube.GetTransform().Rotation, 0.1)

@swaggod it starts glitching as soon as I introduce

  • triggering MoveTo or TeleportTo via VolumeTrigger.AgentEntersEvent (absolutely fundamental)
  • fast tickrate (should be at least 0.4 but I’m trying a range of values from 0.5 to 3.0)

@swaggod I’m investigating the viability of using visual_effect_powerup_device as you suggested… thank you for the suggestion!

Good luck, because it breaks once the volume trigger is introduced I wonder if the problem is with the Agent reference supplied by the AgentEntersEvent

1 Like

Do you use this cube is any sequencer ?
Also, are you sure the FollowPlayer loop is only triggered once ? I don’t see any exit condition on it