Accessing a function of a class with a string

Pretty basic question.
Obviously, I can access rocket.property with rocket[‘property’], but I’ve looked around and I can’t find an equivalent way to access rocket:explode.

Also, what are the requirements for being able to post threads in the “tutorials” category?
I’ve been learning lots about making huge explosions.

1 Like

Pretty sure one of the default control scripts does this. Something like Player.Move(player, slkdfjslkdfj)
Been a while though, so may be a little off about it.

Edit: Actually it saves the function as a variable and calls it that way.

rocket["explode"](rocket) will do the trick. This works with methods you’ve made via tables as well e.g.:

local obj = {color="RED"}

function obj:PrintData()
    print(self.color)
end

obj["PrintData"](obj)

The functions you access with colon notation are methods. Essentially they’re functions that automatically pass the object the method is a member of to the method itself. This is why you’re able to use the self variable in any method. This is also why you can awkwardly call the method with dot notation so long as you include the object as the first parameter.

Only full members can post topics in #tutorials. You’ll need to graduate from the trial program before being able to post new threads there.

3 Likes

That’s how I assumed it worked, and how I implemented it, but if I replace Entity[event](unpack(arguments)) with Entity:explode(unpack(arguments) (event is ‘explode’) I get different results.

Entity:explode(unpack(arguments))

is the same as

Entity["explode"](Entity, unpack(arguments))

You will need to use

Entity[event](Entity, unpack(arguments))

instead of

Entity[event](unpack(arguments))

if you want your two example to be the same.

4 Likes

That works, but why on Earth is it like that?

The main reason, to my understanding, is to save memory with clean syntax. If : did the same thing as . then why would we even need :? : exists to solve a specific problem encountered when trying to save memory and write clean-looking code.

I think the best way to explain this is some code examples. (click to expand)

Consider the following piece of code:

function newEntity()
	local Entity = {}
	function Entity.explode(...)
		print(Entity, "exploded!", ...)
	end
	return Entity
end

newEntity().explode("Neat")

You have to make a new explode function for every single Entity. What a waste!


So let’s generalize explode so we can use the same explode for every Entity:

function explodeEntity(entity, ...)
	print(entity, "exploded!", ...)
end

function newEntity()
	local Entity = {}
	Entity.explode = explodeEntity
	return Entity
end

local entity = newEntity()
entity.explode(entity, "Neat")

This is great! Now every Entity uses the same explode! But look at this mess…

entity.explode(entity, "Neat")

That doesn’t look very good, and I don’t like writing entity twice!


So let’s add in some syntax to clean that up. We want it to be clear that we’re passing entity to itself, but we only want to write “entity” once. Let’s come up with something other than . to make it clear that we’re calling explode slightly differently.

entity:explode("Neat")

Nice! With this new syntax, we can save memory and have cleaner code.


To actually save memory with this, you would need to use metatables. These let you make “objects” where all of their default methods and properties only need to be set once in the metatable that they all share instead of many times in each individual object.

local EntityMeta = {
	__index = {
		explode = function(self, ...)
			print(self, "exploded!", ...)
		end
	}
}

function newEntity()
	local Entity = setmetatable({}, EntityMeta)
	return Entity
end

local entity = newEntity()
entity:explode("Neat")

I’m already using metatables.
It leaks memory like a sieve though because I have no idea what I’m doing, the resource usage footprint goes up by 0.2% for every rocket I fire, causing the serverside physics to start freezing after the 30th rocket.

function Entity:new()
	local NewEntity = {}
	setmetatable(NewEntity, self)
	self.__index = self
	
	NewEntity.Part = script.Rocket:Clone()
	
	NewEntity.EventStream = Instance.new('BindableEvent', NewEntity.Part)
	
	return NewEntity, NewEntity.EventStream, NewEntity.Part
end

I think the problem is not how I’m creating the rocket so much as how I’m destroying it.
If I destroy the part, then I destroy the BindableEvent too and thus disconnect the only thing other than workspace that has a pointer to the rocket.
But clearly the rocket still exists somewhere if it’s eating system resources!

After a little bit of testing, it sounds like some script is actually doing work on those “destroyed” rockets.

In a simple test, was able to spawn as many parts as I wanted (hundreds) while still keeping references to them and physics ran at full speed. I think your problem has less to do with physics being overloaded and more with the whole game being overloaded because your script is still doing work on the old rockets.

Physics won’t slow down unless a part is in the world being physically simulated. If Destroyed parts are slowing your game down, it’s because of something your scripts are doing, not something the Roblox engine is doing.

You could try using the microprofiler Ctrl+F6 to figure out what’s slowing the game down. You can even add microprofiler labels to your scripts to try to catch what in particular is causing the slowdown.

Edit: Unrelated to your performance problems, you don’t need to set self.__index = self every time. You can do Entity.__index = Entity outside of the function.

Neat!
Never knew that existed.
Looks like the problem is that a a “while self.Part do” loop is still running, even after the calling of “self.Part:Destroy()”, which means that the rocket still exists in memory.
Any suggestions for how to destroy these rockets as thoroughly as possible?

I doubt it’s the reference itself or the fact that the rocket exists and more so whatever that loop is doing.

You should check if the rocket is destroyed in the loop and break out of the loop if it’s destroyed. You could set a property like self.Destroyed to true and use while self.Part and not self.Destroyed do.

Alternatively, you could set self.Part to nil (as this will also break out of the loop), but this could cause problems with some of your other code. This may be a good way to make sure none of your code is interacting with the rocket after it’s destroyed, though!


Note that both ways of doing this require you to run code when your rocket is destroyed. You will want to destroy your rocket with a custom method such as Entity:Destroy() so you can run all of your cleanup code too.

I’ll go with self.Part, since I want to foolproof my entity framework.
If there’s a fault, it should be a blatant one, rather than a subtle memory leak.
Pity I can’t just do self = nil and be done with it.

Anything more that my cleanup code needs?
Like, will the rocket be removed for me without a part, code loop, or bindableevent?

Oh, here’s the rocket launcher so far, by the way:


The explosions are unsatisfying, but that’s because the final explosions are going to be programmed similarly to the rockets.

Make sure you…

  • Destroy all your Instances (Part, BindableEvent)
  • Remove all references to your Entity/rocket and Instances
    • This means any running code that references these needs to stop running or needs their references set to nil
    • This means that these should not be keys or value in a table. excluding weak keys/values

If your Instances are Destroyed and all references to your Instances and Entity/rocket object are removed then the garbage collector will remove the Instances and Entity/rocket from memory.

Thanks for all of this!
One last question though: Will the entity and rocket be garbage collected if the only reference to the rocket is the entity, which itself is never referenced?
I realised that checking if self.Part ~= nil isn’t good enough; since there are external events that can destroy the rocket without giving it the chance to be set to nil, the only secure method is to check if self.Part is a descendant of workspace, which makes checking self.Part redundant.
I’m wondering if I can get away with not setting self.Part to nil as long as the loops are terminated, because then I would only need self.Part:Destroy() to destroy the rockets.

If nothing references the Entity and no code can access it by any means then the Entity then it will be garbage collected. If the Part is in nil and no running code can access it (no references), then it will be collected too.

If you’re allowing other code to remove the Part, perhaps you should listen for that and run your cleanup code when you detect that the Part is no longer in Workspace.

That’s a better idea.
A cleanup function triggered by an ancestry change would mean that the particle effects are cleanly removed in any situation.
Speaking of which, should I just be disabling the particles then sticking them in workspace and the debrisservice?
Because that’s what I’m doing, but the particles briefly appear in workspace and the client says “Trying to set locked parent”.

1 Like

I’m not sure what you mean by this.

If you’re trying to clean up “particle” Instances with the rocket, why not Destroy them like everything else? I’m not sure I understand the context and what exactly you’re doing. I’m not sure what “disabling the particles then sticking them in workspace and the debrisservice” is supposed to mean – is this how you remove particles or how you handle their entire lifetime?

Additionally, by “particles” do you mean ParticleEmitters/Trails/Beams or something more complex like “particle” effects made with Parts?

Oh, if you’ve not worked with particles much before, then I guess what I said wouldn’t make sense.
If a ParticleEmitter is taken out of workspace, all particles instantly vanish.
That’s bad. Usually it doesn’t matter much, but for a lingering rocket trail arcing across the sky, it definitely matters.
So, when something that emits particles is destroyed, the clean way to handles the particles is to:

  1. Disable the emission of new particles
  2. Put the particle emitter in workspace
  3. Add the particle emitter to Debris Service, with a duration equivalent to the maximum lifetime of a particle

Not necessarily in that order, at least as far as I know.
Unfortunately, while it’s usually fine, it’s giving me the aforementioned problems.
Maybe it’s just one of those bugs that occurs under a set of conditions that no one knows, so the only solution is to shuffle things around until it works.

Also, turns out I was wrong about it being a good idea to have the ParticleEmitters go through that process in response to their parent being destroyed, since the their parents are locked and thus they can’t be moved to workspace.

Completely forgot that’s how ParticleEmitters worked – I’ve barely worked with them at all.

I don’t know any better way to do this since I’ve worked with ParticleEmitters so little. Someone else might have an idea for it.


“Trying to set locked parent” sounds like the error that occurs when you try to set the parent of an object after it’s Destroyed. When you Destroy an Instance, all of its children and descendants are Destroyed too – and cannot be reparented. Make sure you are parenting the ParticleEmitter to Workspace before you Destroy the Part it was part of. Edit: looks like you figured that out, but I didn’t notice until now.

1 Like