Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Weird problem — Gideros Forum

Weird problem

dddentdddent Member
edited April 2014 in General questions
Hello,

I'm having kind of a weird problem and no idea why... I want to draw a playing field on the screen (kind of like a chess field) and for testing purposes drew some rectangle sprites I already made in there. Those sprites are objects of a class called Button, which is made up of an object of the Rectangle class and a textfield. Now the following problem occurs: When adding all the fields with a simple loop, the first rectangle is missing the textfield, the last one misses the rectangle, and I can not find out why. As I dont want to post the code out of context I'll post the entire code, the problem however should occur in one of the classes Field (where the tiles are created and added to an array), Tile (the class for the tiles themselves) and maybe Rectangle and Button. So, here comes the code, I hope someone can help.

init.lua
-- battlefield 0.1a, (c) David Heuschmann
 
--[[ Notes
------ NOTES ------
 
-------------------
]]
 
-- CONSTANTS
Dimensions = {}
Dimensions.TILESWIDTH = 8 -- number of tiles on the x axis
Dimensions.TILESHEIGHT = 8 -- same for y axis
 
--[[ CLASS: Rectangle ##########
	Creates a rectangle with the specified position, size, border width and color and background color
 
	x: X-Position of the rectangle
	y: Y-Position of the rectangle
	width: width of the rectangle
	height: height of the rectangle
	lineWidth: width of the rectangles border | default: 1
	lineColor: color of the rectangles border | default: black
	bgColor: color of the rectangles background | default: white
]]
Rectangle = Core.class(Shape)
function Rectangle:init(x,y,width,height,lineWidth,lineColor,bgColor)
	-- set rectangles position
	self:setX(x)
	self:setY(y)
 
	-- draw the rectangle
	self:beginPath()
	self:setLineStyle(lineWidth or 1,lineColor or 0x000000) -- set border width and color
	self:setFillStyle(Shape.SOLID,bgColor or 0xFFFFFF) -- set background color
	self:moveTo(0,0)
	self:lineTo(width,0)
	self:lineTo(width,height)
	self:lineTo(0,height)
	self:lineTo(0,0)
	self:endPath()
 
	print("Rectangle added.")
end
-- END CLASS: Rectangle \\\\\\\\\
 
 
--[[ CLASS: Button ##########
	a clickable button
 
	x: buttons x-Position
	y: buttons y-Position
	text: the text displayed on the button
	graphic: the buttons background graphic (a sprite)
	action: a function that specifies what should happen if the button is clicked
]]
Button = Core.class(Sprite)
function Button:init(x,y,text,graphic,action)
	-- save the properties (should be different, because they should not be changeable later on)
	self.graphic = graphic
	self.text = text
	self.action = action
 
	-- set x and y position
	self:setX(x)
	self:setY(y)
 
	-- create the textfield with the buttons label and set its position to be in the buttons center
	tf = TextField.new(nil, text)
	tf:setX(graphic:getX() + graphic:getWidth()/2 - tf:getWidth()/2)
	tf:setY(graphic:getY() + graphic:getHeight()/2 + tf:getHeight()/2)
 
	-- add the graphic and the label
	self:addChild(graphic)
	self:addChild(tf)
 
	-- add the event listener to call self.onTouchesEnd when a click was performed
	self:addEventListener(Event.TOUCHES_END, self.onTouchesEnd, self)
 
	print("Button added.")
end
 
-- when the click was performed inside the buttons bounds call the action function that was passed
-- to the button on initialisation
function Button:onTouchesEnd(event)
	if self:hitTestPoint(event.touch.x, event.touch.y) then self:action() end
end
-- END CLASS: Button \\\\\\\\\
 
--[[CLASS: Tile ##########
	a tile on the field, which contains all the information about itself, like if there is a unit on it or if its highlighted
 
	width: the tiles width
	height: the tiles height
	fieldx: the x position of the tile on the field (ATTENTION: not the pixel position, but the count of tiles)
	fieldy: same, but for y
]]
Tile = Core.class(Sprite)
function Tile:init(width, height, fieldx, fieldy)
	self.width = width
	self.height = height
	self.fieldx = fieldx
	self.fieldy = fieldy
	self:setX((fieldx - 1) * width)
	self:setY((fieldy - 1) * width)
 
	print("Tile "..fieldx..fieldy.." added at "..self:getX()..", "..self:getY())
 
	rect = Rectangle.new(0,0,width, height, null,null,0xFF0000)
	button = Button.new(0,0,fieldx..fieldy,rect,function() print(fieldx..fieldy.." "..self:getX().." "..self:getY()) end)
 
	self:addChild(button)
