Object Oriented Programmer's Dream, a module so you never have to worry about weird work arounds to create classes ever again

OOPer'sDream
v2.0.1 Release

INTRO
Have you ever hated that you can’t make classes in roblox? Or rather, that you have to do weird work arounds to get one to work? Well worry no more! Presenting, OOPer’s Dream. A simple module/package that will allow you to create nice and organized classes, with class extending, inheritence, static and nonstatic methods, public and private methods, and properties. There’s even a plugin with a nice gui to manage all your classes and methods! Simply put the folder under replicated storage and get coding! Tutorial below.

HOW DOES IT WORK?
This will assume you have a vague knowledge of Classes and Objects in other programming languages. Once you import the folder into ReplicatedStorage, you will have a folder called “Classes” with a “ClassManager” and a “ReadMe” inside it. To make a class, simply make a new folder in the Classes folder called whatever you want the class to be called. It’s as easy as that to make a new class! To add a method, simply add a module script inside this new folder, and name it what you want the method to be called. In the module script, make it so it returns a function that takes two parameters, self and “data”. You don’t have to call them these, I just like to ;). It should look like this:

return function(self, data)
     -- some code
end

Lets say we’re making a method called “PrintSomething”. Then, you can simply name the module script that. Then, lets say we want it to take a string parameter, then print that out. To take in that parameter, we add a string parameter to our function, and then in the function, we have it print. The code would look something like this:

return function(self, data, printString)
     print(printString)
end

Now to test it, run the game, require the module ClassManager, call GetClass on what it returns, passing the name of the class you made, run .new() on the class you got to create an object of that class, then run .PrintSomething("Hello, World!") on the object you made. Here’s what that would look like if we called our class “ExampleClass”:

local ClassManager =  require(game.ReplicatedStorage.Classes.ClassManager)
local ExampleClass = ClassManager.GetClass("ExampleClass")
local exampleObject = ExampleClass.new()
exampleObject.PrintSomething("Hello, World!")

Sure enough, this prints out

Hello, World!

By creating module scripts, we can create methods on a class that we can create objects of at will! And because each method is in its own module script, it forces you to be organized! Under the TUTORIAL section, you’ll learn how to use the self and data parameters, use special methods, make classes inherit from other classes, make static/private methods, and make properties.

EXAMPLE USE

You may be wondering, when could this ever be useful? Well, I can think of a number of ways! Let’s say you’re working on an RPG game, and you want to make an item system. Well, you’re in luck! Let’s start by making an item class. Make a folder called “Item” under Classes.
image
Now we can start adding methods. Let’s say we need the methods drop and use, and to run some code when the item is made. Simply add three module scripts called __init__, use, and drop, and make them return a function that takes the parameters self and data. On __init__ we can also make it take the player that picked it up. In __init__, we can set a value on the object called “owner” for example to the player that picked it up. This would make our __init__ module script look like this:

return function(self, data, player)
     self.owner = player
end

And lets say that in drop, we want to set that value to nil. The module we made called “drop” would look something like this:

return function(self, data)
     self.owner = nil
end

Finally, lets have use print out that the owner of the item used it. The module called “use” could look like:

return function(self, data)
     print(self.owner.." used this item!")
end

Great, now we have a working item class! To test this, we can put the following code into a serverscript and run the game:

local ClassManager =  require(game.ReplicatedStorage.Classes.ClassManager)
local ItemClass = ClassManager.GetClass("Item")
local item = ItemClass.new("ianfinity01")
print(item.owner)
item.use()
item.drop()
print(item.owner)

This creates an item object thats owner is “Ianfinity01” (you can make this an actual player instead of a string if you want). It then prints the owner value of the object, uses the item, drops the item, then prints that value again.
Sure enough, we get

ianfinity01
ianfinity01 used this item!
nil

Great! Now, let’s say there are two types of items, weapons and food.
Lets create two more classes for these: “Weapon” and “Food”. Now we have:
image
Since weapon and food are subclasses of Item, go onto both the folders, and add a string attribute called “Extends” and set it to “Item”
image
Weapon and Food now inherit from Item! Now lets overwrite the use method. Put a module in both Weapon and Food called “use”
image
In weapon’s use, let’s make it take a target and print out that the owner of the item attacked the target. weapon’s use will look like this:

