Reducing the Performance Hit of Tweening Hundreds of Parts on the Client

Explanation

The script first starts playing music and waits until it reaches a certain time position in the song, then this function starts running :

function AnimateParts(Player, Radius)
	EffectedObj = {}
	workspace.TempParts:ClearAllChildren()
	local SkyBox = Instance.new("Sky")
	SkyBox.Name = "Galaxy"
	SkyBox.SkyboxBk = "http://www.roblox.com/asset/?id=159454299"
	SkyBox.SkyboxDn = "http://www.roblox.com/asset/?id=159454296"
	SkyBox.SkyboxFt = "http://www.roblox.com/asset/?id=159454293"
	SkyBox.SkyboxLf = "http://www.roblox.com/asset/?id=159454286"
	SkyBox.SkyboxRt = "http://www.roblox.com/asset/?id=159454300"
	SkyBox.SkyboxUp = "http://www.roblox.com/asset/?id=159454288"
	SkyBox.StarCount = 0
	SkyBox.CelestialBodiesShown = false
	SkyBox.Parent = game:GetService("Lighting")
	for i, v in pairs(game:GetService("Workspace"):GetDescendants()) do
		if v:IsA("Decal") or v:IsA("Texture") then
			if not v:IsDescendantOf(Player.Character) then
				local Data = {
					Object = v;
					Transparency = v.Transparency
				}
				v.Transparency = 1
				table.insert(EffectedObj, Data)
			end
		end
	end
	for i, v in pairs(game:GetService("Workspace"):GetDescendants()) do
		if v:IsA("BasePart") and not v:IsA("Terrain") and not v:IsDescendantOf(Player.Character) then
			local Data = {
				Object = v;
				Transparency = v.Transparency
			}
			v.Transparency = 1
			if (Player.Character.HumanoidRootPart.Position - v.Position).magnitude <= Radius then
				local Clone = v:Clone()
				Clone.CanCollide = false
				Clone.Transparency = 0
				Clone.Parent = workspace.TempParts
				local MaxRot = 20
				TweenService:Create(Clone, TweenInfo.new(1.2), {
					Size = v.Size/3; 
					Orientation = Vector3.new(math.random(0, MaxRot) + v.Orientation.X, math.random(0, MaxRot) + v.Orientation.Y, math.random(0, MaxRot) + v.Orientation.Z);
					Transparency = 1
				}):Play()
			end
			table.insert(EffectedObj, Data)
		end
	end
end

Basically this function finds parts in a specified radius around the player ( the part count could range from 20 - 400 parts ), and makes these parts disperse in random rotations, then they disappear. ( All of this is running inside of a local script )

Problems

The script works fine and all, but there are two issues :

  • Each time the animation starts playing a few frames get skipped. In this video you can see that while I’m walking it freezes :

( This is the main problem i want to fix )

  • I want to exclude all player characters from the animated parts. ( Currently I’m only excluding the local player, because I’m not sure how to exclude all players )

Ideas

I’ve got two ideas for fixing the freeze frames right now :

  1. Lower the amount of parts being animated

  2. Don’t use TweenService ( e.g. maybe I could try using a for loop, but I’m not sure how I would make that work )

Lowering the amount of parts

This one probably isn’t too hard to implement. The reason why I wouldn’t want to reduce the amount of parts too much, is because it won’t look the same as originally intended. If I had to implement this, I would first get a list of the parts inside of the radius, then get rid of 40% of the parts in the list. ( I’m not sure how to do this in a script though )

Avoiding TweenService

In my opinion this would be the best option. The reason for doing this would be to reduce the smoothness of the tween, so it gets updated less frequently. ( Don’t know if this would have the ultimate performance improvement though )

Of course if you have a better idea post that instead.

Addition

These might not even be the main reasons for why the script causes lag in the beginning. Maybe another option would be to preload the assets for the skybox?

If you have any other ideas on how my code could be improved please tell me!

I feel like a simple way of doing this is to loop through workspace, setting everything to transparent instantly. Stay away from TweenService, it might look choppy but it isn’t worth the frame lag especially for low end computers.

There’s some things in the script I’m not sure of, which is you’re checking for radius within the characters position, is there a reason for this? I feel like it’s doing more harm than good.