end
 
--[[ CLASS: Field ##########
	represents the playing field with individual tiles on it
]]
Field = Core.class(Sprite)
function Field:init()
	self:setX(0) -- set the fields x position to be all the way to the left
	self:setY((application:getLogicalHeight() - application:getLogicalWidth()) / 2) -- set the fields y position to center it vertically
 
	rect = Rectangle.new(0,0,application:getLogicalWidth(), application:getLogicalWidth()) -- test rectangle
 
	tilewidth = application:getLogicalWidth() / Dimensions.TILESWIDTH
	tileheight = application:getLogicalWidth() / Dimensions.TILESHEIGHT
 
	tiles = {}
 
	for x = 1, Dimensions.TILESWIDTH do
		tiles[x] = {}
		for y = 1, Dimensions.TILESHEIGHT do
			tiles[x][y] = Tile.new(tilewidth, tileheight, x, y)
			self:addChild(tiles[x][y])
		end
	end
 
	self:addChild(rect)
end
-- END CLASS: Field \\\\\\\\\\
 
--[[ CLASS: Menu ##########
	Displays the menu and implements all its functionality
 
	screenmanager: the screenmanager object, that holds and changes the screen currently displayed
]]
Menu = Core.class(Sprite)
function Menu:init(screenmanager)
	-- things every screen needs:
	self.getScreenManager = function () return screenmanager end
	-- /////
 
	self.buttonbg = Rectangle.new(0,0,100,50) -- the background sprite
 
	-- add buttons
		-- button that starts the game (ofa)
	self.playbutton = Button.new(application:getLogicalWidth()/2 - self.buttonbg:getWidth()/2, 
		100,"Play",self.buttonbg, function () screenmanager:changeScreen(Screenmanager.GAME) end)
 
	-- add all sprites
	self:addChild(self.playbutton)
end
-- END CLASS: Menu \\\\\\\\\
 
 
 
--[[ CLASS: Game ##########
	Displays the game and implements all its functionality
]]
Game = Core.class(Sprite)
function Game:init(screenmanager)
	-- things every screen needs:
	self.getScreenManager = function () return screenmanager end
	-- /////
 
	self.buttonbg = Rectangle.new(0,0,100,50)
 
	self.backbutton = Button.new(application:getLogicalWidth()/25, 
		application:getLogicalHeight()/50,"Back",self.buttonbg, function () screenmanager:changeScreen(Screenmanager.MENU) end)
 
	field = Field.new()
 
	self:addChild(field)
	self:addChild(self.backbutton)
end
-- END CLASS: Game \\\\\\\\\
 
 
 
--[[ CLASS: Screenmanager ##########
	manages which screen is displayed, screen change is triggered by eve
]]
Screenmanager = Core.class(Sprite)
 
Screenmanager.MENU = "menu"
Screenmanager.GAME = "game"
 
function Screenmanager:init()
	-- instantiate the different screens
	self.menu = Menu.new(self)
	self.game = Game.new(self)
 
	-- set and add the start screen
	self.currentScreen = self.menu
	self:addChild(self.menu)
end
 
-- called when a screenchange event is dispatched
function Screenmanager:changeScreen(newScreen)
	if newScreen == Screenmanager.MENU then screen = self.menu
	elseif newScreen == Screenmanager.GAME then screen = self.game
	else return false
	end
 
	self:removeChild(self.currentScreen)
	self.currentScreen = screen
	self:addChild(self.currentScreen)
	return true
end
 
-- END CLASS: Screenmanager \\\\\\\\\
main.lua
sm = Screenmanager.new()
 
stage:addChild(sm)