return function(self, data, target)
	print(self.owner.." attacks "..target)
end

In food’s use, lets make it print that the owner of the item ate the item, then drop the item.

return function(self, data, target)
	print(self.owner.." ate this item!")
	self.drop()
end

Notice that we can call the drop method, even though Food doesn’t have a drop method. This is because it inherited it from Item. It also inherited __init__, meaning the object’s owner will be set. Now, lets do something special whenever you drop a weapon. Add a drop module to the Weapon folder. Lets have it say “player laid down their weapon” before doing what it would normally do when an item is dropped. This is where that data parameter finally comes into play. The drop method of weapon will overwrite the drop method of Item, but using data.getParentMethod(), we can get the method weapon’s drop method overwrote (item’s drop method). Weapon’s drop method code would look like this:

return function(self, data)
	print(self.owner.." laid down their weapon!")
	local drop = data.getParentMethod()
	drop()
end

This prints that the owner of the weapon laid down their weapon, then it gets item’s drop method and calls that as well.
Lets test this.

local ClassManager =  require(game.ReplicatedStorage.Classes.ClassManager)
local WeaponClass = ClassManager.GetClass("Weapon")
local FoodClass = ClassManager.GetClass("Food")
local weapon = WeaponClass.new("ianfinity01")
local food = FoodClass.new("ianfinity01")
food.use()
print(food.owner)
weapon.use("bugs")
weapon.drop()
print(weapon.owner)

This should create a food and a weapon held by ianfinity01, then eat the food (which drops the food), print the food’s owner (should be nil because we dropped it). Then, it will attack “bugs” with the weapon, drop the weapon, and print the owner of the weapon (should also be nil because it was dropped). Sure enough, we get:

ianfinity01 ate this item!
nil
ianfinity01 attacks bugs
ianfinity01 laid down their weapon!
nil

If you’re confused why weapon’s owner still gets set to nil even though the drop method was overwritten, it’s because we manually ran the method it overwrote by calling the function returned by data.getParentMethod(). We could take this further by adding other item types like potions, or adding specific weapons like bows, swords, and daggers that extend the weapon class, but this should give you an idea of how this module works, so we’ll stop here for now. Feel free to mess around with this on your own!

EXAMPLE USE WITH PLUGIN

Here, we’re going to do the same thing we did in EXAMPLE USE, but using the OOPer’s Dream plugin to make things a bit easier. I suggest still reading the EXAMPLE USE section. Let’s say we want to make an item system for an RPG game. First, open up the plugin. You’ll see a filter classes box and a empty box.
image
Right click on the empty box and press Create New Class. Now you have a new class! Select the class and either press F2, or right click on it and press rename to rename it. Let’s rename it to “Item”.
image
If you double click on the class or press enter, it will open another window for that item class.
image
Press the + on methods and rename it to “__init__”. Repeat this process to also create a “drop” and a “use” method.
image
You can double click or press enter on a method to edit it. __init__ will run when the object is created, passing any parameters that are passed on the object’s creation. Let’s edit __init__. Right away, we’ll see this:

return function(self, data)
	
end

Let’s have it so you pass the owner of the item, and store that under item.owner. We will end up with this:

return function(self, data, player)
	self.owner = player
end

Now, under drop, let’s have it set the owner to nil. It will look like this:

return function(self, data)
	self.owner = nil
end

Now, lets say under use, it prints out that the owner of the item used the item. It will look like this:

return function(self, data)
	print(self.owner.." used this item!")
end

Now, lets test this. Put the following into a script and run the game:

local ClassManager =  require(game.ReplicatedStorage.Classes.ClassManager)
local ItemClass = ClassManager.GetClass("Item")
local item = ItemClass.new("ianfinity01")
print(item.owner)
item.use()
item.drop()
print(item.owner)

This should create an Item class, print the owner, use the item, drop it, then print the owner again. However, since we dropped it, the owner should be nil the second time. Sure enough, we get:

