Displaying a text on screen according to the player's position to a part

Hello,

So I was making a script that shows a text on your screen when you’re close to a part, To do this I used a for loop and looped through a folder but I just realized that if the folder had multiple parts the text will never show up due to the fact that other parts may be not close to me, Im trying to see if there’s a better way to do this.

my script:

while wait() do
	for i, v in pairs(game.Workspace.Hays:GetChildren()) do
		local UpMag = (math.abs(player.Character.HumanoidRootPart.Position.Y) - math.abs(v.Position.Y))
		local DisMag = (math.abs(player.Character.HumanoidRootPart.Position.X) - math.abs(v.Position.X))
		local HoriMag = (math.abs(player.Character.HumanoidRootPart.Position.Z) - math.abs(v.Position.Z))
		
		
		if  math.floor(DisMag) <= 11 and math.floor(UpMag) >= script.MinHeight.Value and math.floor(HoriMag) <= 30 and math.floor(HoriMag) > 0 then
			if CanLeap == true then
				print("yes")
				player.PlayerGui.LOF_Msg.Msg.Visible = true
			else
				player.PlayerGui.LOF_Msg.Msg.Visible = false
			end
		else
			player.PlayerGui.LOF_Msg.Msg.Visible = false
		end
	end
end

Layout:
image

image

Why don’t you just make a hitbox that detects when a player is up there using the Part.Hit feature?

Because there is no hitbox, if the player is 14 studs above the part the text will appear

edit: mean to respond to @Warriorfoox

Something like a hitbox would be a better solution to this, because as it is you’re looping through some number of parts once per frame. It’s probably fine, but if you have a lot of parts that might get slow.

Anyways, the idea for loops like this is something like this (obviously you’ll need to adapt it)

local isCloseEnough = false
for i, v in pairs(children) do
    if (PlayerIsNearby(v)) then
        isCloseEnough = true
        break
    end
end

if (isCloseEnough) then
    print("yes")
else
    print("no")
end

Again though, I think there are better ways to achieve what you’re trying to do without this loop-check stuff.

Is there a way to check if the player moved or the player’s position changed?

GetPropertyChangedSignal, it exists. Not the preferred method, but yes.

Does it not work in local scripts?

It would be easier to describe what you’re trying to accomplish. You’ve told us “this is how I’m solving this problem: with a for loop”, but there’s probably a better way to do what you want.

Im trying to make a text appear when a person is atleast 14 studs higher than a part, I have multiple parts which mean i wonts be 14 studs high than every single part which makes the text always transparent due to the fact the part’s positions differ

Why not use an if statement for finding the magnitude of the part. If the players magnitude >= 14 then the distance of the part then the text will display.

I’m still not really sure why you’re doing what your doing, but that’s OK.

If you really want to keep doing it that way, GetPropertyChangedSignal won’t work to detect position changes. It’s bad with Position updates. And anyways, that changed event would fire pretty much every frame anyways, so you might as well skip the event and check every frame.

Here, I refactored your code and labeled where I changed things with comments. I haven’t tested it so there might be some typos, but it should be mostly fine. You have to do some work to integrate it with whatever system you’re using (for example, I assumed “player” was a variable because I don’t know where you got that from).

local folder = game.Workspace.Hays
local root = player.Character.HumanoidRootPart
local gui = player.PlayerGui.LOG_Msg.Msg

-- distance you must be within on the X axis
local maxX = 11
-- min on the Y axis
local minY = script.MinHeight.Value
-- max on the Z
-- issue 0: Why are your X and Z limits different?
local maxZ = 30

local function update()
    local shouldShow = false
    
    for _, part in pairs(folder:GetChildren()) do
        -- issue 1: you were subtracting the absolutes. You should absolute the
        -- subtraction instead:
        local dx = math.abs(root.Position.X - part.Position.X)
        local dy = math.abs(root.Position.Y - part.Position.Y)
        local dz = math.abs(root.Position.Z - part.Position.Z)
        
        -- issue 2: no need for math.floor
        -- also since dx,dy,dz are all positive now this check is simpler:
        if (dx < maxX and dy > minY and dz < maxZ) then
            -- issue 3: don't set the visibility on/off yet. Just determine that
            --  you should show it...
            shouldShow = true
            -- ...and exit the loop because we don't need to check anything else
            break
        end
    end
    
    -- issue 3b: ... NOW we can determine visibility because if we're within ONE
    -- of the parts, that loop up there would've caught it and set shouldShow.
    -- So, we can just use shouldShow directly:
    gui.Visible = shouldShow
end

-- issue 4: instead of a loop, we bind our update to the rendering step so it
-- just runs before anything gets rendered.

-- issue 5: As I said, you ideally wouldn't do this -- you're checking so often!
-- A better method would be to not do the distance checking at all, and instead
-- rely on Touched/TouchEnded events or something but for now this should work

game:GetService("RunService"):BindToRenderStep(
    "positionCheck",
    Enum.RenderPriority.First.Value,
    update
)

edit:

Also, I don’t know if you’re doing this in a persistent LocalScript or not - if you are, fine. If not, you’re going to need to add logic to bind/unbind that renderstep update when a player joins/leaves.