Dynamic variable names?

1
2
3
4
5
6
7
8
local buttons = 3
local buttonImage = { "btn1.png", "btn2.png", btn3.png" }
local buttonTable = display.newGroup()
 
for i=1, buttons do
button = display.newImage(buttonimage[i]) --button[1] = display.newImage()
buttonTable:insert(button)
end

Was searching for the exact same thing (something like this[] in flash), but than I actually forget why to use that, since there are always better workarounds.

Why would you want to do that?
Why not putting those into the group directly and/or into a table?
You could also give those Images other "attributes"
i.e.

1
2
3
4
5
6
for i=1, buttons, 1 do
  local button = display.newImage(...)
  button.name = i
  buttonGroup:insert(button) 
  -- or even table.insert(buttonTable,button)
end

Why not just use a table/array:

1
2
3
4
5
6
7
8
9
local buttons = 3
local buttonImage = { "btn1.png", "btn2.png", "btn3.png" }
local buttonTable = display.newGroup()
local button = {}
 
for i=1, buttons do
    button[i] = display.newImage(buttonimage[i])
    buttonTable:insert(button[i])
end

There are ways to do it if you *really* want to. Though I think the table of buttons is better, I'll post this as an example of how it could be done.

Globals are easy:

1
2
3
4
5
for i=1, 10 do
_G["button"..i] = i
end
 
print( button1 ) -- here the global button1 is equal to 1

@ Yobonja

Is there actually a benefit of doing it that way?
I really haven't come across a good reason to make it - but I love to learn new stuff :)

xxxfanta: The "why" is simply because if you don't know the number of buttons, you can't declare the variables. (I won't know if I need button2 until I know the variable, right?)

Building out a table works too (hence my original code), but it just seemed like a simple, elemental thing to do would be able to make variable names out of variables. Also, none of the table.function calls work in a displayGroup which can complicate things.

robmiracle: Looks like your code is identical to my original sample except:
1. You didn't miss the " I did. ;)
2. You're inserting all of the buttons into button{} and then inserting button{} into buttonTable. Not really sure why?

Yobonja: Well...uh...I've never even heard of _local or __index before, so I can see why it's less desirable.Heck, I think I can finish coding the function before I can figure out exactly what's going on there. But thanks for the demo :-)

@xxxfanta
I haven't found any good reason to do it :) just that it's possible. it's essentially the exact same thing though, you're just adding the variable to a table.

@richard9
He puts it in the table so you can reference button1 by writing button[1].
Also, I've found that you can dump a display group into a table, then call the table function you want on that table.

-Angelo

You're original example wasn't using an array:

button = display.newImage(buttonimage[i])

Each time you iterate through the loop, you keep reusing the variable "button" but it keeps reallocating memory. This is a prime example of a memory leak since you loose access to the previous buttons.

My code keeps unique references:

button[i] = display.newImage(buttonimage[i])

There was something commented out at the end of the line where it looks like you tried to use an array. You also needed to create the array before the loop:

button = {}

Hope that helps.

@richard, have a read of the tutorial here

and to answer your question, as to why the button={} and buttonTable:insert

if you have a little idea about coronaSDK, you will see that buttonTable is declared as display.newGroup(), this is the group that @robmiracle has created to hold the buttons. This is a display object, nothing to do with arrays.

so after creating the button object, he is using the buttonTable:insert(button[i]) to place the display object into the display group. This line can be avoided by altering the code as

6
7
8
for i=1, buttons do
    button[i] = display.newImage(buttonTable, buttonimage[i])
end

@richard9

the example by @robmiracle works, but i think that storing buttons in both a regular table and a display group is overkill - as they're both tables so can both be used for storage.

the example by @yobonja is giving you the ability to do name look up as you asked for, but i don't think it's what you want. as they say, "be careful what you ask for because you might get it." :) one of the issues with this solution is that it requires having a deeper understanding of the Lua language. second issue is that in practice you'd never need to do this nor can i think of a reason why you would want to. :P

i think an important question that you need to ask/answer is: "What are you *doing* with the buttons ?"
What do you want to happen when you click one ? why do you need to have a way to find them after they are created ? Do they need to act as a unit, a la Display Group ?

your answer to that will guide you to determine whether to use a Lua table or Corona Group. also, the key you use to retrieve them from the storage will play a factor as well.

the display group and the regular lua table can both be used as storage - they both have insert/remove/get functionality on them. (though the methods are different). however, i'd say that the one you choose depends on what you have intended for your buttons.
for example, putting display objects into a display group implies that the objects are going to act as a unit. if you change x,y position on the group, you change the location for all contained objects. if you change alpha on the group, you change for all contained objects.
the regular Lua table doesn't do any of that. if you use it, it's really just a raw Lua-based container for things, like your buttons or anything else you want. plus, you have A LOT more flexibility in how to index / lookup your values - see below.

so without knowing more about what you're trying to achieve, i'd say that combining both examples from @robmiracle and @xxxfanta is the way to go. choose either display group or table, taking the above into consideration.

1
2
3
4
5
6
7
8
9
10
11
12
local buttons = 3
 
local buttonTable = display.newGroup()
-- local buttonTable = {} -- use this if just need a container object
 
for i=1, buttons do
        local button = display.newImage( "btn" .. i .. ".png" )
 
        button.name = i
        buttonGroup:insert( button ) 
        -- or even table.insert( buttonTable, button )
end

Welll, first off, truly, thanks to all for responding. I thought it was a simple mechanical question but there has been a ton of variety in these well thought out responses. :)

