Scripting help- Animated curtains not working as intended

Hey! I am making a concert game and with that comes an animated curtain system. Right now I have been able to make a model with 2 parts, humanoid, and a humanoid root part for these curtains. I have extra parts for my script to work.

Once my code hits the part where it unanchors the curtains it breaks. As shown in the image below it starts already opened and then continues with the opening animation from that part. Theres not really a work around as it has to be unanchored for animations to play. I have tried making it unanchored from the start but that breaks it in another way. I have also tried looking in devforms for solutions but theres nothing about this since this is pretty niche.

Any help or ideas would be appreciated.

CD.MouseClick:Connect(function()
	left.CFrame = CFrame.new(90.113, 24.658, 83.4)
	right.CFrame = CFrame.new(90.113, 24.658, 133.717)
	task.wait(0.1)
	left.Anchored = false
	right.Anchored = false
	open:Play()
	task.wait(0.1)
	e1.Transparency = 1
	e2.Transparency = 1
	task.wait(10)
	left.Anchored = true
	right.Anchored = true
	left.CFrame = CFrame.new(90.113, 24.658, 38.4)
	right.CFrame = CFrame.new(90.113, 24.658, 178.717)
end)

1 Like

For me if I wanted to animate parts I usually use TweenService for this without adjusting the anchor parts to false. You can also adjust the CFrame with XYZ axis so that it doesn’t move the whole curtain.

local left = game.Workspace.["Your Left Curtain"]
local right = game.Workspace.["Your Right Curtain"]
local CD = game.Workspace.["Your Click Detector"]
local db = false -- added debounce to open and close curtains, Also helps to avoid spam clicking / interacting the curtain

