Tweens glitching out

I’m trying to make working curtains that open and close in the middle of tweening, whenever the player wants.

The curtains go away if you click them multiple times, while they tween. Here’s an example: example

This only works perfectly if you wait until the tweening finishes, but I don’t want that.
Here’s what I’m trying to achieve (source: Start Survey): example

Here’s my tweening script.

local tweenService = game:GetService("TweenService")

local curtains = script.Parent.Curtains.Value

local leftCurtain = curtains.LeftCurtain
local rightCurtain = curtains.RightCurtain

local clickDetector = Instance.new("ClickDetector")
clickDetector.Parent = curtains.ClickDetectorPart

local isFolded = false

local defaultSizeX = 3.851
local openedSizeX = 1.32

local playingTweens = { }


local function makeTween(target, newPoint, speed, delta)
	local tween = tweenService:Create(
		target,
		
		TweenInfo.new(
			(target.Position.X - newPoint) / speed,
			Enum.EasingStyle.Linear,
			Enum.EasingDirection.In,
			0, false, 0
		),
		
		{
			Size = Vector3.new(newPoint, target.Size.Y, target.Size.Z),
			Position = Vector3.new(target.Position.X, target.Position.Y, target.Position.Z - delta)
		}
	)
	print(delta)
	return tween
end

local function updateTweens()
	local tweens = 
		{
			open = 
			{
				openLeftCurtain = makeTween(leftCurtain, openedSizeX, 40, (defaultSizeX - openedSizeX) / 2),
				openRightCurtain = makeTween(rightCurtain, openedSizeX, 40, (defaultSizeX - openedSizeX) / 2)
			},
			
			close =
			{
				closeLeftCurtain = makeTween(leftCurtain, defaultSizeX, 40, ((defaultSizeX - openedSizeX) / 2) * -1),
				closeRightCurtain = makeTween(rightCurtain, defaultSizeX, 40, ((defaultSizeX - openedSizeX) / 2 * -1))
			}
		}
	return tweens
end

clickDetector.MouseClick:Connect(function()
	for i, v in pairs(playingTweens) do --Stops all current tweens from playing
		table.remove(playingTweens, i)
		v:Destroy()
	end
	
    --Plays the new tweens

	local tweens = updateTweens()
	
	if isFolded then
		isFolded = false
		for _, v in pairs(tweens.close) do
			table.insert(playingTweens, v)
			v:Play()
		end
	else
		isFolded = true
		for _, v in pairs(tweens.open) do
			table.insert(playingTweens, v)
			v:Play()
		end
	end
	
	print(playingTweens)
end)

You are tweening them based on their current size and position, with no debounce, which is going to cause issues, because effectively (I’m guessing without seeing all the code) , that you’re actually updating the tweens when they’re mid-progress; this won’t work.

The way I’d do it, is through Attributes on each curtain, having the positional and size values for both open/close stored there, and then using that to tween based on whether its currently set to open or close, and obviously, make sure the tweens that are in-use get :Cancel() called on them when they are stopped.

We ideally need more code than just the two functions to help you, like how you are running them, but ultimately that seems to be partially your issue as stated above in my last text block.

Okay, ill post the whole script right now.

Is there any reason you’re using a custom function to construct the tweens instead of just using TweenService:Create? It seems much more simpler to just use :Create.

I’ve updated the script to the full one.

@antvvnio

I’m not sure why you are updating the Open + Close tweens each time the mouse click event happens, as that is updating the tweens based on their current size + position, rather than the previous.

Instead you should make the tweens constant values, I.E the same. So run the function to make the tweens at the start, and then don’t change them whenever the MouseClick() event happens, keep them the same, just run the close tweens/open tweens respectively.

You also don’t need to destroy the tweens, just cancel, as they can be reused.

local tweenService = game:GetService("TweenService")

local curtains = script.Parent.Curtains.Value

local leftCurtain = curtains.LeftCurtain
local rightCurtain = curtains.RightCurtain

local clickDetector = Instance.new("ClickDetector")
clickDetector.Parent = curtains.ClickDetectorPart

local isFolded = false

local defaultSizeX = 3.851
local openedSizeX = 1.32

local playingTweens = { }


