Can't call :Destroy() on class object

I’m trying to destroy a bullet object if it times out or manages to hit something. The problem is that I’m getting an error “attempt to call a nil value” on the line containing the :Destroy() call. I’ve checked the object itself and it still exists, done this by printing one of its values.

The modulescript looks like this:

local bullet = {}
bullet.__index = bullet

function bullet.new(part, velocity)
	local self = {
		part = part,
		velocity = velocity,
		lifeTime = 0
	}
	setmetatable(self, bullet)
	
	return self
end

return bullet

The server script saves all the bullets in a dictionary with the object as a key, that way I can iterate through the dictionary and grab the object to destroy it. (Maybe there’s a better way?)
Server Script:

local bullets = {}

local function moveBullets()
	for i, bullet in pairs(bullets) do
		-- Check if lifeTime has run out
		if bullet.lifeTime >= 120 then
			bullet:Destroy()
			bullets[bullet] = nil
		else 
			if raycastResult then
				bullet:Destroy()
				bullets[bullet] = nil

			end

			bullet.lifeTime = bullet.lifeTime + 1
		end
	end
end

game.ReplicatedStorage.RemoteEvents.RequestShot.OnServerEvent:Connect(function(plr, cframe, velocity)
	local bulletObj = bulletClass.new(bulletPart, velocity)
	
	bullets[bulletObj] = bulletObj
end)

-- Start heartbeat on server startup
local connection = runService.Heartbeat:Connect(moveBullets)

I removed some code from the example that only updates and checks on the objects values but you should get a hang of what the code is doing still.

Why can’t it run :Destroy() on the bullet objects?

What is the object? A union, mesh, part, etc…

The object is a new object of the bullet class. It’s called and added to the list in the RequestShot function.

You’re never creating a .Destroy key in your bullet.

    local self = {
		part = part,
		velocity = velocity,
		lifeTime = 0,
        Destroy = function(self)
            print('Bullet destroyed!')
        end
	}

Oh, so I have to define my own destructor? Do I just go through the self values and destroy them in the destructor then?

Now this might not be the best solution, but after a said amount of time, or before you destroy it, parent the bullet into ReplicatedFirst, and add a script in replicated first like this:

for i, Objects in pairs(script.Parent:GetChildren()) do
    if Objects.Name == "Bullet" then -- Change to the bullet name
    Objects:Destroy()
else
    return
end)

This should work, however if not, let me know!

Yeah, you could then also use the __metatable metamethod and cause it to error to lock the metatable which I think roblox does, that way it isn’t modifiable. I don’t think there aren’t any other destructors aside from just stopping every reference to the table, though I might be wrong.

1 Like

I found a way around it this time, I added the properties as attributes to the part and skipped even need to use OOP this time. I’m used to C++ style of memory management so lua’s metatables and the garbage collection is a bit foreign to me. Since it’s only a part I can also use the debris system roblox has :slight_smile:

1 Like