ianfinity01
ianfinity01 used this item!
nil

Now, let’s take this a step further. Let’s create a weapon class and a food class that are subclasses of item. In the classes gui, hover over Item and press the plus icon twice. Rename the two classes you created to “Weapon” and “Food”.
image
Now let’s edit Food. Double click on it or press enter with it selected. You should see this:
image
The arrows mean the class is inherited from a parent class, and to the right of the name, you can see that they are all inherited from item. Since Food is a subclass of Item, it inherits Item’s methods. Now, let’s overwrite the use method. Right click on use and press overwrite. Now, use has an O next to it. This means its overwriting a method it inherited. If you edit use, you’ll see this:

return function(self, data)
	local overwrittenMethod = data.getParentMethod()
	overwrittenMethod()
end

We do not need everything inside the function, so just delete that for now.

return function(self, data)
     
end

Now, lets make it so that it prints that the owner of the item ate it, then drops the item. Food’s use method should now look like this:

return function(self, data)
     print(self.owner.." ate this item!")
     self.drop()
end

Great, that’s the Food class. Swapping over to the weapon class, let’s overwrite use. Let’s make use take an additional parameter of who the weapon attacks. Then, let’s have it print who it attacks. Now, Weapon’s use method looks like this:

return function(self, data, target)
	print(self.owner.." attacks "..target)
end

Finally, let’s modify the drop method. Right click the drop method and press overwrite. This time, let’s not delete everything it gives us, and instead add something before it. Lets have it print that the owner of the item laid down their weapon. Now, Weapon’s drop method looks like this:

return function(self, data)
	print(self.owner.." laid down their weapon!")
	local overwrittenMethod = data.getParentMethod()
	overwrittenMethod()
end

You’re probably wondering what the code we left in does. It calls the getParentMethod() function of data, which returns the function that this method overwrote. Since weapon’s drop method overwrote item’s drop method, this will return item’s drop method. Then, we call that function. So basically, it just makes it so that instead of running this new drop method instead of the item drop method, it just runs it before the item drop method. Let’s test out everything we’ve done so far. Put this code into a serverscript and run the game:

local ClassManager =  require(game.ReplicatedStorage.Classes.ClassManager)
local WeaponClass = ClassManager.GetClass("Weapon")
local FoodClass = ClassManager.GetClass("Food")
local weapon = WeaponClass.new("ianfinity01")
local food = FoodClass.new("ianfinity01")
food.use()
print(food.owner)
weapon.use("bugs")
weapon.drop()
print(weapon.owner)

This will create a weapon and a food that belong to “ianfinity01”. Then, we use (eat) the food which should also drop it. Then, it prints the owner of the food, which should be nil because it was dropped. Then, it uses the weapon on “bugs”, and drops the weapon. It then prints the weapon’s owner which should be nil because it was dropped. Sure enough, we get this output:

ianfinity01 ate this item!
nil
ianfinity01 attacks bugs
ianfinity01 laid down their weapon!
nil

Recall that the weapon’s owner was set to nil when it was dropped because the item’s drop method was manually called in the weapon’s drop method. We could go a lot further with this by adding other item types like armor or potions, or we could make bow, sword, dagger, etc sub classes to weapon, but for the purpose of this mini-tutorial, let’s just leave it there. I hope this gave you the tools you needed to start making your own classes!

TUTORIAL

Let’s say you want to create a class. All you have to do is create a folder under the Classes folder in ReplicatedStorage. If you have the plugin, just right click on the empty box in the gui and press Create New Class. To access the class ingame, you can require the ClassManager module in the classes folder, and run .getClass(className) on the required module where className is the string name of the class. If you want to make the class a sub class of another class, add a string attribute to the folder called “Extends” (capital E) and set it to the name of the class you want it to inherit from. In the plugin, right click on the class and press Change Parent. To add a method, add a module script to the folder. Name the module script what you want the method to be called. Make it so that it returns a function that takes self and data as parameters. You can make it take additional parameters after self and data. Those must be manually passed when calling the method. In the plugin, double click on or press enter on the class you want to add a method to. Then, in the gui that opens, press the plus next to Methods to add a method. You can double click on it or press enter to edit it. You can right click and press rename, or press F2 on the method to rename it. In a method, self is the object on which the method was called, and data is a object that contains some extra information about the object on which the method was called, but we’ll get into that later. You can make a method private by adding a boolean attribute called “Private” on the method’s module script and setting it to true. On the plugin, right click on the method and press Make Private. To make a method static, add a boolean attribute on the method’s module script called “Static” and set it to true. On the plugin, right click on the method and press Make Static. A method CAN be both Private and Static. A private method can only be accessed using data (I’ll tell u how later). A static method can only be accessed on the class, not an object. Self will be the class on which it was called for static methods.

