CollectionService IsDescendantOf() won't work

Nothing like waking up in the morning to a bug you’ve been trying to fix for weeks.

For the next game that I’m working on, I have coins that are stored within ServerStorage and are then cloned into workspace. I’m using CollectionService to handle every coin in the game. The problem is that the coins won’t work unless their script is somehow delayed until they are cloned into the workspace. For the longest time, using wait(15) would have them work just fine as after 15 they’d already be in the workspace. Yesterday I tried to optimize this with the following code (because at some point wait() will also stop working):

if coin:IsDescendantOf(game.ServerStorage) then
	repeat wait() until coin:IsDescendantOf(workspace)
end

No matter how I word it or where I put it, it doesn’t fully work. It will, however, print before the repeat, but never after.
Here’s all of the coin script:

local CollectionService = game:GetService("CollectionService")
local CoinScriptEvent = game.ReplicatedStorage.Events.GameEvents.CoinScriptEvent

-- if i put a wait(15) here, it'll work just fine

for i,coin in pairs(CollectionService:GetTagged("Coin")) do
    -- this is the code im trying to get working
    -- waits until the coin is inside workspace
	if coin:IsDescendantOf(game.ServerStorage) then
		repeat wait() until coin:IsDescendantOf(workspace)
	end

	local Players = game:GetService("Players")
	local CoinGuiEvent = game.ReplicatedStorage.Events.GuiEvents.CoinGuiEvent
	local coinGui = game.StarterGui.CoinGui
	local coinSound = coin.Collect
	local _isTouched = false
	
    -- if touched by player, execute
	coin.Touched:Connect(function(object)
		if object:FindFirstAncestorWhichIsA("Model") then
			if object.Parent:FindFirstChild("Humanoid") then

				--found player
				local RealPlayer = game.Players:GetPlayerFromCharacter(object.Parent)

				if _isTouched == false then
					-- not important so left out
				end
			end
		end
	end)
	
    -- play animation until collected
	function spinAnimation()
        -- code here
	end
	
	task.defer(spinAnimation)
end

Any help/feedback would be appreciated. Thanks.

1 Like

Have you tried using CollectionService:GetInstanceAdded event to detect new coins being added?

So this sounds like the start and end of your problem; the coins in the ServerStorage are cloned into the workspace. This means that there is always going to be a basic coin inside the ServerStorage that just never moves, and because it’s tagged, it’ll just keep repeating over and over.

So, here is a better layout concept, break it into 3 parts;

  1. The coin’s code, this is specific to whatever object, but you house it inside a function called something like ‘‘CoinSpawn’’ and it does all your touched events and everything specific to each coin.
  2. You use that existing loop you have there to find any coins that already exist in the game world. Theres lots of different reasons that coins might already be in the world, this just captures them if they are.
  3. You use @dthecoolest’s suggestion and use CollectionService:GetInstanceAdded(“Coin”):Connect(CoinSpawn)
    You can now create loads of different coins after this and they’ll all spawn with the code from the Coin spawn working.

What does this mean? Well, you can just state:
if coin:IsDescendantOf(workspace) == false then return end
No need for loops and the like here.

Went ahead and combined 1. and 3. together and came up with this new script:

function CoinSpawn(coin)
	if coin:IsDescendantOf(workspace) == false then
		return
	end

    -- yadda yadda yadda	
end

CollectionService:GetInstanceAddedSignal("Coin"):Connect(CoinSpawn)

It’s very strange. It works without the if statement. But the coin(s) were inside workspace when I was running the game. Did I put the if statement in the wrong spot?

All you’re doing here is saying;

  • A new coin has been added, lets pass that coin into this function
  • Is the coin somewhere in the workspace?
    → If it’s in the workspace, lets do something
    → If it’s not in the workspace, lets just return and finish.

The script you’ve got there should work whenever you add a new object with the tag.
I use something similar when I’m searching for objects with tags that can only exist in the workspace;


function CollectAllTaggedInGame(Tag) --Returns a full list of the objects, but only if they are children of the workspace.
    local TheseObjects  = game:GetService("CollectionService"):GetTagged(Tag) --Search for the given tag
    local ThisList      = {}
    for key, object in pairs(TheseObjects) do --Iterate through the returned objects
        if object:IsDescendantOf(workspace) then    --So long as the object is a descendant of the workspace
            table.insert(ThisList, object)          --Add it to the given list
        end
    end

    return ThisList
end