How to update a tween while it is being played?

Let’s say I have a tween of a part moving from point A to point B that loops indefinitely. Well, what if point A and point B change while the tween is being played? How can my tween be updated smoothly to transition into following a new point?

This problem can be illustrated with a simple example. Here is the gif first:
https://gyazo.com/828c94f28f5c6fbea27e61648c7c2c98

As I move the red part, the tween does not follow the new position of the red part, but it instead follows the old position. This is what I want to change. In other words, I want my tween to start following the new position.

So what approach can I take to update the tween while it is being played? Or is tweening unsuitable for this purpose? I’m not really looking for a code sample answer, just trying to understand what approach I can use to achieve what I want.

Here is the code I’m using for reference. Should be quite simple.

local target = partA
local TweenService = game:GetService("TweenService")
local part = partB
part.Anchored = true
local goal = target
local tweenInfo = TweenInfo.new(
	2, -- Time
	Enum.EasingStyle.Linear, -- EasingStyle
	Enum.EasingDirection.Out, -- EasingDirection
	-1, -- RepeatCount (when less than zero the tween will loop indefinitely)
	true, -- Reverses (tween will reverse once reaching it's goal)
	0 -- DelayTime
)
local tween = TweenService:Create(part, tweenInfo, {Position = target.Position})
tween:Play()
3 Likes

Ok oh a good one so this is what you need to do you need to make a function within that function you need to have both the tween play and the targets so like this.

local target = partA
local TweenService = game:GetService("TweenService")
local part = partB
part.Anchored = true
local goal = target
local tweenInfo = TweenInfo.new(
2, -- Time
Enum.EasingStyle.Linear, -- EasingStyle
Enum.EasingDirection.Out, -- EasingDirection
-1, -- RepeatCount (when less than zero the tween will loop indefinitely)
true, -- Reverses (tween will reverse once reaching it's goal)
0 -- DelayTime
)
function MovePart()
target = partA
part = partB
TweenService:Create(part, tweenInfo, {Position = target.Position})
end
MovePart()

This way every time you run the move part it will check the position of the part.

What about adding a while true do. Something like

function PartPosition()
while true do
return yourpart.Position
end
end

tween:Play()

If that doesn’t work, maybe surround the tween:Play() with a

while true do
wait(2) (wait matches the time it takes to tween)
tween:Play
end

Hope this helps. As always please mark it as the solution if it works.

Thanks,
Sam.

You’re most likely better off using a custom lerp instead of TweenService, as you cannot directly change the goal, especially if you use Reverse. The below code will move towards the endGoal by 5% of it’s distance every wait() interval.

If you want it to take a specific time, then you can use an event loop and the delta as this is just an example. To do the reverses effect, you’ll have to change the goal position once within a certain distance.

You can make it go a specific studs per second using (studsPerSecond / distBetweenGoal) * timeDelta

local endGoal = workspace.Target;
local item = workspace.Root;

while true do
	
	item.CFrame = CFrame.new(item.Position:Lerp(endGoal.Position, .05));
	wait();
	
end
3 Likes

This might be what you’re looking for, but if it’s not then you’re going to have to write your own tweening function like @fireboltofdeath said.

while wait(2) do
	local target = partA
	local TweenService = game:GetService("TweenService")
	local part = partB
	part.Anchored = true
	local goal = target
	local tweenInfo = TweenInfo.new(
		2, -- Time
		Enum.EasingStyle.Linear, -- EasingStyle
		Enum.EasingDirection.Out, -- EasingDirection
		0, -- RepeatCount (when less than zero the tween will loop indefinitely)
		true, -- Reverses (tween will reverse once reaching it's goal)
		0 -- DelayTime
	)
	local tween = TweenService:Create(part, tweenInfo, {Position = target.Position})
	tween:Play()
end

Basically what I did here was tell TweenService to only do the tween once but then encase the code inside a loop so it will update for any new positions.

Alternatively, you can look into using BodyPositions if you want something that changes in real-time.

local tween -- predefine it because you need a reference to it
...
-- in function

if tween then
   tween:Cancel() -- do this, then create a new one
end

-- ^ like i said
local tweenInfo = TweenInfo.new(
	2, -- Time
	Enum.EasingStyle.Linear, -- EasingStyle
	Enum.EasingDirection.Out, -- EasingDirection
	-1, -- RepeatCount (when less than zero the tween will loop indefinitely)
	true, -- Reverses (tween will reverse once reaching it's goal)
	0 -- DelayTime
)
tween:Create(part, tweenInfo, {Position = target.Position})

2 Likes

This solution will achieve what you’re looking for. If you want it to be perfectly smooth or otherwise have more specific control over the tween, you can use a custom implementation of Penner’s original easing formulas: easing/lib/easing.lua at master · EmmanuelOga/easing · GitHub

3 Likes

Can’t you just loop a position check and stop the old tween/start a new tween when the part moves? If it reaches the goal before the part moves a Tween.Completed event will fire letting you know to stop checking the position of the part.

Thanks for the link, it’s a nice resource to have. What do you mean with ‘perfectly smooth’, though? Are you suggesting that the other methods have a particular disadvantage and if so, what is it?

I managed to get a working prototype following your answer but I wish I wouldn’t have to recompute the tween.

I see. I thought about writing a custom lerp but that does mean doing a lot of work which tweening already does, doesn’t it? So it would be quite a shame.

You’re only using a linear lerp, so it’s not any issue. However, if you want different tweening styles, you can simply use TweenService:GetValue() which takes in an alpha just like Vector3:lerp() so it should work just as well.