displayGroups in CORONA SDK (LUA) - mobile

I created a local group and insert objects in it on the screen as a rectangle and then use myGroup:removeSelf() and myGroup = nil. Automatically memory for the rectangle and all other elements will be emptied too? (next code)
cenarioGrupo = display.newGroup()
local chao = display.newRect( display.contentWidth*0.5, display.contentHeight*0.95, display.contentWidth, display.contentHeight*0.1 )[
cenarioGrupo:insert(chao)
--Then..
cenarioGrupo:removeSelf(); cenarioGrupo = nil;
and other question. How can I use the cenarioGrupo in createScene function, and it is only created in function criarCenario? Returning it? Creating it overall?
local function criarCenario()
cenarioGrupo = display.newGroup()
local chao = display.newRect( display.contentWidth*0.5, display.contentHeight*0.95, display.contentWidth, display.contentHeight*0.1 )
chao:setFillColor(1,1,1)
cenarioGrupo:insert(chao)
end
function scene:createScene( event )
local sceneGroup = self.view
criarCenario()
end

in Corona if you create a display group and add display objects (not native widget of android) to it, when you try to remove display group all of it's children and containments will be erased, too.
for your second question:
you can use sceneGroup as an entry for your criarCenario, like this:
function scene:createScene( event )
local sceneGroup = self.view
criarCenario(sceneGroup)
end
and then in your function just insert your display group to sceneGroup:
local function criarCenario(sceneGroup) -- use an entry
cenarioGrupo = display.newGroup()
local chao = display.newRect( display.contentWidth*0.5, display.contentHeight*0.95, display.contentWidth, display.contentHeight*0.1 )
chao:setFillColor(1,1,1)
cenarioGrupo:insert(chao)
sceneGroup:insert(cenarioGrupo) -- here is the main change
end
you can also do that by returning your cenarioGrupo and insert it to sceneGroup in createScene:
local function criarCenario()
cenarioGrupo = display.newGroup()
local chao = display.newRect( display.contentWidth*0.5, display.contentHeight*0.95, display.contentWidth, display.contentHeight*0.1 )
chao:setFillColor(1,1,1)
cenarioGrupo:insert(chao)
return cenarioGrupo
end
function scene:createScene( event )
local sceneGroup = self.view
sceneGroup:insert( criarCenario() )
end
I myself prefer the second method because it provides you more loose coupling. your criarCenario function is more separated from createScene in the second method.

Related

Lua - Why are C functions returned as userdata?

I'm working on game scripting for my engine and am using a metatable to redirect functions from a table (which stores custom functions and data for players) to a userdata object (which is the main implementation for my Player class) so that users may use self to refer to both.
This is how I do my binding in C# in the Player class:
state.NewTable("Player"); // Create Player wrapper table
state["Player.data"] = this; // Bind Player.data to the Player class
state.NewTable("mt"); // Create temp table for metatable
state.DoString(#"mt.__index = function(self,key)
local k = self.data[key]
if key == 'data' or not k then
return rawget(self, key)
elseif type(k) ~= 'function' then
print(type(k))
print(k)
return k
else
return function(...)
if self == ... then
return k(self.data, select(2,...))
else
return k(...)
end
end
end
end");
state.DoString("setmetatable(Player, mt)"); // Change Player's metatable
For my Player class, I implement a method, bool IsCommandActive(string name). When I need to call this method using self, it needs to use the userdata object, rather than the table, otherwise I get the following error:
NLua.Exceptions.LuaScriptException: 'instance method 'IsCommandActive'
requires a non null target object'
For obvious reasons. This is because self refers to the table, not the userdata. So I implemented a metatable so that it may use self to refer to either. The implementation is taken from here, but here is my particular variant (my userdata is stored in an index called data:
mt.__index = function(self,key)
local k = self.data[key]
if key == 'data' or not k then
return rawget(self, key)
elseif type(k) ~= 'function' then
print(type(k))
print(k)
return k
else
return function(...)
if self == ... then
return k(self.data, select(2,...))
else
return k(...)
end
end
end
end
end
Which I follow by using setmetatable, obviously.
Now to the meat of my question. Notice how I print type(k) and print(k) under the elseif. This is because I noticed that I was still getting the same error, so I wanted to do some debugging. When doing so, I got the following output (which I believe is for IsCommandActive):
userdata: 0BD47190
Shouldn't it be printing 'function'? Why is it printing 'userdata: 0BD47190'? Finally, if that is indeed the case, how can I detect if the value is a C function so I may do the proper redirection?
any functions or objects that are part of a C class are userdata`
This is not true. Function is a function, no matter if it's native or written in Lua. Checking the type of a native function would print "function".
It's just it can be that your binding solution is using userdata with __call metamethod set on it, to expose a marshaller with some state/context associated with it. But it doesn't mean that every native function is a userdata, or that every binding lib will be implemented same way. It could be done effectively the same using Lua table instead of userdata. Would you be telling then that "every native function is a table"? :)
After lots of reading about metatables, I managed to solve my problem.
To answer the question in the title, it's apparently what NLua just decides to do and is implementation-specific. In any other bindings, it may very well return as function, but such is apparently not the case for NLua.
As for how I managed to accomplish what I wanted, I had to define the metatable __index and __newindex functions:
state.NewTable("Player");
state["Player.data"] = this;
state.NewTable("mt");
state.DoString(#"mt.__index = function(self,key)
local k = self.data[key]
local metatable = getmetatable(k)
if key == 'data' or not k then
return rawget(self, key)
elseif type(k) ~= 'function' and (metatable == nil or metatable.__call == nil) then
return k
else
return function(...)
if self == ... then
return k(self.data, select(2,...))
else
return k(...)
end
end
end
end");
state.DoString(#"mt.__newindex = function(self, key, value)
local c = rawget(self, key, value)
if not c then
local dataHasKey = self.data[key] ~= key
if not dataHasKey then
rawset(self, key, value)
else
self.data[key] = value
end
else
rawset(self, key, value)
end
end");
state.DoString("setmetatable(Player, mt)");
What __index does is override how tables are indexed. In this implementation, if key is not found in the Player wrapper table, then it goes and tries to retrieve it from the userdata in Player.data. If it doesn't exist there, then Lua just does its thing and returns nil.
And just like that, I could retrieve fields from the userdata! I quickly began to notice, however, that if I set, for instance, self.Pos in Lua, then the Player.Pos would not update in the backing C# code. Just as quickly, I realized that this was because Pos was generating a miss in the Player wrapper table, which meant that it was creating a new Pos field for the table since it actually did not exist!
This was not the intended behavior, so I had to override __newindex as well. In this particular implementation, it checks if the Player.data (userdata) has the key, and if so, sets the data for that particular key. If it does not exist in the userdata, then it should create it for the Player wrapper table because it should be part of the user's custom Player implementation.

Return array of objects from function - VBA

I need to "return" an Array of Objects from a function that I created using VBA. When I try to set the function as the array it gives me an error message, saying
Object is required.
I am not very used to VBA, and I can't fix this. Here is the function code:
Function sortedList(listRange As Integer, tempList() As ship) As ship
Dim temp As ship
Set temp = Nothing
For i = listRange - 10 To 1 Step -1
For j = 2 To listRange - 10
If tempList(j - 1).Arrival > tempList(j).Arrival Then
Set temp = tempList(j - 1)
Set tempList(j - 1) = tempList(j)
Set tempList(j) = temp
End If
Next j
Next i
'return tempList - how?
Set sortedList = tempList
End Function
Ship is a "class" that I created. tempList is the array of objects from class ship that I need to return from the function sortedList.
The function works, it's just the return part that I can't make work.
Thanks for the help. If more information is necessary let me know!
Declare the function to return an array
Function sortedList(listRange As Integer, tempList() As ship) As ship()
and then assign the result without Set
sortedList = tempList

Values printing in pairs issue? Lua

For some reason the countries that seem to be returned are all returning in pairs? How can you change the code so it only returns the countries in 'Europe' once?
function newcountry(continent,country)
local object = {}
object.continent = continent
object.country = country
local list = {}
for i in pairs( object ) do
if object.continent == "Europe" then
table.insert(list, object.country)
print(object.country)
end
end
return object
end
a = newcountry("Africa","Algeria")
b = newcountry("Europe","England")
c = newcountry("Europe","France")
d = newcountry("Europe","Spain")
e = newcountry("Asia","China")
I'm not sure what you are trying to accomplish with this code, but to answer your question:
function newcountry(continent,country)
local object = {}
object.continent = continent
object.country = country
local list = {}
if object.continent == "Europe" then
table.insert(list, object.country)
print(object.country)
end
return object
end
This code will print countries in Europe just once. When there was loop in there, it printed name of the country twice, because it did it for each element of object table (continent and country, hence two times).
Generic for loops in Programming in Lua (first edition).
I would also like to point out that list is quite useless at the moment. It is not being returned and stays local. On top of that, every time you call newcountry there is new list created. They are all unique - country objects are not added to single list. But again - I don't know what you are trying to accomplish.

Gideros event showing as null after being triggered

I have got Dispatched events in my gideros game. What im not certain of, is that when i try access the event (object triggered) it returns a table that has a length of 0.
I have a letter class as below:
GridLetter = gideros.class(Sprite)
function GridLetter:init(letter)
self.image = Bitmap.new(Texture.new("GridLetters/"..letter.."Grid.png"))
self.image:setAnchorPoint(0.5, 0.5)
self.focus = false
self:updateVisualState(true)
self:addEventListener(Event.MOUSE_DOWN, self.onMouseDown, self)
end
function GridLetter:onMouseDown(event)
if self:hitTestPoint(event.x, event.y) then
self.focus = true
self:dispatchEvent(Event.new("GridLetterDown"))
event:stopPropagation()
end
end
function GridLetter:updateVisualState(state)
if state then
self:addChild(self.image)
end
end
The code that implements this class is below:
grid = Core.class(Sprite)
local letterArr = {"a","b","c","d","e","f","g","h","u","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"}
local gridboard = {{},{},{},{}}
local letterBoxArr = {{},{},{},{}}
local letterBox
function grid:init(params)
local widthOfSingle = (application:getContentWidth() / 4)
for rowCount = 1,4 do
for colCount = 1,4 do
rand = math.random(1, 26)
gridboard[rowCount][colCount] = letterArr[rand]
end
end
for rowCount = 1,4 do
for colCount = 1,4 do
letterBox = GridLetter.new(gridboard[rowCount][colCount])
letterBoxArr[rowCount][colCount] = {letterBoxItem=letterBox}
letterBoxArr[rowCount][colCount].letterBoxItem:setPosition(((widthOfSingle * colCount) - (widthOfSingle / 2)), 100 * rowCount)
letterBoxArr[rowCount][colCount].letterBoxItem.letter = gridboard[rowCount][colCount];
letterBoxArr[rowCount][colCount].letterBoxItem:addEventListener("GridLetterDown", LetterDown, self)
self:addChild(letterBoxArr[rowCount][colCount].letterBoxItem)
end
end
end
function LetterDown(event)
print(event.target.letter)
end
When i click on one of these image, the event is fired and code does run under the LetterDown event, but when trying to access the event parameter, it returns:
grid.lua:32: attempt to index field 'target' (a nil value)
stack traceback:
grid.lua:32: in function <grid.lua:31>
GridLetter.lua:15: in function <GridLetter.lua:12>
any idea or workarounds?
When replacing:
print(event.target.letter)
to
print(event.x)
It prints nil.
I appreciate your help in advance.
Regards,
Warren
This isn't an answer (I'd comment, but that strangely requires rep).
The error you're getting is because in event, there's nothing in the target field.
You could find out what's in the table by looping over it (or by using a pretty table print function, if glideros defines one/you define one yourself):
for k, v in pairs(target) do print(k, v) end
If you're not sure if target will exist all the time, you can also do something like this:
print(not event.target and "event.target does not exist" or event.target.letter)
You can read about the A and B or C construct here: http://lua-users.org/wiki/TernaryOperator
Edit: Assuming that the event you receive fires with a GridLetter object, you are not setting self.letter to anything. Perhaps setting the field will help:
function GridLetter:init(letter)
self.letter = letter -- Forgot this?
self.image = Bitmap.new(Texture.new("GridLetters/"..letter.."Grid.png"))
self.image:setAnchorPoint(0.5, 0.5)
self.focus = false
self:updateVisualState(true)
self:addEventListener(Event.MOUSE_DOWN, self.onMouseDown, self)
end
I have got it.. Someone on gideros forums helped me out, thanks to Advert as well.
So in my onMouseDown event i was assigning the .letter on the general event. So creating a local var and assigning it the event before dispatching it, the variable then keeps my assigned letter.
Hope this helps others in future!
function GridLetter:onMouseDown(event)
if self:hitTestPoint(event.x, event.y) then
self.focus = true
local e = Event.new("GridLetterDown")
e.letter = self.letter
self:dispatchEvent(e)
event:stopPropagation()
end
end
Thanks for all responses.

Stopping physics body from movement in Corona SDK

I'm learning Corona SDK and I'm making small project in that purpose.
So, my problem is next one:
I managed to create 2 physics objects and make one of them "explode" when it collides with the other one. My question is how to make other object (it has linear impulse applied) stop when it collides? Also, when it stops, it has to be removed from the screen to avoid colliding with other objects...
Here is a part with removing first object on collision:
nloDrop = function()
local nlo = display.newImageRect("nlo.png", 65, 25)
nlo.x = 35 + mRand(410) ; nlo.y = -60
physics.addBody(nlo, "dynamic", {density=1, bounce = 0, friction = 0, filter = {maskBits = 4, categoryBits = 2}})
nlo:applyLinearImpulse(0, 0.8, nlo.x, nlo.y)
nlo.isSensor = true
nlo.collision = nloCollision
nlo:addEventListener("collision", nlo)
nlo.name = "nlo"
toFront()
end
And here is 'collision' function:
function nloCollision(self, event)
if ((event.other.myName == "weaponName") then
print("funkc")
self:removeSelf()
self:removeEventListener("collision", nlo)
self = nil
if weapon ~= nil then
-- stop moving of weapon
end
end
end
Thanks!
You can make the object bodyActive false and then it will not respond to physics. You cant remove a body from physics within the active screen so its a better option to keep that object out of the screen.
I made it setting the object like local variable and making a function that deletes/removes each variable (object) after some interaction or collision.
1st function contains object creating (that is a local type under function) and applying physics to that object.
2nd function contains deleting (self:removeSelf()) that works because each object is object for itself and when deleting it, physics stuff will continue to work because new local object is going to be created.
function create(event)
local weap1 = display.newImage("weap1.png", 0, 0)
weap1.x = turret.x ; weap1.y = turret.y
weap1.rotation = turret.rotation
weap1.collision = weap1Collision
weap1:addEventListener("collision", weap1)
physics.addBody(weap1, "dynamic", {density = 1, friction = 0, bounce = 0, filter = {maskBits = 2, categoryBits = 4}})
weap1:applyLinearImpulse(forceWeap1*xComp, forceWeap1*yComp, weap1.x, weap1.y)
function weap1Collision(self,event)
if (event.other.name == "collisionObject") then
self:removeSelf()
self:removeEventListener("collision", weap1)
self = nil
end
end
local type of variable (object) makes it work.
P.S.: vanshika, thanks for your answer, it's useful ;)

Resources