The original goal of this was simply because my designer brain immediately thought "hey, I can just use .. to make dynamic variables on the fly! This is easy!" (*cough*) Basically, jayantv's link is dead on with my thinking.

As a relative "noob", tables flip between easy and intimidating for me. So to dmccuskey, I say, I know what I need, but sometimes it's hard to glean how a particular approach does that for me without maximum understanding of that approach - understanding that doesn't come without trial-and-error.

Now, as to what I'm *actually* trying to make...is the iPad bottom tab bar. I know there's an existing template, but it's not extensible and I'd pretty much have to go in and re-write half of the thing. For learning reasons I'd rather just make my own - that way I have some sturdy to share here and to use for myself on two radically different projects.

(And really, I like to try and respond to other guys posts too. :P)

edit: I do see how you removed the image table...but not so useful in a function where you'd arguably want to be able to state the needed images ahead of time. :)

@richard9,

yeah, there are some quirky aspects of Lua which can be confusing, and tables is one of them. for example, the fact that Lua uses the same base data structure for different situations and that structure can act a little differently depending on how you use it can cause issues. so, as you say, you will have to learn by trial-and-error, but i think once you're familiar and comfortable with the idiosyncrasies, like jayantv says, "it's a lot of fun". in any event, it seems like you're making great progress with Corona and Lua.

since you're tackling the bottom tab bar, i'll mention that i've done that too and added that code to a re-usable, extensible library i've started to put together. the lib uses object oriented programming exclusively (OOP is also part of the lib) so you can tweak it as much as you like, but you'd have to know about OOP and perhaps a little more about Lua. i have written docs and examples, so if you want to check it out: http://developer.anscamobile.com/code/dmc-corona-library

but i also understand the desire to Build It Yourself and be able to learn from that experience. i'm sure we've all been there. :)

good luck with the project !

cheers,
dmc

"the example by @robmiracle works, but i think that storing buttons in both a regular table and a display group is overkill - as they're both tables so can both be used for storage."

**SCRATCH THAT**

First, the OP created the table. And in this case, the display group is only holding buttons, so you could use it to reference the other buttons.

But I still think that using a specific buttons[] array is clearer and easier to understand whats going on.

I guess there are several ways to skin a cat...

No particular way is better than the other, the one which makes you feel comfortable is the best. Sometimes what happens is that in a given context, some things work best and some don't where as in a different context things might be different.

I guess everyone that pitched in had good reasoning and code samples for the issue at hand, I avoid a lot of advance stuff in my tutorial and code as *I believe* that a beginner needs to understand how things work more importantly than understanding memory leaks at this stage. Once they get beyond the stage of basic syntax and operations, then they can bother about optimisations etc.

Just my two bits

cheers,

?:)

Yeah, I totally understand the memory leaks issue but my brain just can't handle going back to recode everything to avoid require and globals yet. Gotta make something first and then make it reliable after. (Considering how badly I managed to break widget.newTableView...)

wait, what, I can insert display.newImage directly into a group right away? Nice.

Alright, more mindless jabber from me later. But I do appreciate the help - just looking at the examples in this thread has given me a good idea out of how to rope in the other bits.

So, I hate to drag this out but I've sort of run into the exact wall I was afraid of by using tables.

I was hoping to do two things by referring to tables/groups inside of other tables/groups, like:

1
2
3
4
5
6
7
function tabBar() -- some code
return tabGroup -- this is a master group containing buttons and over states.
end
 
myTab = tabBar()
 
myTab.buttonsGroup.button[1]:addEventHandler("touch", press) --theoretically would apply the handler only to button 1.

do you have a group called tabGroup?
that has another group added to it called buttonsGroup?
which has an array added to it called buttons?
and each element in this buttons Array is a display object that can be made visible or invisible.

if you have YES to all the questions, let us look at it further. Please read and check carefully.

cheers,

?:)

Yeah, exactly.

1
2
3
4
5
6
7
8
9
local tabGroup = display.newGroup() -- holds everything
local buttonsGroup = display.newGroup() -- holds all of the buttons
local buttonsOverlay = display.newGroup() -- holds all of the button overlays
local button = {} -- the buttons themselves, basically similar to above or your link.
local buttonOverlay = {}
tabGroup:insert(buttonsGroup)
tabGroup:insert(buttonsOverlay)
buttonsGroup:insert(button)
buttonsOverlay:insert(buttonOverlay)

Hmm... I have been doing a lot of this in corona(dynamic "naming" of object... really just putting them in a table and referancing them by thier table index.)

I thought I would throw together a working sample... it is a little ugly (ok maybe more than a little) but I thought it might inspire someone else....

The following is a "Color Picker". The number of colors displayed is based on the screen resoltion.(varible) and they are changed by referancing the rectangle(color) by it's table index. (in this case it just cycles thru all off the objects but it is possible to change just one if you keep track of the indexes... like in anouther Lua table where the index is something usefull and the value is it's position in the display group.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
Ww,Hh = display.viewableContentWidth, display.viewableContentHeight
BackgroundColor = display.newRect( 0,0,Ww,Hh);BackgroundColor:setFillColor(0,0,255)
 
function cButton(lx,ly)
 local c=1
  while customButton[c] do
   local tT=customButton[c]
   if tT["active"]== "true" and lx > tT["x1"] and lx < tT["x2"] and ly > tT["y1"] and ly < tT["y2"] then
    if tT["command"] then
     return tT["command"]
    else
     return "color",tT["r"],tT["g"],tT["b"]
    end
   end
  c=c+1
  end
 return nil
end
 
local testHandle = function (event)
--      print("---began-----"..event.x.."  "..event.y)
 local lCMD,r,g,b = cButton(event.x,event.y)
   if event.phase  == "began" and lCMD then
      print("---began-----"..event.phase.."--------"..lCMD)
      CMD = lCMD
   elseif event.phase  == "moved" and CMD then
      print("---moved-----"..event.phase.."--------"..CMD)
      if lCMD ~= CMD then
         print("---cancel----"..event.phase.."--------"..CMD)
         CMD = nil
      end
   elseif event.phase  == "ended" and CMD then
         print("---ended-----"..event.phase.."--------"..CMD)
         if lCMD == "color" then
            PickerGroup.alpha=0
            PickerButtonGroup.alpha=1
            local i = 1
            --disable picker buttons
            while i<PickerKey do
              customButton[i]["active"]="false"
                i = i+1
                end
              customButton[PickerKey]["active"]="true"
            BackgroundColor:setFillColor(r,g,b)
         elseif lCMD == "pick" then
             displayPicker()
         elseif lCMD == "more" then
             displayPicker()
         elseif lCMD == "cancel" then
            PickerGroup.alpha=0
            PickerButtonGroup.alpha=1
            local i = 1
            --disable picker buttons
            while i<PickerKey do
              customButton[i]["active"]="false"
                i = i+1
                end
              customButton[PickerKey]["active"]="true"
         end
          CMD = nil
   end
end
function createPicker()
local cgrid =math.floor(Ww/7)
local maxWIDE = 7
local maxHIGH = math.floor((Hh - 100)/cgrid )
local colorKey = 1
customButton = {}
local gW = 3--math.ceil(cgrid/2)--10 --width of lines
PickerGroup = display.newGroup()
local mH,mV = 1,1
      while mH <= maxWIDE do
            local bH=(mH-1)*cgrid
                  while mV <= maxHIGH do
                        local bV=(mV-1)*cgrid
                        local rR,gG,bB = math.random (1,255),math.random (1,255),math.random (1,255)
                        cColor = display.newRect( PickerGroup,bH,bV,cgrid,cgrid);cColor:setFillColor(rR,gG,bB)
                        customButton[colorKey] = {}
                        customButton[colorKey]["x1"] = bH
                        customButton[colorKey]["x2"] = bH+cgrid
                        customButton[colorKey]["y1"] = bV
                        customButton[colorKey]["y2"] = (bV+cgrid)
                        customButton[colorKey]["r"]= rR
                        customButton[colorKey]["g"]= gG
                        customButton[colorKey]["b"]= bB
                        customButton[colorKey]["active"]="false"
                        colorKey = colorKey + 1
                        mV = mV+1
                  end--vert while
            mH = mH+1
            mV = 1
      end--horz while
