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!
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)
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.
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)
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