Suppose we have a class called “ExampleClass” and ran the following code:

local ClassManager = require(game.ReplicatedStorage.Classes.ClassManager)
local class = ClassManager.getClass("ExampleClass")
local object = class.new()

If we had a public non-static method called exampleMethod, we could do object.exampleMethod(). However, if it is static, we must do class.exampleMethod() instead.

If a method “privateMethod” is private, it must be accessed using data (the second parameter of all methods). This is so that it can only be accessed within the classes’ methods. If you have a non-static private method, within another non-static method, you can do the following:

local privateSelf = data.getPrivateObjectData()
privateSelf.privateMethod()

If you want to store private variables, you can also do this:

local privateSelf = data.getPrivateObjectData()
privateSelf.variableName = someValue

Then you can access that variable later using the data parameter of another parameter on the same object. For example, to print it, you could do this:

local privateSelf = data.getPrivateObjectData()
print(privateSelf.variableName)

You can only do data.getPrivateObjectData() on non-static methods. If you have a private static method called “privateMethod”, it can be accessed using this:

local privateClass = data.getPrivateStaticData()
privateClass.privateMethod()

You can similarly store data on privateClass. Remember that .getPrivateStaticData(), unlike .getPrivateObjectData(), returns data specific to the entire class, not specific to any one object of that class. The last thing data is good for is accessing overwritten methods. Let’s say that you have a public static method called “someMethod” on a class “Class1”. Let’s also say that you have another class “Class2” that extends Class1. If Class2 also has a public static method called “someMethod”. Class2’s someMethod will overwrite Class1’s someMethod if you call Class2.someMethod() Let’s say you want to run Class1’s someMethod in Class2’s someMethod. You can do data.getParentMethod() to get the method a method overwrote. So, if Class1’s someMethod looks like this:

return function(self, data)
     print("a")
end

And Class2’s someMethod looks like this:

return function(self, data)
     local superMethod = data.getParentMethod()
     superMethod()
     print("b")
end

And you run Class2.someMethod(), it will print this:

a
b

This is because in Class2’s someMethod, it calls Class1’s someMethod (which prints a), then prints b.
If you prefer calling methods with a : instead of a . in your code, you can enable this by setting an attribute on the class folder called “UseColons” to true. On the plugin, right click on the class and press “Use Colons”. The class will work identically except all methods must be called with a colon. In our previous example, you would run Class2:someMethod() instead of Class2.someMethod(). Keep in mind that enabling UseColons will also make built in functions like getClassName and IsA use colons as well.
As a tool to keep your code clean, you can also use properties, which are like variables that have set code to determine what is returned when reading from them and to determine what happens when writing to it. To make a property, make a module script and add an attribute to it named “Property” and set it to true. On the plugin create a new method, right click on it, and select “Convert to Property”. How these work is like the following

return function(self, data, set, val)

end

set is a boolean of whether or not it is being set to (or read from if set is false)
val is the value that it is being set to. If set is false, this will be nil.
if set is false, the value the function returns is what the property is read as.
For example, if you had the following code on a non static public property, you could read and write to the value as normal, but have prints telling you whenever the value is written to/read from

return function(self, data, set, val)
     local privateSelf = data.getPrivateObjectData()
     if set then
          privateSelf.testingVal = val
          print("Set property to"..tostring(val))
     else
          print("Read property (value is "..tostring(val)..")")
          return privateSelf.testingVal
     end