-- adding command buttons
--cancel
PickerGroup.alpha=0
--Picker button
PickerButtonGroup = display.newGroup()
local Choose = display.newRect(PickerButtonGroup, Ww/4,Hh/4,Ww/2,Hh/2);Choose:setFillColor(255,255,255)
customButton[colorKey] = {}
customButton[colorKey]["x1"] = Ww/4
customButton[colorKey]["x2"] = Ww-(Ww/4)
customButton[colorKey]["y1"] = (Hh/4)
customButton[colorKey]["y2"] = Hh-(Hh/4)
customButton[colorKey]["command"]= "pick"
customButton[colorKey]["active"]="true"
PickerKey = colorKey
colorKey = colorKey + 1
local title = display.newText(PickerButtonGroup,"Pick Color",Ww/3,Hh/3, nil, 20 );title:setTextColor(0,0,0)
local Cancel = display.newRect( PickerGroup,0,Hh-100,Ww/2-2,100);Cancel:setFillColor(255,255,255)
customButton[colorKey] = {}
customButton[colorKey]["x1"] = 0
customButton[colorKey]["x2"] = Ww/2
customButton[colorKey]["y1"] = Hh-100
customButton[colorKey]["y2"] = Hh
customButton[colorKey]["command"]= "cancel"
customButton[colorKey]["active"]="false"
colorKey = colorKey + 1
local More = display.newRect( PickerGroup,Ww/2+2,Hh-100,Ww/2-2,100);More:setFillColor(255,255,255)
customButton[colorKey] = {}
customButton[colorKey]["x1"] = Ww/2
customButton[colorKey]["x2"] = Ww
customButton[colorKey]["y1"] = Hh-100
customButton[colorKey]["y2"] = Hh
customButton[colorKey]["command"]= "more"
customButton[colorKey]["active"]="false"
colorKey = colorKey + 1
local title = display.newText(PickerGroup, "Cancel",Ww/8,Hh-50, nil, 20 );title:setTextColor(0,0,0)
local title = display.newText(PickerGroup, "More",Ww/8*6,Hh-50, nil, 20 );title:setTextColor(0,0,0)
end--func
 
function displayPicker()
  PickerGroup.alpha=1
  PickerButtonGroup.alpha=0
  local i = 1
  --enable picker buttons
    while i<PickerKey do
          local rR,gG,bB = math.random (1,255),math.random (1,255),math.random (1,255)
          PickerGroup[i]:setFillColor(rR,gG,bB)
          customButton[i]["active"]="true"
          customButton[i]["r"]=rR
          customButton[i]["g"]=gG
          customButton[i]["b"]=bB
          i = i+1
    end
  i = i+1
  customButton[i]["active"]="true"
  i = i+1
  customButton[i]["active"]="true"
  PickerButtonGroup.alpha=0
  customButton[PickerKey]["active"]="false"
end
createPicker()
Runtime:addEventListener("touch",testHandle)

That's...pretty complex, Allen. Hopefully somebody can get inspired from it. :)

As to my puzzle, I've found some behavior I can't explain.

1. As mentioned, the function variable myTab returns tabGroup.
2. tabGroup contains three groups, so you could say it's like this: (I didn't declare it this way, but this is the order)
a. tabGroup[1] = buttonsGroup
b. tabGroup[2] = buttonsOverlay
c. tabGroup[3] = buttonsTitle
3. If I add an event listener to "myTab"...

Results:
myTab[1] acts like tabGroup.buttonsGroup.button[1]
myTab[2] acts like tabGroup.buttonsOverlay.overlay[1]
myTab[3] acts like tabGroup.buttonsTitle.title[1]

Each group has 5 objects within, so I must be missing some sort of syntax that is causing it to pick the first one out of the subgroup...

views:1643 update:2011/10/5 8:48:05
corona forums © 2003-2011