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)
Related
I am building a game with enemies and players, all of which are set up to have various states for animation and behavior. The parent class for both is Entity.lua, from which they inherit variables and methods. However, while both enemies and players are inheriting the variables, for some reason the enemies do not inherit the methods. So, if I try to call snakey:changeState('search'), for example, it gives me an error message "Attempt to call method 'changeState' (a nil value)".
I have used the same sequence for creating entities in several games in the past and never had this problem. In fact, if I create the Player in the same way, file and location as the enemies, I receive no error messages.
Here is the code where I create the entities.
local snakey
snakey = Snakey {
platform = platform,
player1 = self.player1,
player2 = self.player2,
stateMachine = StateMachine {
['search'] = function() return SnakeySearchState(snakey) end,
['chasing'] = function() return SnakeyChasingState(snakey) end,
['idle'] = function() return SnakeyIdleState(snakey) end
}
}
-- snakey:changeState('search')
-- snakey.stateMachine:change('search', params)
table.insert(self.entities, snakey)
The two coded out lines are where I noticed the problem. The first line gives and error and the second does work, but is not satisfactory because it is a work-around.
Here is the code for Entity.lua: I don't include details of the functions for brevity, but all are working properly for when player calls them.
Entity = Class{}
function Entity:init(def)
-- position
self.x = def.x
self.y = def.y
self.gravity = 6
-- many more variables
end
function Entity:changeState(state, params)
self.stateMachine:change(state)
end
function Entity:update(dt)
self.stateMachine:update(dt)
end
function Entity:collides(entity)
-- do something
end
function Entity:onDamage()
-- do something
end
function Entity:render()
- renders sprite
end
Player code (in brief)
Player = Class{__includes = Entity}
function Player:init(def)
Entity.init(self, def)
-- more variables
end
function Player:update(dt)
Entity.update(self, dt)
end
function Player:render()
Entity.render(self)
end
And perhaps the trouble spot, one one enemy's script
Snakey = Class{__includes = Entity}
function Snakey:init(def)
Entity.init(self, def)
-- yet more variables
end
function Snakey:update(dt)
Entity.update(self, dt)
-- entity behavior (works fine, so omitted)
end
function Snakey:render()
Entity.render(self)
end
Thank you very much for your help. I'm feeling quite frustrated because this sequence has worked in the past and I would really like to know why it's not calling those Entity methods.
Adding the class library
--Copyright (c) 2010-2013 Matthias Richter
local function include_helper(to, from, seen)
if from == nil then
return to
elseif type(from) ~= 'table' then
return from
elseif seen[from] then
return seen[from]
end
seen[from] = to
for k,v in pairs(from) do
k = include_helper({}, k, seen) -- keys might also be tables
if to[k] == nil then
to[k] = include_helper({}, v, seen)
end
end
return to
end
-- deeply copies `other' into `class'. keys in `other' that are already
-- defined in `class' are omitted
local function include(class, other)
return include_helper(class, other, {})
end
-- returns a deep copy of `other'
local function clone(other)
return setmetatable(include({}, other), getmetatable(other))
end
local function new(class)
-- mixins
class = class or {} -- class can be nil
local inc = class.__includes or {}
if getmetatable(inc) then inc = {inc} end
for _, other in ipairs(inc) do
if type(other) == "string" then
other = _G[other]
end
include(class, other)
end
-- class implementation
class.__index = class
class.init = class.init or class[1] or function() end
class.include = class.include or include
class.clone = class.clone or clone
-- constructor call
return setmetatable(class, {__call = function(c, ...)
local o = setmetatable({}, c)
o:init(...)
return o
end})
end
-- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons).
if class_commons ~= false and not common then
common = {}
function common.class(name, prototype, parent)
return new{__includes = {prototype, parent}}
end
function common.instance(class, ...)
return class(...)
end
end
-- the module
return setmetatable({new = new, include = include, clone = clone},
{__call = function(_,...) return new(...) end})
It turns out that sequence matters. In trying to create the minimum reproducible code I could not reproduce the error. After some searching (and a little frustration), I noticed that in Dependencies.lua I was requiring the enemies BEFORE Entity.lua, but Player.lua was required after. I would have thought this wouldn't matter, since everything was imported into the program on frame 1 and I was creating entities on something like frame 1000, but alas. Anyway, problem solved! Always require parent classes before child classes... Lesson learned. :)
Part 1:
Write function def setPixelToBlack(pixel) to change each pixel's colour to black.
Part2:
Write the function def setPictureToBlack(picture) which blacks out the picture. The function has to call setPixelToBlack from part 1.
I have set up my setPixelToBlack(pixel) code but do not know how to use it within the setPictureToBlack(picture) function!
def setPixelToBlack(pixel):
for p in getPixels(pixel):
value = getRed(p)
setRed(p, value * 0)
value = getGreen(p)
setGreen(p, value * 0)
value = getBlue(p)
setBlue(p, value * 0)
def setPictureToBlack(picture):
for p in getPixels(pixel):
setPixelToBlack(pixel)
> f = pickAFile()
>>p = makePicture(f)
>>>setPictureToBlack(p)
>>>>explore(p), this should black out the image selected!
for p in getPixels(pixel): isn't necessary in setPixelToBlack, as you're not looking at multiple pixels.
You also don't need to get the original value of the pixel if you're just setting it to zero.
I assume the getPixels() function takes in the picture, so change that and setPixelToBlack(p) should get you the desired outcome
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 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 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")