So I’m creating a system where you collect crystals and then it’ll disappear for a few seconds and reappear somewhere else. Now I’m wondering, “how would I do that?”
Now I could just have it disappear then reappear in the same spot but I want it to reappear in a different spot.
BUT, how would I make it reappear in only certain spots?
My map as seen in the thumbnail has buildings and I wouldn’t want the crystals to reappear in/on the building.
local RNG = Random.new()
local item = -- this is the object you want to place
local bases = {
{Object = --[[path to base one]], Weight = --[[rarity]]};
{Object = --[[path to base two]], Weight = --[[rarity]]};
{Object = --[[path to base three]], Weight = --[[rarity]]};
}
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()
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
setRandomPosition()
Call the setRandomPosition() function when you want to update the position of the parts.
To avoid these Sapphires spawning in buildings, I suggest you go into Studio and make a table of possible positions that the Sapphires can spawn in. Then let the script pick any one of those positions. However, if there are any other solutions above this reply, I suggest you use those if you don’t have time for picking out positions.
So I tried it out and added the touching part, but I’m getting this error in the output:
attempt to call a nil value
I couldn’t find the source so I don’t know which line.
Here is my script:
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(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
for i, gem in pairs(hit:GetTouchingParts()) do
if gem.Name == "Crystal" then
gem.Parent = crystalFolder
--wait(math.random(10,20))
wait(1) -- for testing purposes
gem.Parent = workspace
setRandomPosition(gem)
deb = false
break
end
end
end
end
end
for i, crystal in pairs(workspace:GetChildren()) do
if crystal.Name == "Crystal" then
crystal.Touched:Connect(crystalTouched)
end
end
Have you tried debugging using print statements? If you put a couple of print statements interspersed between the lines of code you can narrow down the area where the bug is.
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.