end

If the aforementioned module script (property) was named “ExProp” then you could do the following:

obj.ExProp = 5
print(obj.ExProp)

giving the following output:

Set property to 5
Read property (value is 5)
5

Properties are great if you need to limit what a value can be set to, or want to fire events/run code when the value is changed, but dont want to have a get method and a set method.
I hope this helps you use my module!

DOCS

Special Methods: if a method is named this, it will have special properties as said next to it.
__init__ This can be static or non-static. If it is static, it will run when the class is created. If it is non-static, it will run when an object is created. In its static form, its parameters will only be self (the class) and data. In its non-static form, its parameters will be self (the object), data, and anything else passed in the class.new(). In its non-static form, it may also return a value. If nothing is returned, or the returned value is nil, calling .new() will return the created object as usual. However, if it returns a non-nil value, .new() will return the returned value instead of the created object. This method cannot be called manually but will be called automatically. Keep in mind that you must manually call overwritten __init__ methods using data.getParentMethod()([params])
__negate__ This is only non-static. Its parameters will be self (the object) and data. Whenever you do -object, it will produce whatever this method returns.
__add__ This is only non-static. Its parameters will be self (the object), data, objectA, and objectB. One of objectA or objectB will be self, but the other can be anything. Whenever you do objectA + objectB where objectA or objectB is an object with this method, it will produce whatever is returned by calling this function.
__sub__ Like __add__ but whenever you do objectA - objectB.
__mul__ Like __add__ but whenever you do objectA * objectB.
__div__ Like __add__ but whenever you do objectA / objectB.
__mod__ Like __add__ but whenever you do objectA % objectB.
__pow__ Like __add__ but whenever you do objectA ^ objectB.
__str__ This is only non-static. Its parameters will be self (the object) and data. Whenever you do tostring(object), it will return whatever this method returns.
__eq__ Like __add__ but whenever you do objectA == objectB. Only works when comparing two objects of the same class, otherwise returns false.
__lt__ Like __add__ but whenever you do objectA < objectB. If you do objectA > objectB, it will return the result of this function, but swapping the order of the two parameters after self and data. Only works when comparing two objects of the same class, otherwise errors.
__le__ Like __add__ but whenever you do objectA <= objectB. If you do objectA >= objectB, it will return the result of this function, but swapping the order of the two parameters after self and data. Only works when comparing two objects of the same class, otherwise errors.
__len__ This is only non-static. Its parameters will be self (the object) and data. Whenever you do #object, it will return whatever this method returns.

A class can extend (inherit from) another class by adding a string attribute on the child class’ folder that is set to the name of the parent class (case sensitive)
If you require the ClassManager module in the Classes folder under Replicated storage, it will have the following methods:
GetClass(className) className is a string. Will error if there is a compilation error, or no class can be found with the name className. className is case sensitive. Returns the class with the name className
GetClassExtensionPath(className) className is a string. Will error if there is no class with the name className, or if the class hierarchy is invalid. Returns a list of classNames where the first is className, the second is the name of the className’s parent class, the third is the parent of that parent class, and so on.
IsObject(object) object can be anything. Will return true if it thinks it is an object of a class. Will return false if it thinks otherwise.
IsClass(class) class can be anything. Will return true if it thinks it is a class. Will return false if it thinks otherwise.

The following methods will always be found on a class (unless manually overwritten)
getName() Returns the name of the class.

The following methods will always be found on an object of a class (unless manually overwritten)
getClass() Returns the class it was created from.
IsA(className) className is a string. Returns if it is called an object of a class named className, or if the class of the object was called on is a descendant class of a class named className. If you had a class named Class1, and a class that extends Class1 called Class2, and you did Class2.new().IsA(“Class1”), it would return true. However, if you did Class1.new().IsA(“Class2”), it would return false.
getClassName() Returns the name of the class it was created from.

The following methods are on the data object that is passed as the second parameter of all methods:
getParentMethod() Returns the callable method the method overwrote. Returns nil if the method did not overwrite a method of a parent class.
getPrivateObjectData() Only callable in non-static methods. Returns a table that contains all private methods of the object. Can also be used to store private variables. The self parameter of private methods will still be the public object (or the public class in the case of static methods).
getPrivateStaticData() Can be called from a non-static or static method. Returns a table that contains all static private methods of the class. Can also be used to store private variables.

