Returning values with spawn(function() end)

  1. What do you want to achieve?

I have been organising a raycast collision detection system using attachments as hitboxes in module scripts; the original position of the attachment would be indexed and then be followed by waiting with spawn() to calculate if the attachment’s position has changed.

  1. What is the issue?

The issue is returning the raycast results to the script that called the module, as the spawn() function creates an alternative thread.

  1. What solutions have you tried so far?
    I have tried using coroutines to replace spawn(), but the performance decreases as instead of looping through the entire arrays of attachments simultaneously, it would only loop through on only the first attachment.
                        -- looping through the part to fetch attachments
			for _, child in pairs(part:GetChildren()) do
				if child:IsA("Attachment") then
					table.insert(dmg, child)
				end
			end
			
			if dmg ~= nil then
				-- loops through the table of attachments
				for _, point in ipairs(dmg) do
					local oldPos = point.WorldPosition
					local hit, pos
					
					spawn(function()
						-- wait in order to detect the position change
						rs.RenderStepped:Wait()
						local newPos = point.WorldPosition
						local distance = (newPos - oldPos).Magnitude
						local direction = (newPos - oldPos).Unit
						-- raycast function
						local instance, position = raycast(point, oldPos, direction, distance)
						
						hit, pos = instance, position
					end)
					
					-- returns the result to the main script
					return hit, pos
				end
			end

The reason this is happening is because spawn waits before creating the thread whereas coroutines are instant. You could probably use a wait outside of the coroutine before continuing but this kinda eliminates the point of coroutines.

Try something like this:

for i,v in ipairs(dmg) do
    local oldPos = point.WorldPosition
    local success, hit, pos = coroutine.resume(coroutine.create(function()
        rs.RenderStepped:Wait()
		local newPos = point.WorldPosition
		local distance = (newPos - oldPos).Magnitude
		local direction = (newPos - oldPos).Unit
		local instance, position = raycast(point, oldPos, direction, distance)
						
		return instance, position
    end))
    wait()
end

The issue is that the results are stored on the coroutine’s thread, instead of the main thread where the coroutine was spawned. I am able to print the instance detected on the coroutine, but not from the main thread.

for i,v in ipairs(dmg) do
    local oldPos = point.WorldPosition
    local success, hit, pos = coroutine.resume(coroutine.create(function()
        rs.RenderStepped:Wait()
		local newPos = point.WorldPosition
		local distance = (newPos - oldPos).Magnitude
		local direction = (newPos - oldPos).Unit
		local instance, position = raycast(point, oldPos, direction, distance)
                print(instance) -- prints the detected instance
                hit = instance
                print(hit) -- prints the detected instance
 						
		return instance, position -- somehow returns nothing
    end))
   
    wait()
    print(hit) -- prints nil
end

Ahh okay, gotcha. Have you tried checking its status or printing success to see if it’s being suspended/was unsuccessful?

local cor = coroutine.create(function()
-- rest of func here
end)
local success, hit, pos = coroutine.resume(cor)
print(success)
print(cor.status)

The coroutines do not fail, whether if it is spawn() or coroutine.create(). It is just that the generated result from the coroutine cannot be replicated nor returned, I have tried using an object value to index the hit instance directly, but it does not seem to replicate despite printing the result perfectly without erroring.

1 Like

That’s weird. Have you tried the variable outside of the scope of the coroutine (the thing in your OP) with coroutines?

Also, are you switching between the client/server boundary at any time? If the instance exists on the client but not on the server, it wouldn’t replicate. But then again it worked with spawn which is weird so that’s probably not it. When you tried printing the status, what was the status?

The module is called from the client to enhance performance issue, but it should not trigger any FE consequences as it is simply returning a detected value to the client that called the module.

module.Add = function(x)
	local n 
	
	local function add(x)
		x += 1 
		n = x
	end
	
	local f = coroutine.wrap(add)
	print(f) -- function: 0x744c744e124581cd
	print(n) -- nil
	return n -- returns nil
end

Ahh okay, coroutine.wrap doesn’t return any values, coroutine.resume does. You also aren’t ever calling the thread.

local f = coroutine.wrap(add)
f()
local obj = {
    ['Number'] = 1; 
    ['Add'] = function(self) 
        self.Number += 1
    end;
}

local thread = coroutine.wrap(obj.Add)

print(obj.Number) --> 1

thread(obj)

print(obj.Number) --> 2

I forgot to test that part, my fault.

1 Like

It behaves normally on another module, but not for the raycast module.

https://gyazo.com/e58bf432c655562001144f3e3455a31b
Video for both the local script and the module script.
On line 57, the module script prints it has detected the humanoid root part, but the local script returns result as nil.

are you trying to wait for a thread to finish and return the value from it? there are two things you can use.

  1. Events, your own implementation of :Connect()able instances. this one is popular, could make your own too.
  2. Promises, which are a bit like events but more dynamic. they stop the script until they get back. for your case you might want this one. this one is pretty de-facto for roblox, I think.

Yes, but it is not OOP based however.

no problemo, OOP or not you aren’t using them to make objects, just using them in a script. think of it like a Vector3, it’s an object but it works with any programming style.