I want to add custom method to display object something like the prebuilt methods for example "setFillColor"
i wrote the following code which is not working; however, it explains what i need
function display:foo(bar)
print(bar)
end
local myRectangle = display.newRect( 0, 0, 150, 50 )
myRectangle:foo("something to be printed")
i want the "foo" method to be ready for all DisplayObjects not only myRectangle?
Example below. Untested but should work. Then it will be available to all newRect calls. You would have to do this for all display.* calls that you wish to use however
local oldNewRect = display.newRect
function display.newRect( ... )
local rect = oldNewRect( ... )
function rect:foo( str )
print( str )
end
return rect
end
-- usage
local rect = display.newRect( 0, 0, 40, 40 )
rect:foo( "hello" ) -- prints "hello"
I prefer not to replace any methods in the library because this means that every object created, not just those you create, are affected, and this could have unintended side effects. So I would advocate simply creating a function that adds the desired method to every instance you create. This method also serves as a signature that this display object is one of "yours" (not one created "behind the scenes" by Corona). For example,
function myNewCustomRect(...)
local obj = display.newRect(...)
obj.foo = function (self, a,b,c)
-- self is obj, although obj is an upvalue so can use directly too
print(self, obj, a,c,b)
end
return obj
end
yourRect = myNewCustomRect(0, 0, 150, 50)
yourRect:foo(1,2,3) -- first two data printed should be same
A bit complicated as we need to hijack the display.newRect call. Here is how to do it:
-- Store the old method
local oldNewRect = display.newRect
display.newRect = function(...)
local oDisplayObject = oldNewRect(...)
oDisplayObject.foo = function(oDisplayObject, sText)
-- Do something with the display object
-- Print the text passed as argument
print(sText)
end
return oDisplayObject
end
local myRectangle1 = display.newRect( 0, 0, 150, 50 )
myRectangle1:foo("something to be printed")
local myRectangle2 = display.newRect( 0, 0, 150, 50 )
myRectangle2:foo("something to be printed")
Related
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'm confused by how Lua properties are working in some of the code I'm trying to maintain. I've spent a good amount of time in the Lua documentation before this. So...
There is a function in one of those Lua tables, like this (we'll call this the 'nested tables' example):
function addItem(item)
index = itemTable.getIndex(item.position[1], item.position[2])
itemTable.items[index] = item
end;
a = Xpl3dSwitch { position = { 27, 0, 1, 1} }
itemTable.addItem(a) --doesn't seem to 'register' the position property
whereas
a = Xpl3dSwitch { }
a.position[0] = 27
a.position[1] = 0
itemTable.addItem(a) --this 'registers' the position properties
...etc, seems to work. Why are the position tables not sticking in the 'nested table' example?
Also, regarding 'a = Xpl3dSwitch { }' - is it an object constructor? It's not clear from the Lua 'documentation' what this is.
Look inside the table a and compare them. That should point you in the direction where the error happens.
to look inside a use something like:
function getTableContent(tab, str)
str = str or "table"
for i, v in pairs(tab) do
if type(v) == "table" and v ~= _G then
str = str.."->"..tostring(i)
getTableContent(v, str)
else
print(str.." Index: "..tostring(i).." Value: "..tostring(v))
end
end
end
getTableContent(a)
io.read()
Once you know how the working and the not working one are structured you should be able to make the adjustments needed.
Edit:
Also you could use:
a = Xpl3dSwitch { }
a.position = {27, 0, 1, 1}
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
I'm learning lua and i'm trying the OOP approach.
To start, I'm trying to make a grid class, but I think i'm still lacking some knowledge to make it do what I want it to do.
This is my code so far:
local screen = require( 'data.screen')
Grid = {}
Grid_mt = { __index = Grid }
--- Constructs a new Grid object.
function Grid:new( params )
local self = {}
local function try( self, event )
print(self.empty)
end
for i = 1, screen.tilesAcross do
if not self[i] then
self[i] = {};
end
for j = 1, screen.tilesDown do
self[i][j] = display.newImageRect( params.group, "images/playable.png", screen.tileWidth, screen.tileHeight )
self[i][j].x = (j - 1) * (screen.tileWidth + screen.tileSpacing) + screen.leftSpacing
self[i][j].y = (i - 1) * (screen.tileHeight + screen.tileSpacing) + screen.topSpacing
self[i][j]._x = i
self[i][j]._y = j
self[i][j].enable = false
self[i][j].empty = true
self[i][j].tap = try
self[i][j]:addEventListener( "tap", self[i][j] )
end
end
setmetatable( self, Grid_mt )
return self
end
function Grid:setEnable(value, x, y)
if value ~= true and value ~= false then
error("Boolean expected")
end
self[x][y].enable = value
end
function Grid:getEnable(x, y)
return self[x][y].enable
end
function Grid:setEmpty(value, x, y)
if value ~= true and value ~= false then
error("Boolean expected")
end
self[x][y].empty = value
end
function Grid:getEmpty(x, y)
return self[x][y].empty
end
function Grid:SetmColor()
self[1][4]:setFillColor( 255,255 )
end
I have 2 questions:
My event works but I would like to do something like: print(self:getEmpty() )
but whenever I try to use a method in my event, it doesn't work "attempt to call method 'getEmpty' (a nil value)"
and also the setfillcolor wwon't work, I want to be able to change the color of a case with it.
Thanks for your time!
and if i'm going to the wrong road, let me know, and by the way, I have a working code without make a class, this is just for training purpose :)
Thnaks!
The issue is that self[i][j] is not a Grid, only self is a Grid. So when the event handler is called, self in the handler is the display object, not the Grid object. If the handler needs to know the grid object, you could assign the grid to each display object:
self[i][j].grid = self
then in try you could do
grid = self.grid
But in this case you may get a cleaner design if you have the tap handler not be specific to each display object. In this case you would use an upvalue, you can just use self:
function Grid:new( params )
local self = {}
local function try( event ) -- omit the first param, self is an upvalue
print(self.empty)
end
for i = 1, screen.tilesAcross do
...
for j = 1, screen.tilesDown do
self[i][j] = display.newImageRect( ... )
...
self[i][j]:addEventListener( "tap", try )
end
end
Since listener given is a function rather than a table, it gets only one parameter, the event, and self is an upvalue so it will be the Grid instance created just above the try function.
For the first question, more information would be needed. See my comment above.
As for the setFillColor not working, the following is taken from the Corona docs:
object:setFillColor( gray )
object:setFillColor( gray, alpha )
object:setFillColor( red, green, blue )
object:setFillColor( red, green, blue, alpha )
object:setFillColor( gradient )
gray, red, green, blue, alpha (optional)
Numbers between 0 and 1 that represent the corresponding
value for that channel. alpha represents the opacity of the object.
Now, when you are trying to execute:
self[1][4]:setFillColor( 255,255 )
you are passing the values for gray and alpha channels. The values NEED TO BE less than, or equal to 1. Probably you want to pass 1 (as 255 is generally the max. value)
I want to make a class, that holds some graphics and dispatch events from this class when a button is tapped... here is my simple code:
MyClass.lua:
local Class = {}
local Class_mt = { __index = Class } -- metatable
local thisObject
-------------------------------------------------
-- PRIVATE FUNCTIONS
-------------------------------------------------
local function tapListener( e ) -- local; only visible in this module
local event = {
name = "customEvent",
mycustomvar = "this is my custom var"
}
thisObject.body:dispatchEvent( event )
end
-------------------------------------------------
-- PUBLIC FUNCTIONS
-------------------------------------------------
function Class.new( ) -- constructor
local rect = display.newRect( 0, 0, 50, 50 )
rect:addEventListener( "tap", tapListener )
local newClass = {
body = rect
}
return setmetatable( newClass, Class_mt )
end
-- added this function to get reference to the object
function Class:init()
thisObject = self
end
-------------------------------------------------
return Class
and then create this class and listen to some events being dispached:
local MyClass = require( "MyClass" )
function myCustomEvent(e)
print(e.mycustomvar)
end
local myNewClass = MyClass.new()
myNewClass:init()
myNewClass.body:addEventListener("customEvent", myCustomEvent)
This does work .. but seems kinda hacky - please tell me what you think?
Thanks.
As explained on http://docs.coronalabs.com/api/type/EventListener/dispatchEvent.html, the dispatchEvent is a method of a display object. So you can't call it on your Class, because class is just a table representing a "Class" class (you should probably name it something else ;) So you would call dispatchEvent on the rect (if it represents a custom event that occured in the rect), or from the runtime (if it represents an "application" event). If custom event is for the rect, and since the event listener callbacks only take parameter (the event), you should inlude a "target" member in your custom event in case the listener wants to know which "rect" sent the event.
local function tapListener( e ) -- local; only visible in this module
local event = {
name = "customEvent",
myscustomvar = "this is my custom var",
target = rect
}
rect:dispatchEvent( event )
end
Notice how you use rect in tapListener, so that function needs access to rect. Probably best to put the tapListener inside the Class.new():
function Class.new( ) -- constructor
local rect = display.newRect( 0, 0, 50, 50 )
local function tapListener( e ) -- local; only visible in this module
local event = {
name = "customEvent",
myscustomvar = "this is my custom var",
target = rect
}
rect:dispatchEvent( event )
end
rect:addEventListener( "tap", tapListener )
...
end
If you don't like to create functions in the contructor, there are other ways, they each have different advantages and disadvantages. I personally like to keep things together so I would favor the above. You could also move the various handlers (like tapListener) to a "listener factory" function like this:
function bindCustomEventDispatcher(rect)
local function tapListener(e)
local event = { ... } -- create custom event
rect:dispatchEvent( event )
end
local function touchListener(e)
local event = { ... } -- create custom event
rect:dispatchEvent( event )
end
local function zapListener(e)
local event = { ... } -- create custom event
rect:dispatchEvent( event )
end
rect:addEventListener( "tap", tapListener )
rect:addEventListener( "touch", touchListener )
rect:addEventListener( "zap", zapListener )
end
I didn't suggest using module-global variable as it doesn't scale as easily to more than one object (rect instance), you would have to use an array. But if that is not a concern then your approach is perfectly valid.
I reviewed the code. Now it should work.
MyClass.lua:
local Class = {}
local Class_mt = { __index = Class } -- metatable
-------------------------------------------------
-- PRIVATE FUNCTIONS
-------------------------------------------------
local function tapListener( e ) -- local; only visible in this module
local event = {
name = "customEvent",
myscustomvar = "this is my custom var"
}
Runtime:dispatchEvent( event )
end
-------------------------------------------------
-- PUBLIC FUNCTIONS
-------------------------------------------------
function Class.new( ) -- constructor
local rect = display.newRect( 0, 0, 50, 50 )
rect:addEventListener( "tap", tapListener )
local newClass = {
body = rect
}
return setmetatable( newClass, Class_mt )
end
-------------------------------------------------
return Class
main.lua
local MyClass = require( "MyClass" )
local mynewclass = MyClass.new()
local function myCustomEvent(e)
print(e.myscustomvar)
end
Runtime:addEventListener("customEvent", myCustomEvent)