The reason is because it reduces the parts affected. ( e.g. in my game there’s an island that’s made out of lots of triangles, I don’t want to animate that island if I’m not located on it )

Could you set up a system where it keeps track of what island you’re on?

Let’s saying you’re on Island 1 and the script knows that, it can instantly know just to animate Island 1. This would help the script because it isn’t iterating through hundreds of parts that are on other islands.

Sadly this won’t work because I’m trying to make it work on multiple games. ( like an admin command )
Right now it’s iterating through the workspace 2 times, which is probably the cause of the lag. I’m planning on making it so it iterates trough workspace once, and marks which objects should be changed, then later on it will go trough this list and complete the animation. If this does fix the issue I still want to find out how to exclude all players from the part list

Since you’re iterating through workspace, have a function that checks if it’s model and it has a Humanoid, if it is then check if the model name matches a name of the player in Players service.

Would it work if you collected information on where all the players are? Let’s say there’s players in Island 1, 2 and 4. Since the script knows there’s no one in Island 3, it could avoid it?

1 Like

Thanks, I completely forgot that I could use a function, but I used a slightly different method than the one you mentioned. This is the result :

function CheckPlayer(Object)
	local Check = false
	for i, v in pairs (game:GetService("Players"):GetChildren()) do
		if Object:IsDescendantOf(v.Character) then
			Check = true
		end
	end
	return Check
end

I’ve changed the rest of the functions, now it first creates a list of the parts that need to be changed, then makes the changes. I’ve also added a part limit so it doesn’t animate more than 500 parts.

In this video you can see that some of the lag is reduced, and works even in areas with lots of parts :
( Of course if I ever need to reduce the lag even more I can change the max amount of parts )

Here are the rest of the functions :

function Prepare(Player, Radius, MaxParts)
	AnimatedParts = {}
	InvisibleObj = {}
	local PartCount = 0
	for i, v in pairs(game:GetService("Workspace"):GetDescendants()) do
		pcall(function()
			if not v:IsA("Terrain") and not CheckPlayer(v) then
				if v:IsA("Decal") or v:IsA("Texture") then
					local Data = {
						Object = v;
						Transparency = v.Transparency
					}
					table.insert(InvisibleObj, Data)
				elseif v:IsA("BasePart") then
					local Data = {
						Object = v;
						Size = v.Size;
						Orientation = v.Orientation;
						Transparency = v.Transparency
					}
					if (Player.Character.HumanoidRootPart.Position - v.Position).magnitude <= Radius and PartCount <= MaxParts then
						PartCount = PartCount + 1
						table.insert(AnimatedParts, Data)
					else
						table.insert(InvisibleObj, Data)
					end
				end
			end
		end)
	end
end

function AnimateParts(Player)
	local SkyBox = Instance.new("Sky")
	SkyBox.Name = "Galaxy"
	SkyBox.SkyboxBk = "http://www.roblox.com/asset/?id=159454299"
	SkyBox.SkyboxDn = "http://www.roblox.com/asset/?id=159454296"
	SkyBox.SkyboxFt = "http://www.roblox.com/asset/?id=159454293"
	SkyBox.SkyboxLf = "http://www.roblox.com/asset/?id=159454286"
	SkyBox.SkyboxRt = "http://www.roblox.com/asset/?id=159454300"
	SkyBox.SkyboxUp = "http://www.roblox.com/asset/?id=159454288"
	SkyBox.StarCount = 0
	SkyBox.CelestialBodiesShown = false
	SkyBox.Parent = game:GetService("Lighting")
	for i, v in pairs(InvisibleObj) do
		v.Object.Transparency = 1
	end
	for i, v in pairs(AnimatedParts) do
		local Clone = v.Object:Clone()
		Clone.CanCollide = false
		v.Object.Transparency = 1
		Clone.Parent = workspace.TempParts
		local MaxRot = 20
		TweenService:Create(Clone, TweenInfo.new(1.5), {
			Size = v.Size/3; 
			Orientation = Vector3.new(math.random(0, MaxRot) + v.Orientation.X, math.random(0, MaxRot) + v.Orientation.Y, math.random(0, MaxRot) + v.Orientation.Z);
			Transparency = 1
		}):Play()
	end
end