How detect if player near light source, so I will be able to Enable Light

I want make some optimization for my game. The light is one example.
The problem is that I know only 1 way to detect if player nearby Light - Vector3.Magnitude.
But there’s 1 problem - what if there… 100-1000 light instances? It will probably just lag game.
So, can someone say me - is there any better ways to achive this?

2 Likes

Maybe for every light make script like:

for _,light in ipairs(lightList) do
local lightPos = light.Position
local PlrPos = Player.Character.HumanoidRootPart.Position
while true do
if lightPos.X - PlrPos.X <= 100 then
light.Enable -- Or some that will enable light
end
end
end
1 Like

As simple as it may sound the usual approach is to make batches, this is referred to as spatial indexing and often uses a QuadTree for 2D spatial indexing and a Octree for 3D points.

If you have a static level with pre-determined light sources and rooms you might have an easier time making this by manually specifying regions and lights to go with those regions, say an invisible part with the lights as children. When the player touches this invisible part you add it’s children to a list of lights to check, when the TouchEnded event fires you remove them from the list.

2 Likes

I think Roblox’s engine already handles performance things like this. I’ve only seen issues with shadow enabled lights. But I would use GetPartBoundsInRadius from the HumanoidRootPart’s position to get all light parts within range.

4 Likes

You would need to use CollectionService to tag all the Lights of your game. It’s really useful to use it as it’ll reduce alot the Cluster to organize things. You can check-out a Plugin about it here.

Right after this, Create a tag named “Light” and tag all of your Light Instances (Light Objects, Such as SpotLight, PointLight, SurfaceLight, etc) with it. Then you can loop through each Tagged Instance with the “Light” tag every Frame and update it. We can check the Distance between the Player’s Character and the Light’s ancestor.

The code would need to be Client-sided as we only want to update it for the Client. This is what it would look like inside a script in “StarterCharacterScripts”

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

local LocalPlayer = Players.LocalPlayer
local LocalCharacter = LocalPlayer.Character or LocalPlayer.CharacterAdded:Wait()
local LocalRootPart = LocalCharacter:WaitForChild("HumanoidRootPart")

local TaggedLightInstances = CollectionService:GetTagged("Light")
local UpdateTaggedAdded = CollectionService:GetInstanceAddedSignal("Light"):Connect(function() TaggedLightInstances = CollectionService:GetTagged("Light") end)
local UpdateTaggedRemoved = CollectionService:GetInstanceRemovedSignal("Light"):Connect(function() TaggedLightInstances = CollectionService:GetTagged("Light") end)

local LIGHT_DISTANCE = 100

RunService.RenderStepped:Connect(function()
	if LocalRootPart and #TaggedLightInstances > 0 then
		for _, Light in ipairs(TaggedLightInstances) do
			-- Ensure the Object is actually a Light Source
			if Light:IsA("Light") then
				-- Find the Ancestor and get the Distance
				-- If there's not an Ancestor which's a BasePart or Attachment, It's gonna get ignored
				local Ancestor = Light:FindFirstAncestorWhichIsA("BasePart") or Light:FindFirstAncestorWhichIsA("Attachment")
				local Distance = if Ancestor then (LocalRootPart.Position - Ancestor.Position).Magnitude else nil

				-- Are we close to the Distance?
				-- Yes; Enable
				-- No; Disable
				if Distance then
					if Distance > LIGHT_DISTANCE then
						Light.Enabled = false
					elseif Distance <= LIGHT_DISTANCE then
						Light.Enabled = true
					end
				end
			else
				continue
			end
		end
	end
end)
2 Likes

I think OverlapParams would be better as it doesn’t require iterating through an entire list every step, only those within range. Tags are certainly useful, but I’m not so sure if there are hundreds.

5 Likes