local function makeTween(target, newPoint, speed, delta)
	local tween = tweenService:Create(
		target,

		TweenInfo.new(
			(target.Position.X - newPoint) / speed,
			Enum.EasingStyle.Linear,
			Enum.EasingDirection.In,
			0, false, 0
		),

		{
			Size = Vector3.new(newPoint, target.Size.Y, target.Size.Z),
			Position = Vector3.new(target.Position.X, target.Position.Y, target.Position.Z - delta)
		}
	)
	return tween
end

local function updateTweens()
	local tweens = 
		{
			open = 
			{
				openLeftCurtain = makeTween(leftCurtain, openedSizeX, 40, (defaultSizeX - openedSizeX) / 2),
				openRightCurtain = makeTween(rightCurtain, openedSizeX, 40, (defaultSizeX - openedSizeX) / 2)
			},

			close =
			{
				closeLeftCurtain = makeTween(leftCurtain, defaultSizeX, 40, ((defaultSizeX - openedSizeX) / 2) * -1),
				closeRightCurtain = makeTween(rightCurtain, defaultSizeX, 40, ((defaultSizeX - openedSizeX) / 2 * -1))
			}
		}
	return tweens
end

local tweens = updateTweens()

clickDetector.MouseClick:Connect(function()


	if isFolded then
		isFolded = false
		for _,v in pairs (tweens.open) do
			v:Cancel()
		end
		for _, v in pairs(tweens.close) do
			v:Play()
		end
	else
		isFolded = true
		for _,v in pairs (tweens.close) do
			v:Cancel()
		end
		for _, v in pairs(tweens.open) do
			v:Play()
		end
	end
end)

Something like, this, not sure if it’ll work as on mobile. Just edited it lol, but I would seriously think of revisiting it and making it cleaner and easier for you to understand.

I’ve tried that and it doesn’t work. It only works once. I was making tweens every click because I wanted to use speed instead of time at the TweenInfo. In this way, the curtains go the same speed, no matter the size. Example, if you tween the part from 0 to 1 in 1 seconds it takes of course, one second. If you want to tween the part from 0.8 to 1, it takes 1 second instead of 0.2 seconds.

Try this:

local tweenService = game:GetService("TweenService")

local curtains = script.Parent.Curtains.Value

local leftCurtain = curtains.LeftCurtain
local rightCurtain = curtains.RightCurtain

local clickDetector = Instance.new("ClickDetector")
clickDetector.Parent = curtains.ClickDetectorPart

local isFolded = false

local defaultSizeX = 3.851
local openedSizeX = 1.32

local playingTweens = {}


local function makeTween(target, newPoint, speed, delta)
	local tween = tweenService:Create(
		target,

		TweenInfo.new(
			(target.Position.X - newPoint) / speed,
			Enum.EasingStyle.Linear,
			Enum.EasingDirection.In,
			0, false, 0
		),

		{
			Size = Vector3.new(newPoint, target.Size.Y, target.Size.Z),
			Position = Vector3.new(target.Position.X, target.Position.Y, target.Position.Z - delta)
		}
	)
	print(delta)
	
	table.insert(playingTweens, tween)

	return tween
end

local function updateTweens()
	local tweens = 
		{
			open = 
			{
				openLeftCurtain = makeTween(leftCurtain, openedSizeX, 40, (defaultSizeX - openedSizeX) / 2),
				openRightCurtain = makeTween(rightCurtain, openedSizeX, 40, (defaultSizeX - openedSizeX) / 2)
			},

			close =
			{
				closeLeftCurtain = makeTween(leftCurtain, defaultSizeX, 40, ((defaultSizeX - openedSizeX) / 2) * -1),
				closeRightCurtain = makeTween(rightCurtain, defaultSizeX, 40, ((defaultSizeX - openedSizeX) / 2 * -1))
			}
		}
	return tweens
end

clickDetector.MouseClick:Connect(function()
	--//Stops all current tweens from playing
	for i, tween in ipairs(playingTweens) do
		table.remove(playingTweens, i)
		
		tween:Destroy()
	end

	--//Plays the new tweens
	local tweens = updateTweens()
	
	isFolded = not isFolded

	if isFolded then
		for _, tween in pairs(tweens.open) do
			tween:Play()
		end
	else
		for _, tween in pairs(tweens.close) do
			tween:Play()
		end
	end

	print(playingTweens)
end)

