Making this not a loop

I have this code run whenever the player respawns and preferably, I’d like to have a similar very responsive and quick effect that while wait() do has but obviously not using a loop. There’s many ways to go about this, but what solution would have the least proformance impact and still achive the quick responsive effect that a while loop has?

local lastZone
while wait() do --TODO: Make this not a loop
	for i, zone in pairs(ReplicatedStorage.LocationRegions:GetChildren()) do
		for x, hitbox in pairs(zone:GetChildren()) do
			local min = hitbox.Position - hitbox.Size * 0.5
			local max = hitbox.Position + hitbox.Size * 0.5
			local partsFound = workspace:FindPartsInRegion3WithWhiteList(Region3.new(min, max), game.Players.LocalPlayer.Character:GetDescendants(), 100)
			for y, part in pairs(partsFound) do
				if part.Name == "HumanoidRootPart" and lastZone ~= zone then
					lastZone = zone
					updateLocation(zone.Name)
				end
			end
		end
	end
end
1 Like

You can try recursive functions, essentially you’d call the function inside of itself near the end.

local function loop()

    --you code here--
    loop() --run forever

end

loop() --initially call it

Unlike while loops, such functions don’t require waits in most cases. They’re only required when you are executing something big like generating a whole map of procedural terrain.

1 Like

I have a question: What does this script achieve?

It checks the player’s location to see what area of my map they are in and whenever it changes, it fires the updateLocation() function which displays their new location.

That seems like a recipe for a stack overflow. Use an event based system.

local lastZone
local function check()
	local min, max, partsFound -- basic optimization strategy, define variables outside loops
	for i, zone in pairs(ReplicatedStorage.LocationRegions:GetChildren()) do
		if lastZone ~= zone then
			for x, hitbox in pairs(zone:GetChildren()) do
				min = hitbox.Position - hitbox.Size * 0.5
				max = hitbox.Position + hitbox.Size * 0.5
				partsFound = workspace:FindPartsInRegion3WithWhiteList(Region3.new(min, max), game.Players.LocalPlayer.Character:GetDescendants(), 100)
				for y, part in pairs(partsFound) do
					if part.Name == "HumanoidRootPart" then
						lastZone = zone
						updateLocation(zone.Name)
						return -- no need to continue after the zone is found?
					end
				end
			end
		end
	end
end
game:GetService("RunService").RenderStepped:Connect(check)
2 Likes

So this would preform better than a while loop?

Maybe firing the script when the player’s position changes would be better than a loop using the .Changed function

1 Like

One quick revision that would improve preformance:

I noticed that I check if the last zone isn’t current zone when it could be done earlier to avoid doing any raycasting if the player is still in the same area as before.

local lastZone
while wait() do --TODO: Make this not a loop
	for i, zone in pairs(ReplicatedStorage.LocationRegions:GetChildren()) do
		if lastZone ~= zone then
			for x, hitbox in pairs(zone:GetChildren()) do
				local min = hitbox.Position - hitbox.Size * 0.5
				local max = hitbox.Position + hitbox.Size * 0.5
				local partsFound = workspace:FindPartsInRegion3WithWhiteList(Region3.new(min, max), game.Players.LocalPlayer.Character:GetDescendants(), 100)
				for y, part in pairs(partsFound) do
					if part.Name == "HumanoidRootPart" then
						lastZone = zone
						updateLocation(zone.Name)
					end
				end
			end
		end
	end
end

You could maybe also speed it up by breaking out of the zones loop once the player’s position is found. (see my revised post)

1 Like

Will do. Thanks for the help!

30chars