Comments

  • The first thing that jumps out at me is that you're using global variables for everything. I haven't looked closely yet, but it wouldn't surprise me if that is causing some, if not all of your issues. If you're not using a value outside of the scope of your function, declare it with "local". For example, in Tile:init, the rect and button lines would look like this:
    local rect = Rectangle.new(0,0,width, height, null,null,0xFF0000)
    local button = Button.new(0,0,fieldx..fieldy,rect,function() print(fieldx..fieldy.." "..self:getX().." "..self:getY()) end)
    That way, if the same values show up elsewhere, Lua won't get confused. I'll look again and see if anything else jumps out, but I felt that this was important enough that I should call it out. It'll save you a lot of headaches down the road!
  • Accepted Answer
    Ah; here we go. I went ahead and added the appropriate "local" declarations where they needed to be, and noticed that the test rectangle in Field:init(), which was a global, was on top of everything. Since it was a global, it was being repositioned for every tile you added, ending up in the lower-right corner. Once the locals are set up properly and I remove the test rectangle, everything looks good.

    Here's what it looks like now:
    -- battlefield 0.1a, (c) David Heuschmann
     
    --[[ Notes
    ------ NOTES ------
     
    -------------------
    ]]
     
    -- CONSTANTS
    Dimensions = {}
    Dimensions.TILESWIDTH = 8 -- number of tiles on the x axis
    Dimensions.TILESHEIGHT = 8 -- same for y axis
     
    --[[ CLASS: Rectangle ##########
    	Creates a rectangle with the specified position, size, border width and color and background color
     
    	x: X-Position of the rectangle
    	y: Y-Position of the rectangle
    	width: width of the rectangle
    	height: height of the rectangle
    	lineWidth: width of the rectangles border | default: 1
    	lineColor: color of the rectangles border | default: black
    	bgColor: color of the rectangles background | default: white
    ]]
    Rectangle = Core.class(Shape)
    function Rectangle:init(x,y,width,height,lineWidth,lineColor,bgColor)
    	-- set rectangles position
    	self:setX(x)
    	self:setY(y)
     
    	-- draw the rectangle
    	self:beginPath()
    	self:setLineStyle(lineWidth or 1,lineColor or 0x000000) -- set border width and color
    	self:setFillStyle(Shape.SOLID,bgColor or 0xFFFFFF) -- set background color
    	self:moveTo(0,0)
    	self:lineTo(width,0)
    	self:lineTo(width,height)
    	self:lineTo(0,height)
    	self:lineTo(0,0)
    	self:endPath()
     
    	print("Rectangle added.")
    end
    -- END CLASS: Rectangle \\\\\\\\\
     
     
    --[[ CLASS: Button ##########
    	a clickable button
     
    	x: buttons x-Position
    	y: buttons y-Position
    	text: the text displayed on the button
    	graphic: the buttons background graphic (a sprite)
    	action: a function that specifies what should happen if the button is clicked
    ]]
    Button = Core.class(Sprite)
    function Button:init(x,y,text,graphic,action)
    	-- save the properties (should be different, because they should not be changeable later on)
    	self.graphic = graphic
    	self.text = text
    	self.action = action
     
    	-- set x and y position
    	self:setX(x)
    	self:setY(y)
     
    	-- create the textfield with the buttons label and set its position to be in the buttons center
    	local tf = TextField.new(nil, text)
    	tf:setX(graphic:getX() + graphic:getWidth()/2 - tf:getWidth()/2)
    	tf:setY(graphic:getY() + graphic:getHeight()/2 + tf:getHeight()/2)
     
    	-- add the graphic and the label
    	self:addChild(graphic)
    	self:addChild(tf)
     
    	-- add the event listener to call self.onTouchesEnd when a click was performed
    	self:addEventListener(Event.TOUCHES_END, self.onTouchesEnd, self)
     
    	print("Button added.")
    end
     
    -- when the click was performed inside the buttons bounds call the action function that was passed
    -- to the button on initialisation
    function Button:onTouchesEnd(event)
    	if self:hitTestPoint(event.touch.x, event.touch.y) then self:action() end
    end
    -- END CLASS: Button \\\\\\\\\
     
    --[[CLASS: Tile ##########
    	a tile on the field, which contains all the information about itself, like if there is a unit on it or if its highlighted
     
    	width: the tiles width
    	height: the tiles height
    	fieldx: the x position of the tile on the field (ATTENTION: not the pixel position, but the count of tiles)
    	fieldy: same, but for y
    ]]
    Tile = Core.class(Sprite)
    function Tile:init(width, height, fieldx, fieldy)
    	self.width = width
    	self.height = height
    	self.fieldx = fieldx
    	self.fieldy = fieldy
    	self:setX((fieldx - 1) * width)
    	self:setY((fieldy - 1) * width)
     
    	print("Tile "..fieldx..fieldy.." added at "..self:getX()..", "..self:getY())
     
    	local rect = Rectangle.new(0,0,width, height, null,null,0xFF0000)
    	local button = Button.new(0,0,fieldx..fieldy,rect,function() print(fieldx..fieldy.." "..self:getX().." "..self:getY()) end)
     
    	self:addChild(button)
    end
     
    --[[ CLASS: Field ##########
    	represents the playing field with individual tiles on it
    ]]
    Field = Core.class(Sprite)
    function Field:init()
    	self:setX(0) -- set the fields x position to be all the way to the left
    	self:setY((application:getLogicalHeight() - application:getLogicalWidth()) / 2) -- set the fields y position to center it vertically
     
    	--local rect = Rectangle.new(0,0,application:getLogicalWidth(), application:getLogicalWidth()) -- test rectangle
     
    	local tilewidth = application:getLogicalWidth() / Dimensions.TILESWIDTH
    	local tileheight = application:getLogicalWidth() / Dimensions.TILESHEIGHT
     
    	tiles = {}
     
    	for x = 1, Dimensions.TILESWIDTH do
    		tiles[x] = {}
    		for y = 1, Dimensions.TILESHEIGHT do
    			tiles[x][y] = Tile.new(tilewidth, tileheight, x, y)
    			self:addChild(tiles[x][y])
    		end
    	end
     
    	--self:addChild(rect)
    end
    -- END CLASS: Field \\\\\\\\\\
     
    --[[ CLASS: Menu ##########
    	Displays the menu and implements all its functionality
     
    	screenmanager: the screenmanager object, that holds and changes the screen currently displayed
    ]]
    Menu = Core.class(Sprite)
    function Menu:init(screenmanager)
    	-- things every screen needs:
    	self.getScreenManager = function () return screenmanager end
    	-- /////
     
    	self.buttonbg = Rectangle.new(0,0,100,50) -- the background sprite
     
    	-- add buttons
    		-- button that starts the game (ofa)
    	self.playbutton = Button.new(application:getLogicalWidth()/2 - self.buttonbg:getWidth()/2, 
    		100,"Play",self.buttonbg, function () screenmanager:changeScreen(Screenmanager.GAME) end)
     
    	-- add all sprites
    	self:addChild(self.playbutton)
    end
    -- END CLASS: Menu \\\\\\\\\
     
     
     
    --[[ CLASS: Game ##########
    	Displays the game and implements all its functionality
    ]]
    Game = Core.class(Sprite)
    function Game:init(screenmanager)
    	-- things every screen needs:
    	self.getScreenManager = function () return screenmanager end
    	-- /////
     
    	self.buttonbg = Rectangle.new(0,0,100,50)
     
    	self.backbutton = Button.new(application:getLogicalWidth()/25, 
    		application:getLogicalHeight()/50,"Back",self.buttonbg, function () screenmanager:changeScreen(Screenmanager.MENU) end)
     
    	field = Field.new()
     
    	self:addChild(field)
    	self:addChild(self.backbutton)
    end
    -- END CLASS: Game \\\\\\\\\
     
     
     
    --[[ CLASS: Screenmanager ##########
    	manages which screen is displayed, screen change is triggered by eve
    ]]
    Screenmanager = Core.class(Sprite)
     
    Screenmanager.MENU = "menu"
    Screenmanager.GAME = "game"
     
    function Screenmanager:init()
    	-- instantiate the different screens
    	self.menu = Menu.new(self)
    	self.game = Game.new(self)
     
    	-- set and add the start screen
    	self.currentScreen = self.menu
    	self:addChild(self.menu)
    end
     
    -- called when a screenchange event is dispatched
    function Screenmanager:changeScreen(newScreen)
    	if newScreen == Screenmanager.MENU then screen = self.menu
    	elseif newScreen == Screenmanager.GAME then screen = self.game
    	else return false
    	end
     
    	self:removeChild(self.currentScreen)
    	self.currentScreen = screen
    	self:addChild(self.currentScreen)
    	return true
    end
     
    -- END CLASS: Screenmanager \\\\\\\\\
     
    sm = Screenmanager.new()
     
    stage:addChild(sm)

    Likes: dddent

    +1 -1 (+1 / -0 )Share on Facebook
  • Thank you very much for looking into that! Luas "oop" can be pretty confusing, though i like it :)
  • edited April 2014
    It's very free-form, to be sure. Lua's lack of strictness is both its greatest strength and its greatest weakness. I love it, personally, but I do get myself in to trouble at times. Forgetting "local" declarations is one that I still often do, and the results almost always leave me scratching my head for a bit.
Sign In or Register to comment.