How to structure a display object comprised of multiple graphical elements

I was wondering if someone could show me the best method for making my individual tiles touchable “selectable” so that I can then create ways for the user to make changes to the specific tile.

The player will have options specific to the tiletype, and if they control the tile they touch.

I’ve read a bit about the takefocus() but the only example I’ve found about a touchable display object inside a scrollview was for a button widget, and not a more generic display object such as my tiles.

[import]uid: 170004 topic_id: 34899 reply_id: 139167[/import]

okay, so I have gotten a start on this, and have gotten it to create a grid of tiles in the scrollview using data from my map file i created, but I’m running into some issues.

my problem is that the structure icons assigned to the tiles are not lined up with my tiles correctly. They seem to be about about 50 pixels off both x and y

here is my code

 -- Create a new ScrollView widget:  
 local scrollView = widget.newScrollView{  
 width = 420,  
 height = 320,  
 scrollWidth = 1000,  
 scrollHeight = 1000,  
 friction = .75,  
 maskFile="mask-420x320.png",  
 hideBackground = true,  
 hideScrollBar = true,  
 listener = scrollViewListener  
 }  
  
 -- populate tiletype  
 local tileType = {}  
 tileType[1] = "blank.png"  
 tileType[2] = "agriculture.png"  
 tileType[3] = "farm.png"  
 tileType[4] = "industry.png"  
 tileType[5] = "factory.png"  
 tileType[6] = "energy.png"  
 tileType[7] = "powerplant.png"  
 tileType[8] = "commerce.png"  
 tileType[9] = "bank.png"  
 tileType[10] = "lab.png"  
 tileType[11] = "market.png"  
 tileType[12] = "barrack.png"  
 tileType[13] = "tavern.png"  
 tileType[14] = "monument.png"  
 tileType[15] = "kapital.png"  
  
 -- populate playerColor  
 local playerColor = {}  
 playerColor[1] = {255,255,255}  
 playerColor[2] = {157,173,249}  
 playerColor[3] = {255,115,115}  
 playerColor[4] = {122,217,114}  
 playerColor[5] = {252,254,117}  
  
 local typeId = 0  
 local defense = 0  
  
 -- populate scrollview  
 local grid = {}  
 for row = 1, 10 do -- 10 rows  
 grid[row] = {}  
 for col = 1, 10 do -- 10 columns  
 grid[row][col] = display.newGroup()  
  
 -- Create tile and insert it into grid  
 grid[row][col].tile = display.newRoundedRect(0,0,95,95,7)  
 grid[row][col].tile:setReferencePoint(display.TopLeftReferencePoint)  
 grid[row][col]:insert(grid[row][col].tile)  
  
 if mapTable[row][col][1] == 0 then  
 grid[row][col].tile.isVisible = false  
 end  
  
 -- Fill tile with correct player color  
 grid[row][col].tile:setFillColor( unpack( playerColor[mapTable[row][col][2] ] ) )  
 -- "unpack()" converts {x,y,z} into x,y,z  
  
 -- Paint a stoke line  
 grid[row][col].tile:setStrokeColor(140, 140, 140)  
 grid[row][col].tile.strokeWidth = 2  
  
 -- Insert Defense value into grid array  
 grid[row][col].defense = mapTable[row][col][3]  
  
 -- Insert Shield icon into grid array and hide it  
 grid[row][col].shield = display.newImageRect("shield.png", 95, 95)  
 grid[row][col].shield:setReferencePoint(display.TopLeftReferencePoint)  
 grid[row][col]:insert(grid[row][col].shield)  
 if mapTable[row][col][3] \<= 0 then  
 grid[row][col].shield.isVisible = false  
 end  
  
 grid[row][col].shield.x = grid[row][col].tile.x  
 grid[row][col].shield.y = grid[row][col].tile.y  
  
 -- Insert defenseText and hide it  
 defense = mapTable[row][col][3]  
 grid[row][col].defenseText = display.newText(defense, 12, 8, "Helvetica", 24)  
 grid[row][col]:insert(grid[row][col].defenseText)  
 if mapTable[row][col][3] \<= 0 then  
 grid[row][col].defenseText.isVisible = false   
 end  
  
 -- Insert structure icons into grid array  
 typeId = mapTable[row][col][1]  
 if typeId \>= 1 then  
 grid[row][col].structure = display.newImageRect( tileType[typeId], 95, 95 )  
 grid[row][col].structure:setReferencePoint(display.TopLeftReferencePoint)  
 grid[row][col]:insert(grid[row][col].structure)  
  
 grid[row][col].structure.x = grid[row][col].tile.x  
 grid[row][col].structure.y = grid[row][col].tile.y  
 end   
 -- Position tile  
 grid[row][col].x = (col - 1) \* 100  
 grid[row][col].y = (row - 1) \* 100   
  
 -- Print confirmation  
 print("Y = " .. row .. " X = " .. col .. " and Tile Type = " .. typeId)  
  
  
 -- Insert grid into scrollView  
 scrollView:insert( grid[row][col] )  

