There some problems with my OOP.
I have parent with clear table and child with same table.
When i'm trying to add object to table of child, object adds to parent's table.
Simple example:
Account = {}
Account.__index = Account
Account.kit = {}
function Account.create(balance)
local acnt = {} -- our new object
setmetatable(acnt,Account) -- make Account handle lookup
acnt.balance = balance -- initialize our object
return acnt
end
function Account:withdraw(amount)
self.balance = self.balance - amount
end
-- create and use an Account
acc = Account.create(1000)
acc:withdraw(100)
table.insert(acc.kit, "1")
print(#Account.kit)
print(#acc.kit)
Result is 1 and 1.
But must be 0 and 1.
How i can isolate child table from parent?
In Lua using acc.kit where acc is a table with the metatable Account will first search the key kit from the table acc and then from table Account.
In your code acc does not have anything with key kit, so the Account.kit will be accessed.
You can solve this simply by defining the kit table for the acc on creation
Account = {}
Account.__index = Account
Account.kit = {} -- You can now remove this if you do not use it - I preserved it to make the test prints to still work.
function Account.create(balance)
local acnt = {} -- our new object
setmetatable(acnt,Account) -- make Account handle lookup
acnt.balance = balance -- initialize our object
acnt.kit = {} -- define kit to be a subtable
return acnt
end
Example:
https://repl.it/B6P1
I'd recommend using closures to implement OOP:
local Animal = {}
function Animal.new(name)
local self = {}
self.name = name
function self.PrintName()
print("My name is " .. self.name)
end
return self
end
--now a class dog that will inherit from animal
local Dog = {}
function Dog.new(name)
-- to inherit from Animal, we create an instance of it
local self = Animal.new(name)
function self.Bark()
print(self.name .. " barked!")
end
return self
end
local fred = Dog.new("Fred")
fred.Bark()
fred.PrintName()
output:
Fred barked!
My name is Fred
Related
local Public = {}
function Public.new(ent)
local State = {}
function State:update(player)
ent:setLinearVelocity(0,0)
end
function State:start(player)
ent.fixedRotation = true
self.attackTimer = _G.m.addTimer(200, function()
ent:setState('attacking', player)
end)
end
function State:exit(player)
ent.fixedRotation = false
timer.cancel(self.attackTimer)
end
return State
end
return Public
I'm using a linter and its complaining that I'm using the colon unnecessarily for my update and exit methods. The reason I do this is to keep all my methods uniform. Sometimes I need self and sometimes I don't.
But in general is there any advantage to using colon on these at all? It seems like if i have something like State:start then I could just reference State directly. I could do State.attackTimer vs self.attackTimer..
Why would you ever really need the colon? If you have access to the table that holds the method then you have access to self.. right?
The : syntax is a great tool when you are making a class using a table and a metatable.
Your code above, rather then creating a class, creates an encapsulated set of functions. which have access to State as an upvalue.
I will use this class from Lua Users - SimpleLuaClasses as an example:
Account = {}
Account.__index = Account
function Account:create(balance)
local acnt = {} -- our new object
setmetatable(acnt,Account) -- make Account handle lookup
acnt.balance = balance -- initialize our object
return acnt
end
function Account:withdraw(amount)
self.balance = self.balance - amount
end
-- create and use an Account
acc = Account:create(1000)
acc:withdraw(100)
Here we have an instance(acc) of the Account class. To adjust or modify the values in this specific instance of Account we can not refer to Account.balance inside of Account:withdraw. We need a reference to the table where the data is stored, and that is where passing that table using : comes in.
acc:withdraw(100) is just syntactic sugar for acc.withdraw(acc, 100) passing in our table as the first param self. When you define Account:withdraw(amount) there is an implicate first variable self the definition could be written as Account.withdraw(self, amount)
I'm aware there are a few questions about implementing OOP in Lua on this site, however, this one is a bit different (at least compared to what I found).
I'm trying to create a class called "human", and make it so objects created with the "new" constructor of "human", inherit everything inside human except it's constructor. However, I also don't want to be able to use methods inside of human, on human. So whatever's inside the human class, is only passed to created objects. Here's an example:
-- "Human" class
human = {}
function human.new(name)
local new = {} -- New object
-- Metatable associated with the new object
local newMeta =
{
__index = function(t, k)
local v = human[k] -- Get the value from human
print("Key: ", k)
if type(v) == "function" then -- Takes care of methods
return function(_, ...)
return v(new, ...)
end
else
return v -- Otherwise return the value as it is
end
end
}
-- Defaults
new.Name = name
new.Age = 1
return setmetatable(new, newMeta)
end
-- Methods
function human:printName()
print(self.Name)
end
function human:setAge(new)
self.Age = new
end
-- Create new human called "bob"
-- This works as expected
local bob = human.new("Bob")
print(bob.Name) -- prints 'Bob'
bob:printName() -- prints 'Bob'
bob:setAge(10) -- sets the age to 10
print(bob.Age) -- prints '10'
-- But I don't want something like this allowed:
local other = bob.new("Mike") -- I don't want the constructor passed
-- I'd also like to prevent this from being allowed, for "human" is a class, not an object.
human:printName()
So creating the object with human.new("Bob") works fine, but it also passes the constructor, and I can still use the object methods on the class. I'm very new to the concept of OOP, so I'm sorry if this was a horrible question. But if anyone could help, I'd appreciate that.
I have run into the same issue before. You need two tables. One for object methods and one for class methods. Set the metatable of constructed objects to the object method table. For example:
local Class = {}
local Object = {}
Object.__index = Object
function Class.new()
return setmetatable({}, Object)
end
setmetatable(Class, {__call = Class.new})
function Object.do()
...
end
return Class
And use it
Class = require('Class')
local obj = Class.new() -- this is valid
obj.do() -- this is valid
obj.new() -- this is invalid
Class.do() -- this is invalid
I just started getting my feet wet with Corona and trying to work with OOP. This is just a simple Match Making Game to help me along with thinking like OOP. I have 2 classes, one class will be creating an instances of a Card (i will be making multiple objects of this type of Card Class) and the other is the MatchCardsManager Class - this creates the cards and applies the properties
The error I am getting is, after i have created the object "MatchCard" i tried to apply an "addEventListener" to the object. but when i do i receive an error of the following
Support/Outlaw/Sandbox/5/MatchCardsManager.lua:53:
attempt to call method 'addEventListener' (a nil value)
stack traceback:
If i comment out the info on addEventListener, all objects are displayed accordingly to the constructor i created in MatchCard Class.
Below are my files - the error i am getting is in the MatchCardsManager class
mCard[i] = MatchCardsManager:displayPlacementCard(i, temp, x, y)
mCard[i]:addEventListener("touch", myTouch)
Any help or suggestions about fixing this or better approach would be greatly appreciated. Thank you.
The MatchCard Class will be just a simple constructor for now as this is not my issue
-- MatchCard Class
-- Meta Data
local sceneGroup = sceneGroup
local MatchCard = { }
local MatchCard_mt = { __index = MatchCard } -- metatable
------------------------------------------------
-- PRIVATE FUNCTION
------------------------------------------------
------------------------------------------------
-- PUBLIC FUNCTION
------------------------------------------------
-- constructor
function MatchCard.new (id, animal, state, imageId, x, y)
local newMCard = display.newRoundedRect( x, y, 59, 47, 5)
newMCard.strokeWidth = 3
newMCard:setFillColor( 0.5 )
newMCard:setStrokeColor( 1, 0, 0 )
newMCard.properties = {
id = id or nil,
animal = animal or nil,
state = state or 0,
imageId = imageId,
}
return setmetatable ( newMCard, MatchCard_mt )
end
MatchCardsManager Class is there I plan to create an many instances of cards
-- require files
local MatchCard = require('MatchCard') --MatchCard
local sceneGroup = sceneGroup
local MatchCardsManager = {} -- originally we should use a displayGroup
-- TODO: insert group into scene
local animalPicsReference = { "dog", "dog", "cat", "cat", "pig", "pig" , "fish", "fish"}
-- manager class properties
MatchCardsManager.totalCards = 8
MatchCardsManager.totalPairs = 4
MatchCardsManager.pairsFound = 0
MatchCardsManager.firstCardSelected = 0
MatchCardsManager.secondCardSelected = 0
-- lets create 6 MatchCardFiles
function MatchCardsManager:create()
local animalPics = animalPicsReference
local x = 108 - 85
local y = 125
print("do we go here never works")
local mCard = {}
for i=1, 4
do
x = x + 85
num = math.random(1, #animalPics)
temp = animalPics[num]
table.remove(animalPics, num)
mCard[i] = MatchCardsManager:displayPlacementCard(i, temp, x, y)
mCard[i]:addEventListener("touch", myTouch)
end
x = 108 - 85
y = 195
for j = 5, 8 do
x = x + 85
num = math.random(1, #animalPics)
temp = animalPics[num]
table.remove(animalPics, num)
mCard[j] = MatchCardsManager:displayPlacementCard(j, temp, x, y)
print(type(mCard[j]))
mCard[j]:addEventListener("touch", myTouch)
end
--mCards:addEventListener("touch", myTouch)
return mCard
end
local function myTouch( event )
if event.phase == "began" then
print( "You touched the object! "..event.target.imageId)
end
end
function MatchCardsManager:displayPlacementCard(idx, animal, x, y)
-- randomly place the cards in the object id
local card = MatchCard.new(idx, animal, 0, animal, x, y)
--card:show(x,y) -- displays card and that is it
print("animal added is "..card.properties.imageId)
return card
end
return MatchCardsManager
The problem is in the constructor.
local newMCard = display.newRoundedRect(...)
creates a display object, but the line:
return setmetatable(newMCard, MatchCard_mt)
overwrites the metatable that the display object had and so it no longer has access to display's __index metamethod that is used to find addEventListener.
To fix this, you need to look into how inheritance is added in Lua.
See Inheritance Tutorial so you can inherit addEventListener. The solution will be something like: setmetatable(MatchCard, {__index=ShapeObject}) or =display.ShapeObject}---I can't be sure how Corona implements its classes.
I am trying to create a class in Lua, which holds information about who is this class following, and I also want followed class to know about his follower.
But my code fails, and I dont know why.
The error is
lua: OOP.lua:90: attempt to call method 'getName' (a nil value)
Vehicle = {}
--------
--FIELDS
--------
Vehicle.name = ""
Vehicle.vtype = "";
Vehicle.follower = {};
Vehicle.leader = {};
---------------------
--SETTERS AND GETTERS
---------------------
function Vehicle:setName(value)
self.name = value
end
function Vehicle:getName()
return self.name
end
function Vehicle:setType(value)
self.vtype = value
end
function Vehicle:getType()
return self.vtype
end
function Vehicle:setFollower(pVeh)
self.follower = {};
self.follower = pVeh;
end;
function Vehicle:getFollower()
return self.follower;
end;
function Vehicle:getLeader()
return self.leader;
end;
function Vehicle:setLeader(pVeh)
self.leader = pVeh;
if (self.leader ~= nil) then
print ("-- setted leader! --");
end;
end;
-----------
--METHODS--
-----------
function Vehicle:follow(pVeh) --> why does this not work?
self:setLeader(pVeh);
pVeh:setFollower(self);
end;
-- constructor
function newVehicle(pNm,pTp)
tbl = {}
setmetatable(tbl, {__index = Vehicle});
tbl:setName(pNm);
tbl:setType(pTp);
return tbl
end
-----------------------------------------------
-- TEST
-----------------------------------------------
local car1 = newVehicle("Mazda","Car");
local car2 = newVehicle("Harley","Bike");
print("--------------");
print (car1:getName()); --> prints "Mazda"
print (car2:getName()); --> prints "Harley"
car2:follow(car1)
print ("Leader of " .. car1:getName() .. " is " .. car1:getLeader():getName())
The problem is in your test code, from what I can see:
print ("Leader of " .. car1:getName() .. " is " .. car1:getLeader():getName())
car1 does not have a leader defined. (you did car2:follow(car1))
Some pointers:
You need to define field tables in the creation method. This won't apply to your code just yet, but say you wanted to do platoons. You'd do something like Vehicle.platoon = {}; Tank1.platoon[Tank2] = true -- This would add Tank2 to Vehicle's platoon table, and share it with every vehicle. If you define this in the new method: function newVehicle(...) tbl.platoon = {} ... end, then it would be unique to every vehicle.
Don't use accessor (get*, set*) methods unless you have a solid reason for using them. They add clutter, and are not good for performance. A good reason to use them would be if you are accessing a table in your accessor method, and want to simplify the interaction with that. (i.e Obj:addChild(Obj2) obj:hasChild(Obj2)) But then it probably doesn't qualify as an accessor anymore :)
You can use dummy objects for unset fields. This'll prevent errors like yours:
local Dummy = newVehicle("None", "None")
Vehicle.follower = Dummy
Vehicle.leader = Dummy
In your code, this produces:
--------------
Mazda
Harley
-- setted leader! --
Leader of Mazda is None
car2:follow(car1)
car1 is being followed by car2. Whereas, later; you're trying to access car1's leader, which, of course returns nil. Hence the error.
print( "Follower of "..car1:getName().." is "..car1:getFollower():getName() )
I have created a small class called Piece, which looks as follows
local Piece = {}
local Piece_mt = { __index = Piece}
function Piece.New(name, img, startPosX, startPosY)
newPiece = {}
newPiece.name = name;
newPiece.img = display.newImage(img, startPosX, startPosY);
print(newPiece.name);
local function OnHit ( event )
if event.phase == "ended" then
print(newPiece.name);
end
end
newPiece.img:addEventListener("touch", OnHit);
return setmetatable( newPiece, Piece_mt )
end
return Piece
When I create the class, it prints the name, and it is correct (matches what I passed as a parameter). However, when I later click the image, and the name is printed as a result of the triggered OnHit function, the name printed is always the name of the last object I created, regardless of which of the objects I click on. Could someone explane why this happens and how to fix it?
function Piece.New(name, img, startPosX, startPosY)
newPiece = {}
-- process
return setmetatable( newPiece, Piece_mt )
end
Here, you are assigning the global variable newPiece and return it every time. Instead, try use a local variable:
function Piece.New(name, img, startPosX, startPosY)
local newPiece = {}
-- process
return setmetatable( newPiece, Piece_mt )
end