methods can be made static by setting an attribute “Static” to true on the module script
methods can be made private by setting an attribute “Private” to true on the module script
methods can be made into properties by setting an attribute “Property” to true on the module script
Properties should be in the format

return function(self, data, set, val)

end

where self and data are the same as always, but set is a boolean of whether or not you are setting to the property (or reading from it if it is false), and val is the value you are trying to set it to if set is true. If set is false, val will always be nil.

INSTALLATION

If you have the plugin, all you have to do is be in a place with the plugin enabled, and it will automatically install the module. You can get the plugin here: plugin. Make sure to enable script injection. If you do not have the plugin, insert this rbxm file into the ReplicatedStorage of your game:
oopersDream.rbxm (7.7 KB)

TROUBLESHOOTING

Here is a list of common errors you may encounter while the module tries to compile a class:
Error, className has an extension loop. Failed to compile. This means the class “className” has an extension loop. An example of this is if you have a class Class1, and a class Class2, and Class1 extends Class2, while Class2 also extends Class1.
Invalid type of Class className's 'Extends' value. Must be the string name of its parent class or nil. This means the class “className” has an attribute Extends, but the attribute is not a string or missing. The Extends attribute must either be a string, or not present.
Class className's' parent Class 'otherClassName' not found. This mean’s the class “className” has its Extends attribute set to “otherClassName” but there is no class with the name “otherClassName”. Make sure to check your capitalization and spelling. If you do not wish for it to extend anything, either remove the attribute, or set it to an empty string.
ERROR WHILE COMPILING: error requiring method methodName of class className. Error message below: This means the module script named “methodName” in the class “className” had an error while requiring. Examine the error below this message to fix the moduleScript.
ERROR WHILE COMPILING: incorrect return type of method methodName on class className. Must return a function. Self explanatory. The method “methodName” of the class “className” did not return a function when required.
ERROR WHILE COMPILING: multiple methods with the same name (methodName) on class className. This means that multiple methods have the same name (“methodName”) and the same visibility (same Static value and same Private value). Delete a module or change the name of one.

If you have an error that is not on this list that you believe is not your fault, please reply to this post.

IMPORTANT WARNINGS

Avoid naming any public non-static methods “IsA”, “getClass”, or “getClassName”. It will still compile, however it will overwrite important functions and is highly not recommended. Same thing goes for public static methods called “getName”. Avoid having multiple classes named the same thing. Never use rawset or rawget on a object or class as this may break properties or other things. Also never set the metatable of a class or object. You may use a class or object as a metatable, but methods (including colon functions) will be run with self as the class or object, ignoring its parent by __index, possibly causing unwanted behavior.

CHANGELOG

v1.1.0 initial release
v1.1.1 fixed special methods
v1.2.1 large bug fixes for plugin
v1.3.1 added the ability to return a value in __init__.
v2.0.0 added the ability to make classes use colons, added properties, added bug fixes for when the init of a class is not instantaneous
v.2.0.1 hotfix to inheritance bug

If you have any problems or questions, please let me know in the replies!

25 Likes

Looks super cool! Just a question, will this plugin cost Robux once released?

2 Likes

No, or at least no initially. I may in the future if a large amount of people use this. The package itself however will always remain free and open source, even if the plugin doesn’t.

3 Likes

This is actually very good! it makes everything organized and better, great module!

2 Likes

It is interesting how it is implemented using instances. Now classes can be seen in the game tree.

However, it seems that you did not implement inheritance. Inheritance is the most important feature of OOP because it is used for design.

Another possible problem is type checking. Generic Programming, which is more modern than OOP, is based on types and their parameters. It is true that Luau is not yet fully supported, but it is possible that in the future it will be.

Anyway good work.

3 Likes

This is just worse than using metatables. It doesn’t make anything more organized and is a bad practice + bad for performance.

5 Likes