I edited this code to reflect changes I’ve made, and put it inside the correct tags [import]uid: 170004 topic_id: 34899 reply_id: 138899[/import]

The basic gist is that scrollView is swallows certain parts of the touch listener in order to function (namely, the “ended” phase). You use :takeFocus() to temporarily snatch the event away. Usually, the code looks a little bit like this:

[code]function tile.touch(self, event)
if event.phase == “began” then
self:takeFocus( event ) – now the only thing the player can influence is the tile
self.isFocus = true
– This next line ensures you had focus before you do anything.
– Otherwise it’s possible to trigger this line by touching something, dragging your finger over the
– button, and then lifting your finger
elseif event.phase == “ended” and self.isFocus then
display.getCurrentStage():setFocus(nil) –
self.isFocus = false
doSomething() – whatever action you want to happen
end
return true
end

tile:addEventListener(“touch”)[/code]

Now as I said, I think scrollView swallows “ended” but this is still a good general approach to ensure that only the thing you touched in the first place is affected.
[import]uid: 41884 topic_id: 34899 reply_id: 139181[/import]

Hi Richard
For some reason I couldn’t get your setup to work for me.

I finally got this working

-- function to handle touch event tile selections  
 local function onTileTap( event )  
 local s = event.target -- reference to onTileTouch object  
  
 -- r and c are references I added to tile so that I could have a direct way to access mapTable with   
 print( "Tile taped located at row " .. s.r .. " and col " .. s.c)  
 event.target:setStrokeColor(250, 250, 250)  
-- TouchListener  
 grid[row][col].tile:addEventListener("tap", onTileTap)  

That gives me info about the target that was tapped and changes the stroke color as an indicator of selection. Scrolling is working great, too.

Trouble is, I need to change the strokecolor back when a new tile is tapped.
If I add a .isSelected to grid[row][col].tile, is there any way for me access and loop through the display objects in search of one with .isSelected with a value of true so that I can then make it false and set the stoke color to normal before applying the selection change to the new selection?
[import]uid: 170004 topic_id: 34899 reply_id: 139209[/import]

Tap is a really troublesome beast to use because it only detects a single state (“did you press?”). You could convert the touch code to work mostly the same as what you have there.

But yeah, you absolutely can search through. It gets slow if you have too many tiles but from what I vaguely remember of your scope it’s probably not a big deal.

I would write a search function and then call it from within the tap listener.

[code]local function searchforActive()
for r = 1, #grid do
for c = 1, grid[r].numChildren do – # doesn’t work for displayGroups
if grid[r][c].tile.isSelected then
grid[r][c].tile.isSelected = false
– change stroke color here
end
end
end
end

– So then in your touch code
searchforActive()
event.target.isSelected = true
event.target:setStrokeColor(250,250,250) [/code]

Something like that. If you are sure there is only one .isSelected to remove you could add break after the = false line to break out of the loop and stop looking further. [import]uid: 41884 topic_id: 34899 reply_id: 139215[/import]

Maybe some day I’ll know enough that I can aid you in some way Richard :wink:

I had to make grid a global to get that to work, I had tried to make something pretty similar work without success, but it’s working now.

Should I be worried about the overhead involved in grid and mapTable both being globals? I have a 100 tiles, but a large map my have 400 tiles.

If you think it’ll be to much of a memory hog, I can probably make mapTable a local and load the info when needed, but I don’t know which is worse on performance, repetitive loading of a json file into mapTable or the extra group of globals. [import]uid: 170004 topic_id: 34899 reply_id: 139235[/import]

You probably didn’t have to make it a global; you just need to follow the rules of scope in Lua:

  1. You can only address what is behind you

[code]local y = 2

local function findX()
print("x = ", x) – nil
print("y = ", y) – 2
end

local x = 1
findX()[/code]

  1. Use forward addressing to get past this in many cases

[code]local x – predeclare so the variable exists!

local function findX() – the function doesn’t care what the value is until it’s executed
print("x = ", x) – 1
end

x = 1
findX() – executed here, AFTER x is given a value[/code]

  1. If you absolutely need to use globals, try to localize them.

[code]_G.bigTable = {} – lots of table stuff

local function manipulateTable()
local bigTable = bigTable – now you can access it inside the function locally
end[/code]

All of this being said in a slower pace game I’m not to worried about the overhead unless you go 1000+ tiles or need to constantly scroll the screen at a high speed. But localizing can often pay off for performance. [import]uid: 41884 topic_id: 34899 reply_id: 139246[/import]