How To Tell Which ClickDetector Has Been Triggered?

Hi! I’ve got a lot of cubes in a game that are generated through code and all of them have ClickDetectors. The problem though is that I don’t know how to efficiently detect which of these ClickDetectors have fired.

In my program, I have a grid of cubes that is generated through for loops with given starting parameters. Additionally, these cubes are added to a matrix for ease of accessibility. Here’s an example:

local sub = {}

for a = 1, value do
     local sub1 = {}
     for b = 1, value do
          local part = Instance.new("Part")
          (...lots of value editing and variable adding later...)
          local cD = Instance.new("ClickDetector")
          cD.MaxActivationDistance = 999999999
          cD.Parent = part
          table.insert(sub1,part)

          [[EXAMPLE SPOT 1]]

     end
     table.insert(sub,sub1)
end

[[EXAMPLE SPOT 2]]

In my code, I currently have a way to detect which cube was clicked, and then sends that information to the server via a RemoteEvent named singleClick which looks as follows:

cD.MouseClick:Connect(function(player

     singleClick:FireServer(a,b)

end)

This block sits in the place of [[EXAMPLE SPOT 1]], and once fired, the server sends a notice to all clients on the updated information. However, I’ve noticed that while this method works, it causes an INCREDIBLE amount of lag as the board increases. I suspect that has something to do with the placement of this block in the overall function. Even though in this example it seems slightly pointless to care about the efficiency, in my real game I will have potentially thousands of these cubes at a time, and I’ve noticed that the more there are, the longer the game freezes after a single click. I’ve simplified and optimized the code as much as I possibly can on the server-side, however the continued stutter causes me to think that the problem rests on the placement of where the signal for the click detection rests. As such, I’m suspecting placing the block in [[EXAMPLE SPOT 2]] will be the best thing I can do, however I don’t know how to do that. So, I’ve come here. Anyone know how I would detect which cube has been clicked from an arbitrarily large matrix of cubes? Thank you!

Nevermind, only now have I remembered that the print statement exists. As it turns out, it only triggers once per click, so I’m back to square one in trying to think of how to optimize this. Unfortunate, as this would greatly explain the problem I’ve been having with this. I’m still not really sure why it causes such a large lag spike, as the process goes as this: Player clicks and sends the coordinates to the server; Server packages the coordinates all nicely (unfortunately I can’t just send the coordinates on their own in this situation) and then sends that package to all players; Each player receives an update on the game’s information and modifies a value of the affected part. This shouldn’t be that intensive as it is simply sends a few numbers to the server and back to all the clients, and even after commenting all the code that changes things after the fact, the problem still persists. I truly have no idea what the problem could be other than what I demonstrated earlier with the example spots. Any help in this regard would be greatly appreciated, as I truly have no idea how to proceed in this matter.

It sounds like the lag issues might be related to networking or other code that is not shown here.

But related to ClickDetectors, I would highly recommend against using one for ever part in a large array of parts. Instead you could generate the parts with a tag and attribute that expresses it’s index in the matrix. Then have the client listen to mouse clicks broadly, and check if they are mousing over a part with a tag.

Unfortunately the lag really shouldn’t be related to other code as there’s almost none more to show, other than the ping to the server being inside 2 if statements.

But that might be a better solution than what I’m using now! Could you give an example of how I’d go about doing that?

It seems that it’s easier to send the clicked object rather than sending the gridpos from the attributes, but both methods are in the code if you want.

LocalScript in StarterPlayerScripts

local Players = game:GetService("Players")
local player = Players.LocalPlayer
local mouse = player:GetMouse()
local event = game:GetService('ReplicatedStorage'):WaitForChild('RemoteEvent')

local CLICKABLE_TAG = 'ClickableCube'

mouse.Button1Up:Connect(function()
	local target = mouse.Target
	if target and target:HasTag(CLICKABLE_TAG) then
		print('Client clicked:', target:GetAttribute('GridPosX'), target:GetAttribute('GridPosZ'))
		event:FireServer('Clicked', target)
	end
end)

Script in ServerScriptService

local GRID_SIZE_X = 10
local GRID_SIZE_Z = 10
local PART_WIDTH = 4
local CLICKABLE_TAG = 'ClickableCube'

local event = Instance.new('RemoteEvent')
event.Parent = game:GetService('ReplicatedStorage')

local templatePart = Instance.new('Part')
templatePart:AddTag(CLICKABLE_TAG)
templatePart.TopSurface = "Smooth"
templatePart.BottomSurface = "Smooth"
templatePart.Size = Vector3.new(1,1,1) * PART_WIDTH
templatePart.Anchored = true


local grid = {}
for x=1,GRID_SIZE_X do
	local gridX = {}
	grid[x] = gridX
	for z=1,GRID_SIZE_Z do
		local part = templatePart:Clone()
		part.CFrame = CFrame.new(x*PART_WIDTH, PART_WIDTH*.5, z*PART_WIDTH)
		part:SetAttribute('GridPosX', x)
		part:SetAttribute('GridPosZ', z)
		part.Parent = workspace
		gridX[z] = part
	end
end

local rng = Random.new()
event.OnServerEvent:Connect(function(player, requestType, target)
	if requestType == 'Clicked' then
		if typeof(target) == 'Instance' and target:HasTag(CLICKABLE_TAG) then
			print(player, requestType, target:GetAttribute('GridPosX'), target:GetAttribute('GridPosZ'))
			target.Color = Color3.fromHSV(rng:NextNumber(0,1),1,1)
		end
	end
end)