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.
Related
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.
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.
I'm a beginner and I'm encountering a problem with an index of a one-dimensional array of strings (m_nameList in the code) that I'm struggling to understand. I have one method (in a class of a Windows Form Application that is not the MainForm) that returns FALSE if a string of the array at a certain index is empty, and viceversa. It looks like this:
Public Function IsReserved(index As Integer) As Boolean
Dim reserved As Boolean = False
If (Not String.IsNullOrEmpty(m_nameList(index))) Then
reserved = True
Else
reserved = False
End If
Return reserved
End Function
I have also a method on the MainForm that calls for it, and if the first method returns true then the second one displays a dialog box, otherwise it doesn't (lstResults is a listbox that has for items the strings of the array):
Private Function CheckIfSeatIsAlreadyReserved() As Boolean
Dim reserved As Boolean = m_seatMngr.IsReserved(lstResults.SelectedIndex)
If (reserved) Then
Dim msgBox As Integer = MessageBox.Show("The seat is already reserved. Would you like to continue?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If (msgBox = DialogResult.Yes) Then
Return True
Else
Return False
End If
Else
Return True
End If
End Function
The problem I find is: if as in the code of the first method above I don't initialize index to any value, the second method will work perfectly, but I'll get a runtime error if I don't select any item on the listBox (and therefore index = -1). On the other hand, if I initialize index to 0 or to m_nameList.Length, then the program won't have any problems if I don't select any item on the ListBox and it will nicely display a warning message I wrote for that case, BUT the second method will behave in a weird way: it will find ALL the strings of the array full even if I only "write" the first one, or it will find ALL the strings empty if I "write" any string that it's not the first one (by writing I mean that I call for a method that puts my input as the text of the string selected). Any idea on why this happens and how I can fix it? Thank you so much.
I will get to the answer below, follow the logic, lets look at your first method. You can refactor it to
Public Function IsReserved(index As Integer) As Boolean
Return Not String.IsNullOrEmpty(m_nameList(index))
End Function
Everything else is called "noise". But as Fabio pointed, what if index is bad? Lets add useful code
Public Function IsReserved(index As Integer) As Boolean
If index < 0 OrElse index > m_nameList.Length - 1 Then
Throw New ArgumentOutOfRangeException(". . . . .")
End If
Return Not String.IsNullOrEmpty(m_nameList(index))
End Function
If you get this exception, your program has a bug - and you know to fix the consumer of this function.
And now lets get to your message
second method will behave in a weird way: it will find ALL the strings of the array full even if I only "write" the first one, or it will find ALL the strings empty if I "write" any string
Most likely reason why it happens - simply because you create condition when you send same value over and over again. Therefore result is same. And this is most likely a bug
If (reserved) Then
. . . . . .
Else
Return True ' <---- BUG ALLERT
End If
And two more things, your second method can shrink down to 1 line too.
Private Function CheckIfSeatIsAlreadyReserved() As Boolean
Return m_seatMngr.IsReserved(lstResults.SelectedIndex) AndAlso _
DialogResult.Yes = MessageBox.Show("The seat is already reserved. Would you like to continue?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
End Function
And, if you do some sort of reservation, array is not really good idea. How about Dictionary(Of String, Boolean)? Your theater or airplane has bunch of seats and you pre-load your dictionary with those
d.Add("1A", False)
d.Add("1B", False)
d.Add("1C", False)
d.Add("1D", False)
d.Add("1E", False)
d.Add("1F", False)
This is your whole airplane row. Now you can do much more with it
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 ;)
I'm new to F# and trying to dive in first and do a more formal introduction later. I have the following code:
type Person =
{
Id: int
Name: string
}
let GetPeople() =
//seq {
use conn = new SQLiteConnection(connectionString)
use cmd = new SQLiteCommand(sql, conn)
cmd.CommandType <- CommandType.Text
conn.Open()
use reader = cmd.ExecuteReader()
let mutable x = {Id = 1; Name = "Mary"; }
while reader.Read() do
let y = 0
// breakpoint here
x <- {
Id = unbox<int>(reader.["id"])
Name = unbox<string>(reader.["name"])
}
x
//}
let y = GetPeople()
I plan to replace the loop body with a yield statement and clean up the code. But right now I'm just trying to make sure the data access works by debugging the code and looking at the datareader. Currently I'm getting a System.InvalidCastException. When I put a breakpoint at the point indicated by the commented line above, and then type in the immediate windows reader["name"] I get a valid value from the database so I know it's connecting to the db ok. However if I try to put reader["name"] (as opposed to reader.["name"]) in the source file I get "This value is not a function and cannot be applied" message.
Why can I use reader["name"] in the immediate window but not in my fsharp code? How can I use string indexing with the reader?
Update
Following Jack P.'s advice I split out the code into separate lines and now I see where the error occurs:
let id = reader.["id"]
let id_unboxed = unbox id // <--- error on this line
id has the type object {long} according to the debugger.
Jack already answered the question regarding different syntax for indexing in F# and in the immediate window or watches, so I'll skip that.
In my experience, the most common reason for getting System.InvalidCastException when reading data from a database is that the value returned by reader.["xyz"] is actually DbNull.Value instead of an actual string or integer. Casting DbNull.Value to integer or string will fail (because it is a special value), so if you're working with nullable columns, you need to check this explicitly:
let name = reader.["name"]
let name_unboxed : string =
if name = DbNull.Value then null else unbox name
You can make the code nicer by defining the ? operator which allows you to write reader?name to perform the lookup. If you're dealing with nulls you can also use reader?name defaultValue with the following definition:
let (?) (reader:IDataReader) (name:string) (def:'R) : 'R =
let v = reader.[name]
if Object.Equals(v, DBNull.Value) then def
else unbox v
The code then becomes:
let name = reader?name null
let id = reader?id -1
This should also simplify debugging as you can step into the implementation of ? and see what is going on.
You can use reader["name"] in the immediate window because the immediate window uses C# syntax, not F# syntax.
One thing to note: since F# is much more concise than C#, there can be a lot going on within a single line. In other words, setting a breakpoint on the line may not help you narrow down the problem. In those cases, I normally "expand" the expression into multiple let-bindings on multiple lines; doing this makes it easier to step through the expression and find the cause of the problem (at which point, you can just make the change to your original one-liner).
What happens if you pull the item accesses and unbox calls out into their own let-bindings? For example:
while reader.Read() do
let y = 0
// breakpoint here
let id = reader.["id"]
let id_unboxed : int = unbox id
let name = reader.["name"]
let name_unboxed : string = unbox name
x <- { Id = id_unboxed; Name = name_unboxed; }
x