local tweenInfo = TweenInfo.new(
	0.6, -- You can adjust the time animation to your likings
	Enum.EasingStyle.Linear, -- EasingStyle
	Enum.EasingDirection.Out, -- EasingDirection
	0, -- RepeatCount (when less than zero the tween will loop indefinitely)
	false, -- Reverses (tween will reverse once reaching it's goal)
	0 -- DelayTime
)

--// Go to workspace, check your left and right curtain and its size, copy paste its axis.
local newSize = Vector3.new(1, 14, 5)  -- Shrink your curtain to what you want to open
local oldSize = Vector3.new(1, 14, 16) -- I added this if you want to close the curtain

local Goleft = {
	CFrame = left.CFrame * CFrame.new(0, 0, (newSize.Z - left.Size.Z)/2), -- Use (newSize.Z - left.Size.Z)/2 to change the size of curtain
	Size = newSize
}

local Goright = {
	CFrame = right.CFrame * CFrame.new(0, 0, (newSize.Z - right.Size.Z)/-2), -- I added -2 to keep it on the right direction.
	Size = newSize
}

local leftclose = {
	CFrame = left.CFrame * CFrame.new(0, 0, (oldSize.Z - left.Size.Z)/2), -- Use (newSize.Z - left.Size.Z)/2 to change the size of curtain
	Size = oldSize
}

local rightclose = {
	CFrame = right.CFrame * CFrame.new(0, 0, (oldSize.Z - right.Size.Z)/-2), -- I added -2 to keep it on the right direction.
	Size = oldSize
}


local Play = game:GetService("TweenService"):Create(left, tweenInfo, Goleft)
local Play1 = game:GetService("TweenService"):Create(right, tweenInfo, Goright)

local Play2 = game:GetService("TweenService"):Create(left, tweenInfo, leftclose)
local Play3 = game:GetService("TweenService"):Create(right, tweenInfo, rightclose)

CD.MouseClick:Connect(function()
if db == false then
--// Open Curtain
	Play:Play()
	Play1:Play()
	wait(2)
	db = true
	elseif db == true then
--// Close Curtain
	Play2:Play()
	Play3:Play()
	wait(2)
	db = false 
	end
end)

Let me know if it helps!

1 Like

Hey! This works out great, thanks for posting this. Not to familiar though with tweening so will have to look into it though :sweat_smile: . Though one issue I’m having is I’m finding when its tweening its pretty chunky in its animation, its not too smooth compared to a proper animation.

2 Likes

Can you provide a video if so? Its smooth for me. Thanks for the response btw.

It may just be me and my lag. And also do note the screen recording makes it even laggier because its recording.

Are you using a Script or a LocalScript? Tweening on the server is typically laggy.

3 Likes

Wow I am happy to see what you work there, now the problem is that its laggy because it only runs on the server side.
Are you familiar with local script with remote event? With this you don’t have to worry with the lag performance that only runs on the server, you can use local script to fire the client event.
(The only issue here is that it can be used with exploiter who has the handle with the local script with the remote that is connected to the server, you can use sanity checks).

You have to create a Remote Event for this to work and put it inside the replicatedstorage.

The Local script should be put inside
the starterplayer > starterplayerscripts

--// Local Script

local TS = game:GetService("TweenService")
local Left = game.Workspace.["Left Curtain"]
local Right = game.Workspace.["Right Curtain"]
local cd = game.Workspace.["Your Click Detector"]

local tweenInfo = TweenInfo.new(
	.7, -- You can adjust the time animation to your likings
	Enum.EasingStyle.Linear, -- EasingStyle
	Enum.EasingDirection.Out, -- EasingDirection
	0, -- RepeatCount (when less than zero the tween will loop indefinitely)
	false, -- Reverses (tween will reverse once reaching it's goal)
	0 -- DelayTime
)

--// It will show the tween from the client except the server
game.ReplicatedStorage:WaitForChild("RemoteEvent").OnClientEvent:Connect(function(part, goal)
	local play1 = TS:Create(part, tweenInfo, goal)
	play1:Play()
end)

--// With this parameter, it will call its CFrame Axis connected to serverscript
game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireServer(Left, Left.CFrame, Right, Right.CFrame)
--// Server Script

local left = workspace:WaitForChild("Left")
local right = workspace:WaitForChild("Right")
local cd = game.Workspace.["Your Click Detector"]
local db = false -- Debounce

local newSize = Vector3.new(1, 14, 5)  -- Shrink your curtain to what you want to open.
local oldSize = Vector3.new(1, 14, 16) -- I added this if you want to close the curtain

--// The OnServerEvent creates an event inside this function
game.ReplicatedStorage:WaitForChild("RemoteEvent").OnServerEvent:Connect(function(plr, part, partCFrame, part1)
cd.MouseClick:Connect(function()
	if db == false then
			local Goleft = {
				CFrame = left.CFrame * CFrame.new(0, 0, (newSize.Z - left.Size.Z)/2), -- Use (newSize.Z - left.Size.Z)/2 to change the size of curtain
				Size = newSize
			}
			local Goright = {
				CFrame = right.CFrame * CFrame.new(0, 0, (newSize.Z - right.Size.Z)/-2), -- I added -2 to keep it on the right direction.
				Size = newSize
			}
--// The FireAllClient runs to each client that's inside the game
		game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part, Goleft)
		game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part1, Goright)
		cd.MaxActivationDistance = 0 --// Added MaxActivationDistance so it waits for the tween to complete.
		print(1) -- Checks the the amount. If its greater than one then its spammable.
		wait(3) 
		cd.MaxActivationDistance = 32
		db = true
	else
			local leftclose = {
				CFrame = left.CFrame * CFrame.new(0, 0, (oldSize.Z - left.Size.Z)/2), -- Use (newSize.Z - left.Size.Z)/2 to change the size of curtain
				Size = oldSize
			}
			local rightclose = {
				CFrame = right.CFrame * CFrame.new(0, 0, (oldSize.Z - right.Size.Z)/-2), -- I added -2 to keep it on the right direction.
				Size = oldSize
			}
--// The FireAllClient runs to each client that's inside the game
			game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part, leftclose)
			game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part1, rightclose)
		db = nil
		cd.MaxActivationDistance = 0 --// Added MaxActivationDistance so it waits for the tween to complete.
		print(2) -- Checks the the amount. If its greater than one then its spammable.
		wait(3) 
		cd.MaxActivationDistance = 32 
		db = false
		end
	end)
end)

