for i, crystal in pairs(workspace:GetChildren()) do
if crystal.Name == "Crystal" then
crystal.Touched:Connect(crystalTouched)
end
end
This means the error is in this section. By the looks of it you havent put any checks to check whether the object named Crystal is actually a BasePart. Objects such as Folders and Models don’t have a .Touched event.
Edit: Sorry, I assumed you had meant this for loop, not the other one.
You can also just do print(#hit:GetTouchingParts()) before the for loop. If the resultant number is 0, then no parts are touching and the for loop will not run.
-- Take the crystal that was touched as an argument
local function crystalTouched(crystal, hit)
if not deb then
if hit.Parent:FindFirstChild("Humanoid") then
local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
local crystals = plr:WaitForChild('leaderstats').Crystals
crystals.Value = crystals.Value + (1 * plr:WaitForChild("Gamepasses").CrystalsMultiplier.Value)
deb = true
-- We can use the crystal that was touched since we already know which crystal it is
crystal.Parent = crystalFolder
--wait(math.random(10,20))
wait(1) -- for testing purposes
crystal.Parent = workspace
setRandomPosition(crystal)
deb = false
end
end
end
for i, crystal in pairs(workspace:GetChildren()) do
if crystal.Name == "Crystal" then
-- Using an anonymous function here to pass the crystal that was touched
crystal.Touched:Connect(function(hit) crystalTouched(crystal, hit) end)
end
end
I would use an anonymous function to pass the crystal that was touched as well as the part that touched it.
This is slightly unrelated but I prefer using debounces like this:
function debounce(func)
local running = false
return function(...)
if not running then
running = true
func(...)
running = false
end
end
end
This function was pulled from the bottom of roblox’s article on debounce and it makes .Touched connections look much cleaner. This is how you use it:
part.Touched:Connect(debounce(function(hitPart)
-- now you don't need anything related to debounce in here
end)) -- don't forget the extra ) from the debounce()
local rs = game.ReplicatedStorage
local crystalFolder = rs.Crystals
local deb = false
local RNG = Random.new()
local bases = {
{Object = workspace.Ground.Grass, Weight = 5};
{Object = workspace.Path, Weight = 2};
}
local function random(choices)
local weightSum = 0
for i = 1, #choices do
weightSum = weightSum + choices[i].Weight
end
local rand = RNG:NextInteger(0, weightSum)
for i = 1, #choices do
if rand <= choices[i].Weight then
return choices[i].Object
end
rand = rand - choices[i].Weight
end
end
local function setRandomPosition(item)
local Base = random(bases)
item.CFrame = CFrame.new(
RNG:NextInteger((-Base.Size.X / 2), (Base.Size.X / 2)),
Base.Position.Y + (Base.Size.Y / 2) + (item.Size.Y / 2),
RNG:NextInteger((-Base.Size.Z / 2), (Base.Size.Z / 2))
) * CFrame.Angles(0, math.rad(RNG:NextInteger(0, 360)), 0)
end
local function crystalTouched(crystal, hit)
if not deb then
if hit.Parent:FindFirstChild("Humanoid") then
local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
local crystals = plr:WaitForChild('leaderstats').Crystals
crystals.Value = crystals.Value + (1 * plr:WaitForChild("Gamepasses").CrystalsMultiplier.Value)
deb = true
crystal.Parent = crystalFolder
print("Yay")
--wait(math.random(8,20))
wait(1)
print("Waited")
crystal.Parent = workspace
setRandomPosition(crystal)
deb = false
end
end
end
for i, crystal in pairs(workspace:GetChildren()) do
if crystal.Name == "Crystal" then
crystal.Touched:Connect(function(hit)
crystalTouched(crystal, hit)
end)
end
end
I got print statements in the output but then I think the bases disappeared.
local rs = game.ReplicatedStorage
local crystalFolder = rs.Crystals
local deb = false
local RNG = Random.new()
local bases = {
{Object = workspace.Ground.Grass, Weight = 5};
{Object = workspace.Path, Weight = 2};
}
local function random(choices)
local weightSum = 0
for i = 1, #choices do
weightSum = weightSum + choices[i].Weight
end
local rand = RNG:NextInteger(0, weightSum)
for i = 1, #choices do
if rand <= choices[i].Weight then
return choices[i].Object
end
rand = rand - choices[i].Weight
end
end
local function setRandomPosition(item)
local Base = random(bases)
item.CFrame = CFrame.new(
RNG:NextInteger((-Base.Size.X / 2), (Base.Size.X / 2)),
Base.Position.Y + (Base.Size.Y / 2) + (item.Size.Y / 2),
RNG:NextInteger((-Base.Size.Z / 2), (Base.Size.Z / 2))
) * CFrame.Angles(0, math.rad(RNG:NextInteger(0, 360)), 0)
end
local function crystalTouched(crystal, hit)
if not deb then
deb = true
if hit.Parent:FindFirstChild("Humanoid") then
local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
local crystals = plr:WaitForChild('leaderstats').Crystals
crystals.Value = crystals.Value + (1 * plr:WaitForChild("Gamepasses").CrystalsMultiplier.Value)
crystal.Parent = crystalFolder
print("Yay")
--wait(math.random(8,20))
wait(1)
print("Waited")
crystal.Parent = workspace
setRandomPosition(crystal)
deb = false
end
end
end
for i, crystal in pairs(workspace:GetChildren()) do
if crystal.Name == "Crystal" then
crystal.Touched:Connect(function(hit)
crystalTouched(crystal, hit)
end)
end
end
Try this.
I moved the deb = true to the top of the if statement. There will be a fraction of a second of lag while assigning the variables because you used :WaitForChild() and :GetPlayerFromCharacter(). During this time, a new .Touched even could have fired on the same crystal, and since deb is not set to true yet, the function executes.
The bases should just be transparent CanCollide = false rectangles that mark the areas where the crystals can spawn. You’re not supposed to be able to see them.
If the problem is fixed you can mark the answer that fixed it for you as the solution. It will help others when they view the thread by allowing them to jump straight to the answer rather than read through every reply.
One problem still:
The grass keeps moving as well as crystals that were not touched:
local crystalFolder = game.ServerStorage.Crystals
local deb = false
local RNG = Random.new()
local bases = {
{Object = workspace.Base, Weight = 3};
{Object = workspace.Base2, Weight = 4};
}
local function random(choices)
local weightSum = 0
for i = 1, #choices do
weightSum = weightSum + choices[i].Weight
end
local rand = RNG:NextInteger(0, weightSum)
for i = 1, #choices do
if rand <= choices[i].Weight then
return choices[i].Object
end
rand = rand - choices[i].Weight
end
end
local function setRandomPosition(item)
local Base = random(bases)
item.CFrame = CFrame.new(
RNG:NextInteger((-Base.Size.X / 2), (Base.Size.X / 2)),
Base.Position.Y + (Base.Size.Y / 2) + (item.Size.Y / 2),
RNG:NextInteger((-Base.Size.Z / 2), (Base.Size.Z / 2))
) * CFrame.Angles(0, math.rad(RNG:NextInteger(0, 360)), 0)
end
local function crystalTouched(crystal, hit)
if not deb then
deb = true
if hit.Parent:FindFirstChild("Humanoid") then
local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
local crystals = plr:WaitForChild('leaderstats').Crystals
crystals.Value = crystals.Value + (1 * plr:WaitForChild("Gamepasses").CrystalsMultiplier.Value)
crystal.Parent = crystalFolder
print("Yay")
--wait(math.random(8,20))
wait(1)
print("Waited")
crystal.Parent = workspace
setRandomPosition(crystal)
deb = false
else
deb = false
end
end
end
script.Parent.Touched:Connect(function(hit)
crystalTouched(script.Parent, hit)
end)