I have a ModuleScript that is a base implementation of a sword in my game.
I have other sword scripts that require this module script and then set various overrides on the base behavior to do things like draw sword-specific particle effects.
I have a bug in my code because I didn’t understand how require works. It’s only called once, globally. I thought I was getting a new script environment each time I called require.
So right now all my swords require this Module Script. The Module Script has local state that I want to be unique to each sword.
Is there a hack that I can use with get/setenv or potentially LoadString to make it so each time I call this Module Script, I get back a separate new instance of it, instead of a shared instance?
I’m basically hacking around Lua not having classes. I want to use require as an object factory instead of whatever it’s doing now (I guess it’s a #include).
You can have a ModuleScript return a function (or library of functions) which constructs an object with its own state. It’s essentially a factory pattern.
return function (tool)
return {
tool = tool;
damage = 15;
someFunction = function (...) ... end;
}
end
Usage:
-- Somewhere in a tool far, far away:
local setupTool = require(you_know_what)
setupTool(script.Parent)
This gets super close to the typical Lua style of simulating OOP using metatables, which is a really common pattern used nowadays. It’s probably your best bet here!
I think you are right and I need to add a layer of indirection here, so instead of my sword state being local variables of the ModuleScript, they should be returned in a table from Init().
That’s pretty much what you’d want. Most devs would return a table from an init/new function like BaseSword.Init, and set its metatable to a table whose __index is a table of functions you want included with every one of that sword. Like:
local BaseSwordFuncs = {}
function BaseSwordFuncs.Slash()
-- ...woosh...
-- (this will be available in all BaseSword)
end
function BaseSword.Init(...)
...
local self = {}
self.SlashSound = ... -- And all the other juicy sword-local properties here...
setmetatable(self, {__index = BaseSwordFuncs})
return self
end
My site has a step by step walkthrough on this if the Programming in Lua book sucks in explaining this (which it does)
Seeing some of these names makes me feel like it’s 2007 again. (Well, that’s not quite true unless I call you by your former name…)
Another approach I use often is to put state variables in the function environment, and then return a table with accessors, mutators, and other methods. Example:
return function(initialState)
local state = initialState
local this = {}
function this:changeState(newState)
state = newState
end
return this
end
I’ve heard it said that this has faster performance at the cost of increased memory usage, but I’d guess that the difference is very minute for most use cases. See here.