How to detect Instances with a certain tag near the player?

I know that looping through every Instance (for my case, MeshParts) and then checking their distance from the player’s camera is the traditional way of solving a problem such as this, but is there any faster method to quickly fetch items with a tag near a certain position in workplace?

(This will be called every time the CFrame of the player’s camera changes, so that it can animate parts with a certain tag that the user can see using EditableMesh)

I’m not too sure on this one, but you can try using the Magnitude.

workspace.CurrentCamera:GetPropertyChangedSignal:Connect(function()
    if part.Magnitude --[[where 'part' is the part]] - game.Players.LocalPlayer.Character.HumanoidRootPart.Magnitude <= 10  then--within 10 studs of each other
        print("Less than 10 studs away")
    end
end)

Sorry that this might be a bit annoying to read. I’m not sure if this will entirely work - I forgot where the Magnitude comes from. Hope it helps!

By that I meant different from the ordinary way, of looping through every item and checking its distance, which is what you provided.

(pss: Distance between p0 and p1 is (p0.Position - p1.Position).Magnitude )

My bad. I mean, it’s pretty hard to do it without a loop. You could use the CollectionService to collect each part with the tag, and then you might just have to loop through them, checking their distance from the camera. There might be some other way I’m not aware of, though - there may be some information in the Creator Documentation.

Cooked up this function which just loops through all items with the tag, like the traditional method.

local meltTag = "meltPart"
local collect = game:GetService("CollectionService")
local assets = game:GetService("AssetService")

local lplr = game.Players.LocalPlayer
local cam = workspace.CurrentCamera

local config = 
	{
		enabled = true,
		searchRadius = 15
	}

local knownMeltParts = {}
local cachedTopVerticies = {}

for _, melt in collect:GetTagged(meltTag) do
	if knownMeltParts[melt] == nil then
		knownMeltParts[melt] = true
		melt.Destroying:Once(function()
			knownMeltParts[melt] = nil
		end)
	end
end

collect:GetInstanceAddedSignal(meltTag):Connect(function(object)
	if knownMeltParts[object] == nil then
		knownMeltParts[object] = true
	end
end)

local function manipulateAnimate(mesh:EditableMesh)
	local topVerticies = cachedTopVerticies[mesh]
	local connection : RBXScriptConnection
	if mesh.Parent and mesh.Parent:IsA("BasePart") and (not topVerticies) then
		local yConstraint = mesh.Parent.Size.Y / 2
		if topVerticies == nil then
			topVerticies = {}
			for _, vId in mesh:GetVertices() do
				local origin = mesh:GetPosition(vId)
				if origin.Y >= yConstraint then
					topVerticies[vId] = origin
				end
			end
			cachedTopVerticies[mesh] = topVerticies
		end
	end
	local specialSeedX = lplr.UserId + os.clock() + os.time()
	local specialSeedZ = lplr.UserId - os.time() * os.clock()
	connection = game["Run Service"].Heartbeat:Connect(function(dt)
		local clock = os.clock()
		for id, origin in topVerticies do
			local noise1 = math.noise(origin.X * specialSeedX + math.sin(clock) * clock, origin.Z * specialSeedZ + math.cos(clock) * clock)
			local newY = Vector3.yAxis * .1 * noise1
			mesh:SetPosition(id, origin + newY)
		end
	end)
	return
		{
			cancel = function()
				connection:Disconnect()
				for id, origin in topVerticies do
				mesh:SetPosition(id, origin)
			end
			end,
		}
end

local lastseen = {}

local function  renderRefresh()
	local view = cam.CFrame	
	local nearby = {}
	local stillseen = {}
	local ignoreseen = {}
	for melt: MeshPart, f in lastseen do
		local onScreen = cam:WorldToScreenPoint(melt.Position)
		if onScreen then
			if #cam:GetPartsObscuringTarget({view.Position, melt.Position}, {melt}) == 0 then
				stillseen[melt] = true
			else
				f:cancel()
				lastseen[melt] = nil
				ignoreseen[melt] = true
			end
		else
			f:cancel()
			lastseen[melt] = nil
			ignoreseen[melt] = true
		end
	end
	for melt: MeshPart, _ in knownMeltParts do
		if not stillseen[melt] and not ignoreseen[melt] then
			local cameraDistance = (view.Position - melt.Position).Magnitude
			if cameraDistance <= config.searchRadius then
				local _, onscreen = cam:WorldToScreenPoint(melt.Position)
				if onscreen then
					if #cam:GetPartsObscuringTarget({view.Position, melt.Position}, {melt}) == 0 then
						local editable = cam:FindFirstChildWhichIsA("EditableMesh")
						if editable then
							lastseen[melt] = manipulateAnimate(editable)
						end
					end
				end
			end
		end
	end
end

cam:GetPropertyChangedSignal("CFrame"):Connect(renderRefresh)

script no workie :frowning:

(can’t sence when a melt becomes visible/invisible to the player)

nvm sorted it out
(my waltuh mask was obscuring it)

ok ok it works but is laggy, very laggy, how can i optimize?

It’s because you are updating it every time the camera moves, and it is a lot of code to run each time. Maybe try adding a cooldown system so that it doesn’t refresh every time the camera moves, but it still works to the extent that the cooldown isn’t noticeable.

If you read any of the source that I posted earlier, you’ve noticed that I’ve used the first 2.

I don’t know if this is what you were trying to do, but here’s what I got:

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local CollectionService = game:GetService("CollectionService")

local player = Players.LocalPlayer

local TAG_NAME = "Apple"
local DISTANCE = 8

RunService.Heartbeat:Connect(function()
	local character = player.Character
	local primaryPart = character and character.PrimaryPart
	if not character or not primaryPart then return end
	for _, object in ipairs(CollectionService:GetTagged(TAG_NAME)) do
		if object:IsA("BasePart") and (primaryPart.Position - object.Position).Magnitude < DISTANCE then
			object.Color = Color3.fromRGB(0, 255, 0)
		elseif object:IsA("BasePart") then
			object.Color = Color3.fromRGB(255, 0, 0)
		end
	end
end)

If you want to use CameraPosition then:

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local CollectionService = game:GetService("CollectionService")
local Workspace = game:GetService("Workspace")

local player = Players.LocalPlayer

local TAG_NAME = "Apple"
local DISTANCE = 8

RunService.Heartbeat:Connect(function()
	local currentCamera = Workspace.CurrentCamera
	
	if not currentCamera then return end
	for _, object in ipairs(CollectionService:GetTagged(TAG_NAME)) do
		if object:IsA("BasePart") and (currentCamera.CFrame.Position - object.Position).Magnitude < DISTANCE then
			object.Color = Color3.fromRGB(0, 255, 0)
		elseif object:IsA("BasePart") then
			object.Color = Color3.fromRGB(255, 0, 0)
		end
	end
end)

modified the code so that it doesnt animate and deanimate the mesh suddenly, which happens when the player moves it camera to look / not look at the mesh
image
edit: excess parts fixed
image