Lua __index method not executing on read - lua

I'm trying to have my __index method fire anytime a key is read from this table, but it is apparently not firing since A) the contents read are never modified according to its intended math and B) its print statement never fires. What am I missing, please?
local scalar = 2
local scalar_template = {
__index = function(t, k)
print('hi?') --never fires
return rawget(t,k) * scalar
end
}
local m = {}
m.scalars = setmetatable({width = 2, height = 1, x = 10, y = 10}, scalar_template)
print(m.scalars.width) --2 rather than (2 * ui_scalar = 4)
scalar = 2.5
print(m.scalars.width) -- 2 rather than (2 * ui_scalar = 5)

You seem to have made a pretty common mistake, __index is only called if the key being used does NOT have a value in the table you are indexing with it.
You need to make use a blank table in the setmetatable call and reference the actually table in the meta function. You often see this with "protected" or "read only" tables.
local scalar = 2
local protected = {width = 2, height = 1, x = 10, y = 10}
local scalar_template = {
__index = function(_, k)
print('hi?')
return protected[k] * scalar
end
}
local m = {}
m.scalars = setmetatable({}, scalar_template)
print(m.scalars.width) --2 rather than (2 * ui_scalar = 4)
scalar = 2.5
print(m.scalars.width) -- 2 rather than (2 * ui_scalar = 5)

Related

I don't Understand "How Lua save data on the same variable when we Iterate over it"

I know Lua remember value in the same field/function with the Same defined Variable. but I still lack the concept behind it. Let's say I'see Int Variable = 2 which can be modify if we iterate over it Int variable = 8
Same logic I was applying on the Code. I got the results, but I didn't understand the concept behind it.
You Can see in my Code I was saving some value in Var x and saveX
and I was doing saveX = x from my logic The value of x should be saved inside saveX instead it was saving the loop value as I'm applying over it. Why is that?
-- Graphical Representation of table1
--[[ { { {},{},{},{} },
{ {},{},{},{} },
{ {},{},{},{} },
{ {},{},{},{} } } ]]
_Gtable1 = {}
for y = 1, 4 do
table.insert(_Gtable1, {})
for x = 1, 4 do
table.insert(_Gtable1[y], {
x = (x - 1) * 10, --Coordinate value of X
y = (y - 1) * 10, --Coordinate value of Y
-- what if i want to save Coordinate value on another variable How should i do?
saveX = x, -- Saving Loop X
saveY = y, -- Saving Loo Y
t = "Check" -- to Debug
})
end
end
ti = _Gtable1
for y = 1, 4 do
for x = 1, 4 do
tim = ti[y][x]
-- print(tim.saveX) -- output 1, 2, 3, 4
print(tim.x) -- output 0, 10, 20, 30
end
end
I hope I understood the question:
Let's simplify the example:
local t = {
x = 10,
y = x,
}
You assume t.y is 10, because you declared it a line before that? But it is nil here, because x = 10, is no variable. It's part of the table constructor and can not be referenced in the same constructor. Check out https://www.lua.org/pil/3.6.html for more details.
After the constructor has finished, you can access that as t.x (not x!).
Now for your example, you need to move (x - 1) * 10 outside the constructor, e.g.: local tempX = (x - 1) * 10. Then use tempX inside the constructor. Or you can set your values individually, e.g. _Gtable1[y][x].saveX = _Gtable1[y][x].x. I would prefer the former.

Making a cubic ease-in-out curve dynamically switch its goal position

Using, Lua, I've made a function to cubically (easing in/"accelerating", then easing out/"decelerating" afterwards) interpolate from one number to another; SimpleSpline takes a number between 0-1 (the time the animation's been going, easiest put), and SimpleSplineBetween does the same, but keeps it in between two given minimum/maximum values.
function SimpleSpline( v )
local vSquared = v*v
return (3 * vSquared - 2 * vSquared * v)
end
function SimpleSplineBetween( mins, maxs, v )
local fraction = SimpleSpline( v )
return (maxs * fraction + mins * (1 - fraction))
end
It all works fine. However, I've run into a bit of an issue. I'd like this to be a bit more dynamic. For example, assume my "mins" is 0.5, and my "maxs" is 1, then I have a variable for time that I pass as V; we'll say it's 0.5, so our current interpolation value is 0.75. Now, let's also assume that suddenly, "maxs" is jerked up to 0.25, so now, we have a new goal to reach.
My current approach to handling situations like the above is to reset our "time" variable and change "mins" to our current value; in the above case 0.75, etc. However, this produces a very noticable "stop" or "freeze" in the animation, because it is being entirely reset.
My question is, how can I make this dynamic without that stop? I'd like it to transition smoothly from moving to one goal number to another.
local Start_New_Spline, Calculate_Point_on_Spline, Recalculate_Old_Spline
do
local current_spline_params
local function Start_New_Spline(froms, tos)
current_spline_params = {d=0, froms=froms, h=tos-froms, last_v=0}
end
local function Calculate_Point_on_Spline(v) -- v = 0...1
v = v < 0 and 0 or v > 1 and 1 or v
local d = current_spline_params.d
local h = current_spline_params.h
local froms = current_spline_params.froms
current_spline_params.last_v = v
return (((d-2*h)*v+3*h-2*d)*v+d)*v+froms
end
local function Recalculate_Old_Spline(new_tos)
local d = current_spline_params.d
local v = current_spline_params.last_v
local h = current_spline_params.h
local froms = current_spline_params.froms
froms = (((d-2*h)*v+3*h-2*d)*v+d)*v+froms
d = ((3*d-6*h)*v+6*h-4*d)*v+d
current_spline_params = {d=d, froms=froms, h=new_tos-froms, last_v=0}
end
end
Usage example according to your values:
Start_New_Spline(0.5, 1) -- "mins" is 0.5, "maxs" is 1
local inside_spline = true
while inside_spline do
local goal_has_changed = false
for time = 0, 1, 0.015625 do -- time = 0...1
-- It's time to draw next frame
goal_has_changed = set to true when goal is changed
if goal_has_changed then
-- time == 0.5 -> s == 0.75, suddenly "maxs" is jerked up to 0.25
Recalculate_Old_Spline(0.25) -- 0.25 is the new goal
-- after recalculation, "time" must be started again from zero
break -- exiting this loop
end
local s = Calculate_Point_on_Spline(time) -- s = mins...maxs
Draw_something_at_position(s)
wait()
end
if not goal_has_changed then
inside_spline = false
end
end