Actually, it has both these things! If you read the example use, youll see an example of inheritance, and if you read the docs, youll see typechecking. To make a class inherit from another class, add a string attribute on the child class’s folder called “Extends”, then set it to the name of the parent class. As for type checking, all objects automatically come with an IsA method, where you can pass a string class name, and it will tell you if it is of that class, or inherits from that class. There is also .getClass, and more (read Docs). However, I will admit that the type checking this module offers, while helpful, is not quite at the same level as in other languages, but this is only due to the limitations of Lua.

1 Like

was certainly added to the Example. Then I had some doubts:

How do you call the constructor of the base class in the other constructors in the inheritance? It is usually mandatory to call the constructor of the base class first to avoid conflicts or unwanted overwriting.

How are static fields and methods used? In lua there is no clear way to differentiate between static and non-static methods without a special syntax.

as for access to members, exactly how was it implemented? accessibility (private, public) is usually more common in compiled languages where they are resolved at compile time so they are very useful when writing code (the editor will warn you if you are using something you shouldn’t). at run time you can’t do anything about it.

Regarding the second point, what you implemented is not type checking, it’s just the typical metadata access (IsA, getClass). Type checking is done at compile time and is especially valuable when tools, such as the code editor, provide adequate support (error marking, auto-completion, documentation).

1 Like

To call the constructor of the base class, all you have to do is this:

local baseConstructor = data.getParentMethod()
baseConstructor(parameters)

This is also outlined in the Example, but with the drop method.
As for how to mark a method as static, just add a boolean attribute on the module script called “Static” and set it to true. This is outlined in TUTORIAL I believe.

For access to members, I have a separate table only accessable using the data value that stores all private methods and variables. You can do

local privateData = data.getPrivateObjectData()
privateData.privateMethod()

to call a non-static private method called “privateMethod”. You can also store variables on it like this:

local privateData = data.getPrivateObjectData()
privateData.privateVariable = "test"

This is a strange way to accomplish this, I know, but it works fine and is good considering lua’s limitations. This is also outlined in TUTORIAL

As for the type checking, I think I’ll leave that up to the programmer, as that is what lua does for everything else.

1 Like

but in what order? should it go first?

I asked about how to use it, not how to create it.

This doesn’t exactly fit with the accessibility I was thinking of but that’s ok.

Personally I liked that the classes are shown in the game tree.

1 Like

For the constructor thing, its completely up to you when you want the parent constructor to run! If you want, you can also not run it at all, its completely up to the programmer.

As for how to use static methods, you just call it on the class instead of the object. If we have a non-static method called “method” on a class “Class”, then we access it like this:

local class = ClassManager.getClass("Class")
local obj = class.new()
obj.method()

However, if method is static, then it is accessed like this:

local class = ClassManager.getClass("Class")
class.method()

As for the accessability, yeah, its really weird, but there’s no way to make values on a table only accessable to certain functions, so this was the best I could think of.

1 Like

The plugin seems to be crashing studio every time I turn it on. I tested this by before going into a game turning the plugin off, then I go into a new game and turn on the plugin and studio instantly crashes.

2 Likes

odd, it works for me. let me try a couple things

2 Likes

pushed an update on a small change. Not sure if it’ll do anything, but it might fix it.

2 Likes

That seems to have done it! It’s working now. Thank you very much!

2 Likes

After some usage and testing I have to say, I absolutely LOVE this module! I personally found Object Oriented Programming annoying. Like I loved it yes but I would always have such weird issues with it. But this makes it so much easier! Thank you so much for sharing this with the community!

2 Likes

I just released it, so im still looking to fix things. If you run into any problems, please let me know and ill try to fix it ASAP! One quick note, i am aware of a bug where playing the game will break the edit class window and am looking for solutions.

2 Likes

I fixed the bug with the plugin breaking when you run the game, as well as added more functionality to init methods (read docs)

1 Like

Yeah no kidding. Plus you are stuck using the entire system. Big downside

3 Likes

The client server methods are a great idea! I’ll definately consider that. The CommunicationClass isn’t a bad idea either. I’m not quite sure what you mean by that last thing though

1 Like