Is There a More Efficient Way to do This?

I have a script that counts the amount a red and blue parts in the map, but it gets very laggy when there are a lot of parts.

Script:

local RS = game:GetService("RunService")
local RedParts
local BlueParts

RS.Heartbeat:Connect(function()
	RedParts = 0
	BlueParts = 0
	for i, Part in pairs(workspace.Map:GetDescendants()) do
		if not Part:IsA("BasePart") then continue end
		if Part.BrickColor == BrickColor.new("Really red") then
			RedParts += 1
		elseif Part.BrickColor == BrickColor.new("Really blue") then
			BlueParts += 1
		end
	end
	script.Parent.RedNumber.Text = tostring(RedParts)
	script.Parent.BlueNumber.Text = tostring(BlueParts)
end)

Also, this script is enabled and disabled from another script.

1 Like

Run it once first to get the initial count, then ideally you would update it from whatever changes colors or adds/removes them to adjust the count so you aren’t looping through everything in heartbeat.

2 Likes

I don’t think I understand. It still lags.

Current script:

local RS = game:GetService("RunService")
local RedParts
local BlueParts
local D = workspace.Map:GetDescendants()

RS.Heartbeat:Connect(function()
	RedParts = 0
	BlueParts = 0
	for i, Part in pairs(D) do
		if not Part:IsA("BasePart") then continue end
		if Part.BrickColor == BrickColor.new("Really red") then
			RedParts += 1
		elseif Part.BrickColor == BrickColor.new("Really blue") then
			BlueParts += 1
		end
	end
	script.Parent.RedNumber.Text = tostring(RedParts)
	script.Parent.BlueNumber.Text = tostring(BlueParts)
end)

Well how does the amount of red/blue parts change? Are you simply changing the color of parts? Adding/removing parts?

local Frame = script.Parent

local RedParts = 0
local BlueParts = 0

for _, Part in ipairs(workspace.Map:GetDescendants()) do
	if Part:IsA("BasePart") then
		if Part.BrickColor == "Really red" then
			RedParts += 1
		elseif Part.BrickColor == "Really blue" then
			BlueParts += 1
		end
	end
end
Frame.RedNumber.Text = tostring(RedParts)
Frame.BlueNumber.Text = tostring(BlueParts)

for _, Part in ipairs(workspace.Map:GetDescendants()) do
	if Part:IsA("BasePart") then
		Part:GetPropertyChangedSignal("BrickColor"):Connect(function()
			if Part.BrickColor == BrickColor.new("Really red") then
				RedParts += 1
				Frame.RedNumber.Text = tostring(RedParts)
			elseif Part.BrickColor == BrickColor.new("Really blue") then
				BlueParts += 1
				Frame.BlueNumber.Text = tostring(BlueParts)
			end
		end)
	end
end
1 Like

The parts start off as white. The parts can then be changed by a player to blue or red depending on their team.

This wouldn’t work because the parts can be changed from blue to red or vice versa.

You can make whatever changes the parts also parent them to folders named after their color and just change the textbox whenever a child is added/removed from those folders.

I wasn’t aware of that, bare with me.

Another solution would be to make an intValue for the different colors and have whatever changes colors add/subtract from that value and update the guis whenever that intValue changes.

local Frame = script.Parent

local RedParts = 0
local BlueParts = 0

for _, Part in ipairs(workspace.Map:GetDescendants()) do
	if Part:IsA("BasePart") then
		local OldColor = Part.BrickColor
		Part:GetPropertyChangedSignal("BrickColor"):Connect(function()
			if Part.BrickColor == BrickColor.new("Really red") then
				if OldColor == BrickColor.new("Really blue") then
					BlueParts -= 1
					Frame.BlueNumber.Text = tostring(BlueParts)
				end
				OldColor = Part.BrickColor
				RedParts += 1
				Frame.RedNumber.Text = tostring(RedParts)
			elseif Part.BrickColor == BrickColor.new("Really blue") then
				if OldColor == BrickColor.new("Really red") then
					RedParts -= 1
					Frame.RedNumber.Text = tostring(RedParts)
				end
				OldColor = Part.BrickColor
				BlueParts += 1
				Frame.BlueNumber.Text = tostring(BlueParts)
			end
		end)
	end
end

We’re good to go. I removed the first loop as you mentioned all of the parts start off as white.

2 Likes

I don’t think either solution would have worked, but thanks for the help!

I tried constructing a clean solution based on @tlr22 suggestion(when the color changes or a part is added/removed):

local RedParts = 0
local BlueParts = 0 

function check(bc, operation) --BrickColor, operation = true for addition or false for subtraction
	local red = BrickColor.new("Really red") 
	local blue = BrickColor.new("Really blue")
	local num = (operation and 1) or -1 --brackets aren't needed, added them for clarity
	if bc == red then 
		RedParts += num 
	elseif bc == blue then 
		BlueParts += num
	end
end

function update() --runs when the amount of blue/red parts changes
	print("Blue =", BlueParts, ", Red =", RedParts)
end

function DescendantAdded(object)
	if not object:IsA("BasePart") then return end 
	local old = object.BrickColor 
	check(object.BrickColor, true)
	object:GetPropertyChangedSignal("BrickColor"):Connect(function()
		check(object.BrickColor, true) 
		check(old, false) 
		old = object.BrickColor 
	end)
	update()
end

function DescendantRemoving(object) 
	if not object:IsA("BasePart") then return end 
	check(object.BrickColor, false)
	update()
end

for _, object in pairs(workspace.Map:GetDescendants()) do 
	DescendantAdded(object)
end
update()
workspace.Map.DescendantAdded:Connect(DescendantAdded)
workspace.Map.DescendantRemoving:Connect(DescendantRemoving)
2 Likes

You’re constantly checking all parts for changes. This is where the inefficiency is from.

Why not use a BindableEvent so the script changing the color can fire the event and update the counts that way?

Or just have it all handled in one script? (Touching the part would update the count on its own, without needing to check all parts)