Hello everyone!
Even though this is a relatively old topic, and the original poster has probably had found an alternative solution, I decided to answer it now. Why is that? Because I saw at least two separate topics mentioning this loop problem, both of them unsolved, and I answered one similar questions today as well.
It’s time to break down the process.
What is actually happening inside the loop?
Original code
RunService.RenderStepped:Connect(function()
for _,Part in pairs(Parts:GetChildren())do
local magnitude = (HumanoidRootPart.Position - Part.Position).Magnitude
if (magnitude <= 10) then
frame.Visible = true
else
frame.Visible = false
end
end
end)
Looks fine, right? Yes it does, but doesn’t do what we expect it to do. What is happening here? RunService.RenderStepped runs on every rendered frame. Right after that, our for-loop starts. It loops through all parts, measures the magnitude between two vectors, namely position of HumanoidRootPart and one of the parts inside a folder, and through each pass magnitude is compared with number 10. Frame is set visible again as long as the condition is met. However! The GUI frame only shows if player is standing near one particular part. The loop runs very fast and passes through the whole table, continuously changing magnitude value according to the current part, and continuously comparing. Player cannot see any difference. It isn’t visible until the last element in given table is reached. Because it is last in order, loop doesn’t repeat, but the GUI is rather set visible and code execution pauses until next frame is rendered, which calls the whole process to run again.
How to fix this behaviour?
Instead of using RunService, I chose a while-loop. Repeat rate of RunService is way to high in for this case.
In short, we will loop through the table again, but this time search for the lowest magnitude value. First we will define “default”, starting one, later compare it with new ones, and if necessary, replace old value with lower number.
while (true) do
--[[
Reset magnitude value each time, so nearest
part is not permanently determined. Otherwise,
magnitude would be once set to the lowest value,
and left unchanged.
]]
magnitude = 100
for _, part in pairs(Parts) do
local partDist = (hrp.Position - part.Position).Magnitude
-- Compare magnitudes and find the nearest part.
if (not magnitude or partDist < magnitude) then
magnitude = partDist
nearest = part
end
end
if (magnitude <= 10) then
frame.Visible = true
else
frame.Visible = false
end
wait(.1)
end
We have two options now. Either to use the above example, or to save three lines of code and use built-in method DistanceFromCharacter().
local partDist = Players.LocalPlayer:DistanceFromCharacter(part position)
Same calculations, both ways base on magnitude, most likely both between HumanoidRootPart’s position and given part’s position.
Finally, here is the whole code:
local Players = game:GetService("Players")
local frame = Players.LocalPlayer.PlayerGui.ShopGui.Frame; frame.Visible = false
local StoreBranches = workspace.StoreBranches:GetChildren()
local magnitude, nearest
while (true) do
--[[
Reset magnitude value each time, so nearest
part is not permanently determined.
]]
magnitude = 100
for _, store in pairs(StoreBranches) do
local dist = Players.LocalPlayer:DistanceFromCharacter(store.Position)
-- Compare magnitudes and find the nearest part.
if (not magnitude or dist < magnitude) then
magnitude = dist
nearest = store
end
end
if (magnitude <= 10) then
frame.Visible = true
else
frame.Visible = false
end
wait(.1)
end
Alternatively, Region3 could be used (because it is excelent, accurate, yet performance demanding if used thoughtlessly). Another good alternative would be detection done via .Touched connection. The former is probably better, because it’s simple and doesn’t run routinely (.Touched only listens for changes).
Cheers!