You do not want to keep making the tweens, or at least, if you do, the position/size need to be constant values both on close and open. Because what you’re currently doing, is getting the position of the curtains as they tween - which will effectively base your calculation on their current position - rather than the End goal positions. So either create attributes for position + size, or get the positions and size defined at the start of the script, so that they don’t change.

It still does the same. The curtains go away.

local TweenService = game:GetService("TweenService")
local Curtains = script.Parent.Curtains.Value
local LeftCurtain = Curtains.LeftCurtain
local RightCurtain = Curtains.RightCurtain

local ClickDetector = Instance.new("ClickDetector")
ClickDetector.Parent = Curtains.ClickDetectorPart

local isFolded = false

local DefaultSizeX = 3.851
local OpenedSizeX = 1.32

local CurrentTweens = {}

local Transitions = {
	["Left Curtain"] = {
		["Open"] = {
			Size = Vector3.new(OpenedSizeX, LeftCurtain.Size.Y, LeftCurtain.Size.Z),
			Position = Vector3.new(LeftCurtain.Position.X, LeftCurtain.Position.Y, LeftCurtain.Position.Z - (DefaultSizeX-OpenedSizeX)/2)
		},
		["Closed"] = {
			Size = Vector3.new(DefaultSizeX, LeftCurtain.Size.Y, LeftCurtain.Size.Z),
			Position = Vector3.new(LeftCurtain.Position.X, LeftCurtain.Position.Y, LeftCurtain.Position.Z - ((DefaultSizeX - OpenedSizeX)/2)*-1),
		}
	},
	["Right Curtain"] = {
		["Open"] = {
			Size = Vector3.new(OpenedSizeX, RightCurtain.Size.Y, RightCurtain.Size.Z),
			Position = Vector3.new(RightCurtain.Position.X, RightCurtain.Position.Y, RightCurtain.Position.Z - (DefaultSizeX-OpenedSizeX)/2)
		},
		["Closed"] = {
			Size = Vector3.new(DefaultSizeX, RightCurtain.Size.Y, RightCurtain.Size.Z),
			Position = Vector3.new(RightCurtain.Position.X, RightCurtain.Position.Y, RightCurtain.Position.Z - ((DefaultSizeX - OpenedSizeX)/2)*-1),
		}
	}
}

ClickDetector.MouseClick:Connect(function(Player)
	for i,v in ipairs (CurrentTweens) do
		v:Cancel()
		table.remove(CurrentTweens, i)
	end
	
	if isFolded then
		isFolded = false
		local RightCurtain = TweenService:Create(RightCurtain, TweenInfo.new(--[[Speed calculation here]]), {Position = Transitions["Right Curtain"]["Closed"].Position, Size = Transitions["Right Curtain"]["Closed"].Size})
		local LeftCurtain = TweenService:Create(LeftCurtain, TweenInfo.new(--[[Speed calculation here]]), {Position = Transitions["Left Curtain"]["Closed"].Position, Size = Transitions["Left Curtain"]["Closed"].Size})
		RightCurtain:Play()
		LeftCurtain:Play()
		table.insert(CurrentTweens, RightCurtain)
		table.insert(CurrentTweens, LeftCurtain)
	else
		isFolded = true
		local RightCurtain = TweenService:Create(RightCurtain, TweenInfo.new(--[[Speed calculation here]]), {Position = Transitions["Right Curtain"]["Open"].Position, Size = Transitions["Right Curtain"]["Open"].Size})
		local LeftCurtain = TweenService:Create(LeftCurtain, TweenInfo.new(--[[Speed calculation here]]), {Position = Transitions["Left Curtain"]["Open"].Position, Size = Transitions["Left Curtain"]["Open"].Size})
		RightCurtain:Play()
		LeftCurtain:Play()
		table.insert(CurrentTweens, RightCurtain)
		table.insert(CurrentTweens, LeftCurtain)
	end
end)

Add in your speed calculation into the TweenInfo part.

I just worked this up so if there’s any errors work through them.

I’ve resolved the problem myself, it works like in the game I showed. Thanks for the help.