How do you make something move Smoothly?

I have a small script that makes a prop follow a player, 80 metres above them

        loop:
            Players : []player = GetPlayspace().GetPlayers()
            if(Player : player = Players[0]):

                if(FortniteCharacter : fort_character = Player.GetFortCharacter[]):
                    PlayerPosition : vector3 = FortniteCharacter.GetTransform().Translation

                    TargetPositionX : float = PlayerPosition.X
                    TargetPositionY : float = PlayerPosition.Y
                    TargetPositionZ : float = PlayerPosition.Z + 8000.0
                    
                    Target.MoveTo(vector3{X:=TargetPositionX, Y:=TargetPositionY, Z:=TargetPositionZ}, IdentityRotation(), 0.5)
            Sleep(0.0)

It works, but it’s not smooth. I can make the MoveTo longer to to make it smoother, but that makes it lag behind further, and you still see a jump each time it completes. I’ve tried making it really small, but that’s too jerky to watch.

I wanted to put the MoveTo in a branch: to it would come round again before it’s ended and I could update it with a new MoveTo, but it looks like you can’t put those in loops?

I also tried changing it to TeleportTo, but it complained about brackets. That’s entirely on me; I don’t know enough Verse yet to understand what I did wrong. Maybe someone here knows how to do that properly? Would that even look smooth though? Feels like the script probably takes too long to run to expect it to update subframe.

Thoughts?

Yeah TeleportTo will work, but it’ll still look jumpy/jerky though because the server only runs at 30 ticks per second and all that.

And yes, because TeleportTo is failable, you’ll need to use square brackets to call it. In order to call a failable thing, it needs to be in a “failable context”, like an if statement (so that if it does fail, the program knows how you want to handle it). In the case of something like TeleportTo, you don’t necessarily need to do anything whether it succeeds or fails. Anyway so that means that instead of Prop.TeleportTo(Position,Rotation) you do it like this:

if(Prop.TeleportTo[Position,Rotation]):

MoveTo is the intended way of moving a prop smoothly for now. Like was mentioned, to have something follow the player it will have to lag slightly behind to maintain a smooth feel. We have identified some bugs with MoveTo and hope to have the issues you described with the jump on completion and not working correctly in loops addressed in a future release.

4 Likes

Is there a time estimate for this work or is there somewhere we can upvote the priority?
It is extremely difficult to generate smooth, controlled movement with the current restrictions.
Will there ever be a MoveCompleted callback? That would solve all of my current requirements.

1 Like

There is a DestionationReached callback on the move_to_result function:

var Result : move_to_result = Target.MoveTo(vector3{X:=TargetPositionX, Y:=TargetPositionY, Z:=TargetPositionZ}, IdentityRotation(), 0.5)
if (Result = move_to_result.DestinationReached):
    RecallFunction()
2 Likes

Thanks @bilnik67

I’ve been struggling with that this morning and not getting the desired result. I’m probably using it wrong.

I’m currently switching my logic to use a prop_mover_device instead, because that has an EndedEvent member function (seems to be undocumented here, so I might run into a similar problem)

-deleted test version of the code-

EDIT: I’m sure I tried this before, but it’s working now - must have been some syntax problems! It moves smoothly during each 2 seconds interval, but still glitches sideways when MoveTo is called again:

    Update()<suspends>:void=
        # meteor update in here, called from meteor_manager
        if (FinishedMoveTo?):
            T := Prop.GetTransform()
            P := T.Translation + V
            Print("starting move to {P}")
            R := T.Rotation
            set FinishedMoveTo = false
            Result := Prop.MoveTo(P, R, 2.0)
            if (Result = move_to_result.DestinationReached):
                set FinishedMoveTo = true
                Print("finished move to {P}")