You can write your topic however you want, but you need to answer these questions:
I want to make it so the Rocks and only the rocks are Highlighted and make it so there name pops up when mous is hovering over the rock along with the rocks respective health.
I want to make it so all this dissapears when your mouse leaves the rock and when the rock is destroyed
The script Works but when the new rocks spawn it no longer works and it only works half the time this is reeally confusing me anything helps
Ive tried looking On the forum looking it up and trying to find any people with other similar issues to mine
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local StarterGUI = game:GetService("StarterGui")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Collection = game:GetService("CollectionService")
local RFolder = game.Workspace.Rocks
-- Tagging new rocks
local function addTagToRock(rock)
Collection:AddTag(rock, "Highlitable")
end
-- Tag initial rocks
for _, rock in RFolder:GetChildren() do
addTagToRock(rock)
end
-- Tag new rocks as they are added
RFolder.ChildAdded:Connect(function(newRock)
addTagToRock(newRock)
end)
-- Highlight logic
local function getRootTarget(target)
while target and not Collection:HasTag(target, "Highlitable") do
target = target.Parent
end
return target
end
mouse.Move:Connect(function()
local target = getRootTarget(mouse.Target)
if target then
script.Highlight.Adornee = target
else
script.Highlight.Adornee = nil
end
end)
-- Health popup logic
for _, Block in RFolder:GetChildren() do
local ClickDetector = Block:FindFirstChild("ClickDetector") or Instance.new("ClickDetector", Block)
ClickDetector.MaxActivationDistance = 10
ClickDetector.MouseHoverEnter:Connect(function()
player.PlayerGui.MineGUI.RockHealth.Visible = true
end)
ClickDetector.MouseHoverLeave:Connect(function()
player.PlayerGui.MineGUI.RockHealth.Visible = false
end)
end
-- Handle rock destruction
local function onRockDestroyed(rock)
rock:GetAttributeChangedSignal("Health"):Connect(function()
local health = rock:GetAttribute("Health")
if health and health <= 0 then
print("Rock destroyed:", rock.Name)
player.PlayerGui.MineGUI.RockHealth.Visible = false
rock:Destroy() -- Optionally destroy the rock
end
end)
end
-- Monitor initial rocks for destruction
for _, Block in RFolder:GetChildren() do
onRockDestroyed(Block)
end
The issue is that this piece of code here will work only for the initial rocks. if you add a new object it will do nothing since the for loop has already been completed what you can do is put this code into a while loop with the wait time before another rock will spawn. doing this will fix your issue
I changed it so It will rerun the function everytime something is added to the folder with rocks
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local StarterGUI = game:GetService("StarterGui")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Collection = game:GetService("CollectionService")
local RFolder = game.Workspace.Rocks
-- Tagging new rocks
local function addTagToRock(rock)
Collection:AddTag(rock, "Highlitable")
end
-- Tag initial rocks
for _, rock in RFolder:GetChildren() do
addTagToRock(rock)
end
-- Tag new rocks as they are added
RFolder.ChildAdded:Connect(function(newRock)
addTagToRock(newRock)
end)
-- Highlight logic
local function getRootTarget(target)
while target and not Collection:HasTag(target, "Highlitable") do
target = target.Parent
end
return target
end
mouse.Move:Connect(function()
local target = getRootTarget(mouse.Target)
if target then
script.Highlight.Adornee = target
else
script.Highlight.Adornee = nil
end
end)
-- Health popup logic
local function GetBlock()
for _, Block in RFolder:GetChildren() do
local ClickDetector = Block:FindFirstChild("ClickDetector") or Instance.new("ClickDetector", Block)
ClickDetector.MaxActivationDistance = 10
ClickDetector.MouseHoverEnter:Connect(function()
player.PlayerGui.MineGUI.RockHealth.Visible = true
end)
ClickDetector.MouseHoverLeave:Connect(function()
player.PlayerGui.MineGUI.RockHealth.Visible = false
end)
end
end
GetBlock()
RFolder.ChildAdded:Connect(function()
GetBlock()
end)
-- Handle rock destruction
local function onRockDestroyed(rock)
rock:GetAttributeChangedSignal("Health"):Connect(function()
local health = rock:GetAttribute("Health")
if health and health <= 0 then
print("Rock destroyed:", rock.Name)
player.PlayerGui.MineGUI.RockHealth.Visible = false
rock:Destroy() -- Optionally destroy the rock
end
end)
end
-- Monitor initial rocks for destruction
for _, Block in RFolder:GetChildren() do
onRockDestroyed(Block)
end
I tried rewriting the script since there are a few issues.
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local StarterGUI = game:GetService("StarterGui")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")
-- Don't use the "rocks" folder. Instead just add a tag called "rock" to each rock.
-- Highlight logic
local function IsRock(target:BasePart)
if target and target:HasTag("Rock") then
return true
end
return false
end
mouse.Move:Connect(function()
if IsRock(mouse.Target) then
script.Highlight.Adornee = mouse.Target
else
script.Highlight.Adornee = nil
end
end)
-- Health popup logic
local function CheckForHealthPopup(rock)
local ClickDetector = rock:WaitForChild("ClickDetector")
ClickDetector.MaxActivationDistance = 10
ClickDetector.MouseHoverEnter:Connect(function()
player.PlayerGui.MineGUI.RockHealth.Visible = true
end)
ClickDetector.MouseHoverLeave:Connect(function()
player.PlayerGui.MineGUI.RockHealth.Visible = false
end)
end
-- Destroy rock logic
local function OnRockDestroyed(rock)
rock:GetAttributeChangedSignal("Health"):Connect(function()
local health = rock:GetAttribute("Health")
if health and health <= 0 then
print("Rock destroyed:", rock.Name)
player.PlayerGui.MineGUI.RockHealth.Visible = false
rock:Destroy()
end
end)
end
-- Don't add tags (can cause memory leaks), instead get objects with the tags and add them to a table
local function RockAdded(rock: BasePart)
-- Remove tag to prevent memory leaks
rock:RemoveTag("Rock")
CheckForHealthPopup(rock)
OnRockDestroyed(rock)
end
-- Get tagged rocks
local initialTaggedRocks = CollectionService:GetTagged("Rock")
for _, rock in initialTaggedRocks do
RockAdded(rock)
end
-- Get newly tagged rocks
CollectionService:GetInstanceAddedSignal("Rock"):Connect(function(rock)
RockAdded(rock)
end)
You can read the comments I added to see what I changed.
I think the main issue was that newly added rocks were not being checked for using a CollectionService.GetInstanceAddedSignal() and it would only work for the initially created rocks.
You should also consider using a BillboardGui parented to the rocks to show health instead of a health GUI in the player GUI.
If you need help making your server script compatible with this script send the server script.
Sorry for the later response but I looked over your script and I understand what you are trying to do and when I go to test it the logic no longer works for highlighting and for the health pop up
and it only works on one of the 2 rocks that are spawned in and when I test the game it works half the time
So I agree with @EffBeeCee on the collectionService tag related comments but you also have to use CollectionService:HasTag() for more consistency. I honestly wouldn’t go about making this system like this and I would separate this entirely into different segments. I would also make the rocks a class object with an asset attached to it for easier cleanup and better damage efficiency. That way, we can rely on OOP to index and create new rock objects later down the line and our client code will be able to highlight those rocks with the CollectionService Instance Added Signal…
This is a place I threw together, the server side is entirely my code but I tried mixing the client with a few things I would do as well and reformatted some stuff: RockSystemTest.rbxl (112.5 KB)
PS: You were using multiple tags for checking if the object was a rock. I would prefer doing what I did and have the one aka “Highlightable” and then have the rocks be models instead of BaseParts.
PPS: I was also in a hurry so in the class object code I was setting the position, in reality I would’ve Pivoted the model to a CFrame, I was just trying to get a quick test place out so you could look at it. I also used Janitor module for the classes for better cleanup and to avoid memory leaks.
I also want to point out that in no way shape or form is this test place supposed to be a teaching tool as some of the practices aren’t up to par with what I would normally do, it’s just sort of a good middle ground for you to understand the concept from. I also edited my Attribute Changed signal on the class objects to set self.Health to whatever the attribute is, so it looks like this:
This makes sure that you can use external scripts to edit the Object’s health Via. Server Context scripts editing the Attribute on the rock model. This also will destroy and cleanup the rock class object if the health falls below or equal to 0.
newJanitor:Add(self.Asset:GetAttributeChangedSignal("Health"):Connect(function()
self.Health = self.Asset:GetAttribute("Health")
print(self.Asset:GetFullName(), "is at", self.Health, " HP")
if self.Health <= 0 then
self:Destroy()
end
end), "Disconnect")
Sounds good and if you have any questions don’t be afraid to ask. It’s not exactly 1:1 of how I would do it but I think it’s slightly more efficient this way. Just make sure to go to the “RockClass” module under RockService and change the code provided above to the better version
Are you using the Rock Class in order to create the rocks? Im asking because on my project I had it so the rock would randomly respawn in specific locations.
-- Services
local RS = game:GetService("ReplicatedStorage")
local WS = game:GetService("Workspace")
-- Variables
local Rocks = RS.Rocks:GetChildren()
local Event = RS.Remotes.RockDestory
local RespawnTime = 3
local possibleLocations = WS.RockPositions:GetChildren()
-- Function to spawn rocks at random locations
local function spawnRocks(amount)
for i = 1, amount do
-- Ensure we have enough locations to spawn rocks
if #possibleLocations == 0 then
warn("No available locations to spawn rocks.")
return
end
-- Select a random rock
local RandomRock = RS.Rocks:GetChildren()
local ChooseRock = RandomRock[math.random(1, #RandomRock)]
-- Clone the rock
local clone = ChooseRock:Clone()
-- Select a random location
local randomIndex = math.random(1, #possibleLocations)
local randomPos = possibleLocations[randomIndex]
-- Position the rock and parent it to Workspace
clone.Position = randomPos.Position
clone.Parent = WS.Rocks
-- Remove the used location to avoid reuse
table.remove(possibleLocations, randomIndex)
end
end
-- Initial Rock Spawn (spawns # of rocks initially)
spawnRocks(2)
-- Rock Respawn on Event Trigger
Event.OnServerEvent:Connect(function()
task.wait(RespawnTime)
spawnRocks(1) -- Respawn # of rocks after the event
end)
This is my code I created to handle all of the rock spawning
Should I incorperate this with the Rock Class and the janitor in order to save data and make it easier to load?
So yes, the rock class is used for creating the rocks, however I made a function inside RockService that just re-routes and does the same logic. The reason I do that is so that you don’t have a recursive require chain and you can just require RockService or put the spawning logic inside of the RockService Init. I would personally make the logic inside of the RockService, but you will have to edit some things if you want to save positions and have them respawn whenever the rocks are destroyed. If you want help making that system in the future let me know, my is _bamz. I will be available tonight until 3AM EST. But you will definitely have to make new logic for the respawning and spawning system being flush.
Sorry For the late response, I have been super bisy lately but I finally got to look over the script and Yes this works great. But I have a quick question. I have been wanting to use the Janitor module in my projects for a while what is the best way to implement this module to my projects
You would use the click detectors and just connect those to the damage function of the rock itself. Make sure its on the server and can get the rock or just on creation have it connect to each click detector automatically.