Why is my lua table object empty?

I create a lua table object called Map in my Map module, and this function creates a new instance:
function Map:new (o)
o = o or {
centers = {},
corners = {},
edges = {}
}
setmetatable(o, self)
self.__index = self
return o
end
and in my island module I put in this code in the first few lines:
local map = require (*map module location*)
Island = map:new ()
and when I print the number of centers, corners, and tables, they all come out to 0.
I have separate modules for Corner:new (), Center:new (), and Edge:new ()
Why does the length of centers, corners, and edges output as 0?
Edit:
This is what I input into the centers table for example(corners and edges is similar)
function pointToKey(point)
return point.x.."_"..point.y
end
function Map:generateCenters(centers)
local N = math.sqrt(self.SIZE)
for xx = 1, N do
for yy = 1, N do
local cntr = Center:new()
cntr.point = {x = 0.5+xx - 1, y = 0.5+yy - 1}
centers[pointToKey(cntr.point)] = cntr
end
end
return centers
end
The size is always a perfect square
This seems to be a problem with variable scope. Firstly, in instantiating a new Map, the o that is returned should be local:
function Map:new (o)
local o = o or { -- this should be local
centers = {},
corners = {},
edges = {}
}
setmetatable(o, self)
self.__index = self
return o
end
When you pass a pointer to a table to Map:generateCenters(), there is no need to return that pointer. The centers have been added to that table:
function Map:generateCenters(centers)
local N = math.sqrt(self.SIZE)
for xx = 1, N do
for yy = 1, N do
local cntr = Center:new()
cntr.point = {x = 0.5+xx - 1, y = 0.5+yy - 1}
centers[pointToKey(cntr.point)] = cntr -- HERE you're adding to the table passed as an argument
end
end
-- return centers --> NO NEED TO RETURN THIS
end
Lastly, you would do:
local map = require( "map" )
local island = map:new()
map:generateCenters( island.centers )
You are saying, "Put the centers into the table pointed to by the table value corresponding to the key called centers in the table called island".
Lastly, note that
local t = island.centers
print( #t )
will still not output the number of elements in the table centers because there are gaps keys (i.e. they don't go {0,1,2,3,4,..} but rather whatever string the pointToKey() function returns ). To count the elements in centers, you could do:
local count = 0
for k,v in pairs( island.centers ) do
count = count + 1
end
print( count )

Why won't __add work?

So I am trying to learn about metatables in lua, so i decided to follow some tutorials. I was trying out the __add part of metatables. But for some reason i kept on getting an error (attempt to perform arithmetic on field (nil)
aTable = {}
--Assign the values for the normal table
for x = 1, 10 do
aTable[x] = x
end
-- metatable
mt = {__add = function(table1, table2)
sumTable = {}
for i = 0, #table1 do
sumTable[i] = table1[i] + table2[i]
end
return sumTable
end}
setmetatable(aTable, mt)
newTable = {}
newTable = aTable + aTable
for x = 1, #newTable do
print(newTable[x])
end
At this point i am confused.Help would be appreciated
In the __add-function it should be:
for i = 1, #table1 do
since you didn't set table[0] initially, but started at index 1 (which is indeed recommended for lua-pseudoarrays, many operations rely on it)
#Ctx is correct that the problem is that differing indices in the array initialization and adding functions. But the best way to fix it is to modify your __add function to handle 'holes' in the arrays passed, by checking for nil entries in them.
for i = 0, #table1 do
if (table1[i] and table2[i]) then
sumTable[i] = table1[i] + table2[i]
end
end
Another thing that's missing: You don't set the same metatable on the result, which means that while things like aTable+aTable, aTable+aTable+aTable etc. will work, aTable+aTable+(aTable+aTable) will fail.
Corrected and cleaned version:
-- metatable
mt = {
__add = function( table1, table2 )
sumTable = {}
for i = 1, #table1 do
sumTable[i] = table1[i] + table2[i]
end
return setmetatable( sumTable, mt )
end,
}
aTable = setmetatable( {}, mt )
--Assign the values for the normal table
for x = 1, 10 do aTable[x] = x end
newTable = aTable + aTable
for x = 1, #newTable do print( newTable[x] ) end
-- and a test for what would have failed:
yetAnotherTable = newTable + newTable
for x = 1, #yetAnotherTable do print( yetAnotherTable[x] ) end

Generate and manipulate a table of "complex" objects in CoronaSDK

I'm trying to generate 40 circles with text and I want to be able to use them and handle them later.
According to several post I found here and on corona forums, I'm doing it well but, in all the examples, is only with one "display" object... not with several so I'm not sure if it's possible or maybe it needs something different.
After some introduction... here the code:
function _.spawn(params)
local orb = display.newCircle( display.contentWidth/2, display.contentHeight/2, 40 ) -- creates the orb shape
orb.orbTable = params.orbTable --assign the internal table
orb.index = #orb.orbTable + 1 -- takes control of the index
orb.orbTable[orb.index] = orb -- assign a orb to the internal table
orb:setFillColor( unpack(params.color) ) -- change the color according to the rules above
orb.x = params.x -- control the X position of the orb
orb.y = params.y --control the Y position of the orb
orb.alpha = 1 -- borns with alpha = 0
orb.value = params.value -- assign the internal value of the orb (1-10)
--local orbText = display.newText( orb.value, orb.x+2, orb.y-5, "Aniron", 40 ) -- generates the value on the ball
--orbText.orbTable = params.orbTable
--orbText.index = #orbText.orbTable + 1
--orbText.orbTable[orbText.index] = orbText
--orbText:setFillColor( 0,0,0 )
--orbText.alpha = 1
--The objects group
orb.group = params.group or nil
--If the function call has a parameter named group then insert it into the specified group
orb.group:insert(orb)
--orb.group:insert(orbText)
--Insert the object into the table at the specified index
orb.orbTable[orb.index] = orb
return orb -- returns the orb to have control of it
end
The function to generate all the objects I need is:
function _.generateDeck()
for i = 1, 40 do -- loop to generate the 40 orbs
internalValue = internalValue + 1 -- counter to assigns the right values
if (internalValue > 10) then -- if the internal value is more than 10 starts again (only 1-10)
internalValue = 1
end
if (i >= 1 and i <= 10) then -- assign RED color to the first 10 orbs
completeDeck[i] = _.spawn({color = {196/255,138/255,105/255}, group = orbsGroup, x = 100, y = display.contentHeight / 2, value = internalValue, orbTable = completeDeck})
elseif (i >= 11 and i <= 20) then -- assigns YELLOW color to the next 10 orbs
completeDeck[i] = _.spawn({color = {229/255,214/255,142/255}, group = orbsGroup, x = 100, y = display.contentHeight / 2, value = internalValue, orbTable = completeDeck})
elseif (i >= 11 and i <= 30) then -- assigns BLUE color to the next 10 orbs
completeDeck[i] = _.spawn({color = {157/255,195/255,212/255}, group = orbsGroup, x = 100, y = display.contentHeight / 2, value = internalValue, orbTable = completeDeck})
elseif (i >= 11 and i <= 40) then -- assigns GREEN color to the next 10 balls
completeDeck[i] = _.spawn({color = {175/255,181/255,68/255}, group = orbsGroup, x = 100, y = display.contentHeight / 2, value = internalValue, orbTable = completeDeck})
end
end
end
After calling the function, it generates the circles correctly and I can read the value:
for i = 1, #completeDeck do
print("Complete Deck Value ("..i.."): "..completeDeck[i].value)
end
But if I uncomment the lines regarding the newText (orbText) then I'm not able to read the values... (value is a nil value)
I guess I need to create a displayGroup with the objects I want inside (circle and text), then "spawn" it, modifying the values? In that case... how can I refer to the object inside the displayGroup?
I think I'm mixing different concepts.
In Lua, objects are tables and to create an object composed of existing objects, we create a table with and add the objects to it as values. The code below creates an objects with orb and orbText which can be accessed by object.orb or object.orbText
function _.spawn(params)
local orb = display.newCircle(display.contentWidth/2, display.contentHeight/2, 40) -- creates the orb shape
orb:setFillColor(unpack(params.color))
orb.x = params.x
orb.y = params.y
orb.alpha = 1
local orbText = display.newText(param.value, param.x + 2, param.y - 5, "Aniron", 40)
orbText:setFillColor(0, 0, 0)
orbText.alpha = 1
-- create an object with orb and orbText as values
local object = {orb = orb, orbText = orbText}
-- add more values to the object
object.value = params.value
object.index = #params.orbTable + 1
params.orbTable[object.index] = object
return object
end
Now to use the object:
for i, object in ipairs(completeDeck) do
print(object.value)
end

Resources