I am not very effecient when it comes to coding with remote events but for this script that I made with debounce and tween, the animation and the function is smooth. Also the Tween doesn’t run on server but the server script runs the script only for all client inside the server.

1 Like

So just to clear this up, when doing a local script is it only going to show on the client side when the curtains open/close or is it still going to show for everyone on the server?

1 Like

Also, where specifically I am supposed to insert those scripts? I have them all under the button to open the curtains, like it was for the original script with tweening. Though right now its not working for the local + server script with the remote event.

1 Like

Yes, the script only plays all the client not in the server meaning all the player saw the curtain moving, but the animation is not playing in the server meaning the tween doesn’t play the animation inside the server. Also forgot to mention the local script should be in starter player > starterplayerscripts.

1 Like

You can put the script anywhere but put the local script inside the starterplayer > starterplayerscript. Please mark it as solution if it helps.

Marked the message about the local/server script as solution.

1 Like

Can you show the vid if it works?

BTW just looked at the video and it made it so much more laggy, in game it works perfectly.

1 Like

Don’t care about the vid if its laggy, just show if it works perfectly and smooth in game :smile:. Thanks for the reply tho!

1 Like

Hello, I did some experiments with the scripts and it doesn’t update the new tween/animation when the new player joins. You can fix the bug here if you still want to make it playable.

Feel free to experiment with this script ↓

local cd = game.Workspace.Port.ClickDetector
local db = false
local left = workspace:WaitForChild("Left")
local right = workspace:WaitForChild("Right")

local newSize = Vector3.new(1, 14, 5)  -- Shrink your curtain to what you want to open.
local oldSize = Vector3.new(1, 14, 16) -- I added this if you want to close the curtain

local updateleft -- This just puts the value of the left curtain-axis
local updateright -- This just puts the value of the right curtain-axis

game.ReplicatedStorage:WaitForChild("RemoteEvent").OnServerEvent:Connect(function(plr, part, partCFrame, part1, Goleft, Goright, leftclose, rightclose)
	cd.MouseClick:Connect(function()
		if db == false and left.Size == oldSize then
			local Goleft = {
				CFrame = left.CFrame * CFrame.new(0, 0, (newSize.Z - left.Size.Z)/2), -- Use (newSize.Z - left.Size.Z)/2 to change the size of curtain
				Size = newSize
			}
			local Goright = {
				CFrame = right.CFrame * CFrame.new(0, 0, (newSize.Z - right.Size.Z)/-2), -- I added -2 to keep it on the right direction.
				Size = newSize
			}
			updateleft = Goleft  -- Updates the value of the left curtain-axis
			updateright = Goright -- Updates the value of the right curtain-axis
			print(updateleft, updateright)
			game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part, Goleft)
			game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part1, Goright)
			cd.MaxActivationDistance = 0
			print(1)
			wait(3) 
			cd.MaxActivationDistance = 32
			db = true
		else
			local leftclose = {
				CFrame = left.CFrame * CFrame.new(0, 0, (oldSize.Z - left.Size.Z)/2), -- Use (newSize.Z - left.Size.Z)/2 to change the size of curtain
				Size = oldSize
			}
			local rightclose = {
				CFrame = right.CFrame * CFrame.new(0, 0, (oldSize.Z - right.Size.Z)/-2), -- I added -2 to keep it on the right direction.
				Size = oldSize
			}
			updateleft = Goleft
			updateright = Goright
			game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part, leftclose)
			game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part1, rightclose)
			db = nil
			cd.MaxActivationDistance = 0
			print(2)
			wait(3) 
			cd.MaxActivationDistance = 32
			db = false
		end
	end)
	--// Updates the curtain's position when the new player joins
	game.Players.PlayerAdded:Connect(function()
		game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part, updateleft)
		game.ReplicatedStorage:WaitForChild("RemoteEvent"):FireAllClients(part1, updateright)
	end)
end)

You don’t have to make changes with the local script, just update the server script.

I added PlayerAdded function so that whenever the player joins it automatically updates the tween. Without it, it has to be clicked again to wait for the curtain to move the tween or somehow it doesn’t update the tween position when you leave and re-join the game.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.