Could I see the module where Move.new
is made?
Sure.
local Move = {}
Move.__index = Move
-- Creates framework for all future moves
function Move.new(Player, Element, Cooldown, Stamina, LastUsed)
local newmove = {}
setmetatable(newmove, Move)
newmove.Element = Element
newmove.Cooldown = Cooldown
newmove.Stamina = Stamina
newmove.LastUsed = LastUsed
newmove.Player = Player
return newmove
end
return Move
This is the module for Move.new
I think that the problem is that youâre trying to setmetatable on an already set metatable, so alternatively you could just return a table created based off all the passed arguments because you arenât really utilizing the point of OOP with the Move
module.
This created a whole new error,
And to the previous reply I was intending on the Move module being a superclass since not all moves use the mouseâs location, i.e AOE attack.
ok i just noticed the error, try doing this
local Move = {}
local newmove = {}
Move.__index = Move
setmetatable(Move,newmove)
-- Creates framework for all future moves
function Move.new(Player, Element, Cooldown, Stamina, LastUsed)
newmove.Element = Element
newmove.Cooldown = Cooldown
newmove.Stamina = Stamina
newmove.LastUsed = LastUsed
newmove.Player = Player
end
return Move
@RomeoEDD OOP convention says that a constructor should be dot syntax. Changing it to a colon doesnât make it a constructor and instead a member function, which is a silly mutation of OOP. As for your new post, thatâs not how you handle object construction with OOP.
@Rhaoke Metatables can be nested. I donât explicitly remember how thatâs done, but itâs in regards solely to the __index metamethod.
Overall, there are a couple of OOP practice mistakes that should be corrected as far as the Move module goes.
-
If you set the metatable first, then setting indices in your table can invoke metamethods. You should be using rawset in this case.
-
Constructing a table without all its elements pre-filled is slightly more costly than not. Iâd suggest writing your module a different way, such as filling the elements first and then setting the metatable or doing that all in one go.
-
If Move doesnât need to be an object, donât force it into OOP convention. This is assuming that you donât have any member methods attached to the Move table which serves as your base class. Just return a standard table.
-
If youâre following Lua conventions, arguments should be camelCase.
local Move = {}
Move.__index = Move
-- Get rid of any metatables if there are no member functions to Move
function Move.new(player, element, cooldown, stamina, lastUsed)
local newMove = setmetatable({
Player = player,
Element = element,
Cooldown = cooldown,
Stamina = stamina,
LastUsed = lastUsed
}, Move)
return newMove
end
return Move
If Move is supposed to serve as a base class and AirBlast, for example, is meant to serve as an inherited subclass, then donât set a metatable for Move, just have it return a table of data that you can add member functions to through your inherited class.
But in the other function when he wants to get the player how would it be done because there is no metabtable attached to the move module itself so when it references it would it be nil.
Thank you for your reply, I am very new to OOP and am not very familiar with the ins and outs of how all of it works, I will try what you have provided for me.
Multiple arguments are being returned back as nil, meaning the constructor is invalid or not receiving arguments as intended (which Iâm still trying to figure out). Changing the syntax only passes self as the first argument but that doesnât explain why several subsequent parameters are nil.
In OOP, object construction is not done with colons.
wouldnt you just need to attach the newMove as a metatable to the Move module itself so when it first notices that the module itself doesnt not have the index it will check if the metatable would have it like i did in this line -
setmetatable(Move,newmove)
--so this is how it should be done:
local Move = {}
Move.__index = Move
-- Get rid of any metatables if there are no member functions to Move
function Move.new(player, element, cooldown, stamina, lastUsed)
local newMove = setmetatable(Move,{
Player = player,
Element = element,
Cooldown = cooldown,
Stamina = stamina,
LastUsed = lastUsed
})
--return newMove -- i am not sure why this is here you dont have the function equal to a variable when your calling it in the local script
end
return Move
To provide you with more information on what exactly I am trying to do, as said in OP I posted earlier about making a move system, with different elements.
One of my friends, Stratiz, suggested I used OOP to load the move modules for a certain element into a table and iterate through them to determine which one was needed based on a keypress.
This is why I was using OOP in the first place.
The problem with the code you provided is that the table is static and constructed only once.
local Move = {}
local newmove = {}
This means that every time the module is required, itâs going to see the same table. For OOP-in-Lua, objects are represented by tables or userdata (created via newproxy). When you call the new method of your pseudoclass, the code calling new expects a new object to be returned. Thus, rather than creating it as an upvalue, youâre meant to create it in the function itself.
The main purpose of metatables in classes is attaching member functions to created objects. The way youâve done it is backwards and incorrect. What your code does is attempt to set a new table as a metatable to the Move table without __index.
Some of these articles may help you understand the choices I made in writing out that code:
https://devforum.roblox.com/search?q=oop%20category%3A71
Youâll notice a pattern of how to create objects.
This is here so that a module that calls new can receive the newly created object. Without it, Move.new is creating a blank table without references thatâll just get discarded and garbage collected (itâs purely data, minus the Player if itâs an Instance).
Would you mind if I contacted you in the future about OOP since I have seen little resources, other than of course, this post:
I promise I only want to learn.
i think i know what your are doing wrong first change the local script to this
local Player = game.Players.LocalPlayer
-- Input (To Server)
UserInputService.InputBegan:Connect(function(key,chatting)
if chatting or Move_Break then return end
local B = tostring(key.KeyCode)
Move_Break = true delay(1,function() Move_Break = false end)
for i,v in pairs(MoveModules) do
local Move = require(v)
if Move.Key == B then
local NewMove = Move.new(Player.Name, Move.Element, Move.Cooldown, Move.Stamina, Move.LastUsed, Mouse.Hit)
NewMove:DoMove()
end
end
end)
then in the module script do this
local Move = {}
Move.__index = Move
-- Get rid of any metatables if there are no member functions to Move
function Move.new(player, element, cooldown, stamina, lastUsed)
local newMove = setmetatable({
Player = player,
Element = element,
Cooldown = cooldown,
Stamina = stamina,
LastUsed = lastUsed
}, Move)
return newMove
end
return Move
You removed this, which means the module isnât defined at all.
local NewMove = require(v)
The information is helpful. Sometimes I struggle a bit with OOP, especially in regards to inheritance. Thatâs what I see your code is trying to accomplish; Move is a superclass and AirBlast is an inherited subclass. I donât know if there are any articles on OOP inheritance but theyâre worth taking a look.
What Iâm thinking is that you will want Move to return a table of pure data and not make it a pseudoclass, so that AirBlast (an inherited class) can add member functions to it. For example, weâll have the Move module return some data and then attach the AirBlast functions on.
Hereâs what Iâm thinking roughly would work out. Keep the Move class the same but donât actually reference it. Your move modules will instead take advantage of the Moveâs constructor to make a data table before adding things to it. If AirBlast doesnât need to create any properties in the table and only contains functions to be attached, all the better.
Move constructor:
local Move = {}
function Move.new(args)
return dictionaryWithoutMetatable
end
return Move
AirBlast module:
local AirBlast = {}
AirBlast.__index = AirBlast
local Move = require(pathToMoveModule)
function AirBlast.new(args)
local newMove = Move.new(args)
return setmetatable(newMove, AirBlast)
end
function AirBlast:Something()
for key, value in pairs(self) do
print(key, value)
end
end
return AirBlast
Point Iâm trying to make here is that if your AirBlast class contains only functions and your Move contains only a constructor that makes a table of properties, we can split the work up. Move becomes a data constructor that other classes make use of. Therefore, when we call the new method of a move (e.g. AirBlast), it first sets up data via Move.new and then attaches its functions on.
it should have said Move.new() sorry look now
I like the framework that you have laid out for me here, I will look into changing the system to something like that, I appreciate the length and detail of responses you have provided.
Donât take my word fully though. OOP is still something Iâm fresh out of water for. Inheritance is one thing I canât get, but classes in general are fine. I donât think pure data is the proper way to do inheritance but itâs a good start if youâre new to the concept.
The post that you linked (which is a strong OOP resource) has a whole section on inheritance so itâs worth trying to dissect the information laid out there. I believe that it has the ânested metatablesâ thing I was talking about earlier. Youâll notice that the Truck object is created from using the Car object.
Yeah, I used it as basically a one-to-one reference since I had no idea about anything OOP related, I donât think many people are familiar with it.
That is why I was confused that when I used almost the same syntax that it errored.
I have realized how big of a task figuring out this whole OOP concept is.