Selecting a block and a Box appears on it

i want to make a block select system where when you hover over a part that has a certain name or is part of the ‘CollectionService’ or a folder a selection box pops up in the part but when you leave hovering it disappears to the ‘ReplicatedStorage’

Also within this i want to make it so when i click the part when im hovering over it gives money

i have tried the Following:

My Module Script:

local blocks = {
	["GreenBlock"] = {
		["Name"] = "Green Block";
		["IsSelected"] = false;
	};
	["RedBlock"] = {
		["Name"] = "Red Block";
		["IsSelected"] = false;
	};
}

return blocks

My BlockScript:

local BlocksModule = require(game:GetService("ReplicatedStorage").Blocks)
local SelectedB = game:GetService("ReplicatedStorage").Selected
		local Blocks = 0

game.Players.PlayerAdded:Connect(function(plr)
	for keyName, keyValue in pairs(BlocksModule) do
		
		local CD = game.Workspace.Blocks:FindFirstChild(keyName).CD
		
		CD.MouseHoverEnter:Connect(function()
			SelectedB.Parent = game.Workspace.Blocks:WaitForChild(keyName)
			SelectedB.Adornee = game.Workspace.Blocks:WaitForChild(keyName)
			print("Hovering")
		end)
		CD.MouseHoverLeave:Connect(function()
			SelectedB.Parent = game:GetService("ReplicatedStorage")
			SelectedB.Adornee = nil
			print("Mouse Left")
		end)
		
		CD.MouseClick:Connect(function(plrWhoClicked)
			SelectedB.Parent = game:GetService("ReplicatedStorage")
			SelectedB.Adornee = nil
	
				Blocks += 1
				print(tostring(Blocks))
				CD.Parent:Destroy()
				
		end)
	end
end)

i don’t know what to use, a ClickDetector or the mouse() function

I feel that using a ClickDetector is easier because of its built-in functions. If you go the ClickDetector route, you should use a LocalScript for the events, or write this:

Server

CD.MouseHoverEnter:Connect(function(playerWhoHovered)
    remote:FireClient(playerWhoHovered, CD.Parent)
end)

Client

remote.OnClientEvent:Connect(function(block)
    selectionBox.Parent = workspace
    selectionBox.Adornee = block
end)

This makes it so that the selection box is only visible to the client that is hovering over the block.
You should cut the iteration inside of the Players.PlayerAdded event and paste it outside of the event; and make sure to add player as an argument in all three of those ClickDetector events.

And I do not know if you did this or not. However, if you added ClickDectectors to every block manually, you should instead make a module for every block that does that for you.

So if i add the ClickDetectors manually will that make it easier when players join the game then

No. However, using a ModuleScript to create the blocks and add the ClickDetectors into the blocks will be more easy than doing it manually. And it will come in handy if you want to add more blocks during a game.

this game is only test worthy for the minute so ill use manually first but i need help with the module part.

also if i use the collectionservice to go through parts with the given tag with things run okay?

Using CollectionService to iterate through the blocks should be fine.
Edit: Just make sure that you are using the ClickDetector events properly. Your current set-up is incorrect. You should not be iterating through all the blocks every time a player joins, too.

oh ok thank you because im going to use that instead of modules until i get to know things better with them.
thank you for your time :slight_smile:

i have this new set up

for i, v in pairs(CollectionService:GetTagged("Selectable")) do
	local CD = Instance.new("ClickDetector")
	CD.Name = "CD_For_Block_"..v.Name
	CD.Parent = v
	
end

is that fine

Yes, that is very well. Do not forget to add player as the parameter for the events, and use that to your advantage if you can.

already ahead with that one but if things go wrong ill come back :slight_smile:

I dont like this script at all, instead I suggest to handle it completely client-sidedly.

Get the LocalPlayer first.

local LocalPlayer : Player = game:GetService("LocalPlayer")

Then we get the mouse:

local mouse = LocalPlayer:GetMouse()

Now every frame we check what the mouse is on top of (because of course parts can move, your camera can move and your mouse can move. connecting to all of those things would not be efficient)
So we use RunService:BindToRenderStep()

RunService:BindToRenderStep("MouseUpdate", 1, function()
   ... -- Code below
end)

Now we will have to get the target, the part the mouse is on top of

local target = mouse.Target -- Part which the mouse is on top of

Now you just have to verify the target is inside your CollectionService, has an attribute or anything like that and you are set.

Then you have to make a TextLabel which displays the name of the part you are on top of (this is why we will put the LocalScript inside a ScreenGui in StarterGui, unless you want to use a billboard Gui or anything like that)

Now if you want the TextLabel to follow the mouse we can use mouse.X and mouse.Y if I am right these are pixel values, not scale values.

TextLabel.Size = UDim2.fromOffset(mouse.X, mouse.Y)

Now once you have verified you can change the text change it to

TextLabel.Visible = true
TextLabel.Text = target.Name

If it’s incorrect we make it invisible

TextLabel.Visible = false
local plr = game.Players.LocalPlayer

local replicatedStorage = game:GetService("ReplicatedStorage")
local Selected = replicatedStorage.Selected

replicatedStorage.Events.test1.OnClientEvent:Connect(function(Block)
	Selected.Parent = workspace
	Selected.Adornee = Block
end)

replicatedStorage.Events.test2.OnClientEvent:Connect(function(Block)
	if Selected.Parent ~= replicatedStorage or Selected.Parent == Block then
		Selected.Parent = replicatedStorage
		Selected.Adornee = nil
	end
end)

what i have so far for @Ovibion advice is that okay for the local script

and @KJry_s i will try yours now

You should only use one RemoteEvent for this situation. Add an action argument. Like this:

event.OnClientEvent:Connect(function(block, action)
    if action == "enter" then
        Selected.Parent = workspace
	    Selected.Adornee = Block

    elseif action == "leave" then
        Selected.Adornee = nil
        Selected.Parent = replicatedStorage
    end
end)

so add like replicatedStorage.Events.test1:FireClient(plr, v, “Leave”)

Yes, you should do that. This is a very good practice. And you do not have to define so many remotes, either, if you have a lot of different actions/events.

1 Like

i know all this stuff but because im testing im not doing that but all this stuff you have taught me is a big step for me thank you very much

@KJry_s i need help with your code

local LocalPlayer : Player = game:GetService("LocalPlayer")
local replicatedStorage = game:GetService("ReplicatedStorage")
local Selected = replicatedStorage.Selected
local CollectionService = game:GetService("CollectionService")

local mouse = LocalPlayer:GetMouse()

local RunService = game:GetService("RunService")

RunService:BindToRenderStep("MouseUpdate", 1, function()
	if mouse then
		local target = mouse.Target
		for i, v in pairs(CollectionService:GetTagged("Selectable")) do
			if target.Name ~= nil	 then return end
			Selected.Parent = workspace
			Selected.Adornee = v
			end
	end
end)

i got errors

What kind of errors, could you give them or tell me what the issue at hand is.

just says cant get the mouse from it

Make sure it is a LocalScript, this has to be handled client-sidedly instead of server-sidedly.