Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
working with events — Gideros Forum

working with events

boriskeyboriskey Member
edited July 2013 in Game & application design
Hi, I am pulling my hair out trying to understand what's wrong with my code. I created a sample code below using button class and have a few questions for you:

1) why my buttons still receive events even after I did this:
			-- remove button and event listener
			self.buttons[i]:removeEventListener(Event.MOUSE_DOWN, self.onClick,self)
			self.buttons[i]:removeFromParent()
2) Why do I need to remove events manually even though I removed the button from parent class? should not Gideros remove event automatically? looks like an extra step...

3) in my onClick event I have to put extra code like this to identify the instance of the button that actually dispatched event. Is there a better way of doing this?
for i=1,5 do
 
		if self.buttons[i]:hitTestPoint(event.x, event.y) then
			print ("onClick  on button "..i)
 
			....
 
			event:stopPropagation()
 
		end

And here is my full sample. I create an array of buttons (5) and i want to remove onclick event and the button itself from stage once it is clicked on. The problem is it is "clicable" even though it is removed from stage.
 
GameScene = gideros.class(Sprite)
 
function GameScene:init()
 
	self.buttons = {}
 
	for i=1,5 do
 
		-- create the up and down sprites for the button
		local up = Bitmap.new(Texture.new("button_up.png"))
		local down = Bitmap.new(Texture.new("button_down.png"))
 
		-- create the button
		self.buttons[i] = Button.new(up, down)
 
		self.buttons[i].id = i
 
		self:addChild(self.buttons[i])
 
		self.buttons[i]:addEventListener(Event.MOUSE_DOWN, self.onClick,self)
 
		self.buttons[i]:setPosition(50, 70*(i-1))
 
 
	end
 
end
 
function GameScene:onClick(event)
 
	for i=1,5 do
 
		if self.buttons[i]:hitTestPoint(event.x, event.y) then
			print ("onClick  on button "..i)
 
			-- remove button and event listener
			self.buttons[i]:removeEventListener(Event.MOUSE_DOWN, self.onClick,self)
			self.buttons[i]:removeFromParent()
 
			event:stopPropagation()
 
		end
 
	end 
 
 
end
 
stage:addChild(GameScene.new())

Likes: Yan

+1 -1 (+1 / -0 )Share on Facebook

Comments

  • ar2rsawseenar2rsawseen Maintainer
    edited July 2013
    Hello @boriskey ;)
    1) hitTestPoint is required. Right now it seems like unnecessary if, but it actually is giving you more flexibility, like if button is small, you can test it to more wider area. So you can keep small graphic but still make it clickable on small resolutions. Or detect if user pressed and then pulled the mouse cursor or touch away off the button to cancel click, etc.

    2) Sprite stops receiving input events, like mouse, touch or key when it is removed from stage. And it is actually happening. In your case problem is, that while sprite is removed, it still returns true for hitTestPoint, and since you have not removed it from your self.buttons table, you still iterate through it and check for hitTestPoint. So you need not only to remove it from parent, but also remove from self.buttons table

    3) Buttons class already provides internal event "click" which does the hitTestPoint checking and stopping propagation, etc.

    4) I would recommend not to assign same event listener to all buttons and iterate through them, but have separate event listener for each one, like:
    GameScene = gideros.class(Sprite)
     
    function GameScene:init()
     
    	self.buttons = {}
     
    	for i=1,5 do
     
    		-- create the up and down sprites for the button
    		local up = TextField.new(nil, "Button UP")
    		up:setScale(2)
    		local down = TextField.new(nil, "Button UP")
    		down:setScale(2)
     
    		-- create the button
    		self.buttons[i] = Button.new(up, down)
     
    		self.buttons[i].id = i
     
    		self:addChild(self.buttons[i])
     
    		self.buttons[i]:addEventListener("click",function(self, event)
    			print ("onClick  on button "..self.id)
    			self:removeFromParent()
    		end,self.buttons[i])
     
    		self.buttons[i]:setPosition(50, 70*(i-1))
     
     
    	end
     
    end
  • just to point out, as you can see from the above example, you need to use hittestpoint (although it can be hidden in the button class), but however you do this, you don't have to iterate through all the buttons to find the right one as with addeventlistener you can send to onclick the info (e.g. as self) about which button is pressed. here it is done at the end of the line: ..."self.buttons[i])". so you immediately know which button is supposed to be pressed, and then you only need to check with hittestpoint if it was indeed pressed.
  • just to point out, as you can see from the above example, you need to use hittestpoint (although it can be hidden in the button class), but however you do this, you don't have to iterate through all the buttons to find the right one as with addeventlistener you can send to onclick the info (e.g. as self) about which button is pressed. here it is done at the end of the line: ..."self.buttons[i])". so you immediately know which button is supposed to be pressed, and then you only need to check with hittestpoint if it was indeed pressed.
    I tried that at first but I wanted to use scenemanager and have to pass self of the scene class to get access to its methods/variables. If I pass self.buttons[i], I can get access to the button members but not the scene. It actually would be perfect if I could access both button self and self for a scene but i did not figure out how to do that. Can you give me an idea?
  • Hi @ar2rsawseen, you are always first to reply - thanks!

    I have a few follow-up questions.

    1) if I use anonymous function like in your example, how then I can remove listener for it? I was looking for that on the forum and I think someone said it is not possible without repeating the whole code or using named function.

    2) how can I remove my button from scene and from buttons table? I thought this code will do this trick, no? self.buttons[i]:removeFromParent(). Are you saying that table does not count as a "parent"?

    thanks guys!
  • you can use perhaps getParent on the buttons to get its parent, in case it is the scene itself. or you can add to each button a reference to the scene it is in, e.g. myButton.parentScene=scene1 ... when creating the button.

    to pass both infos when the event is triggered, there are many ways to do this (i also recently asked about how to pass multiple variables), read this thread e.g.:
    http://www.giderosmobile.com/forum/discussion/comment/27288
  • ar2rsawseenar2rsawseen Maintainer
    Accepted Answer
    @boriskey yes you can't remove anonymous functions, but Gideros will remove it automatically when garbage collecting it. And as I said sprite stops receiving input events once removed so it should not be a problem, if you still want to remove listener, you can do it like that:
    function self.buttons[i]:onClick(event)
    	print ("onClick  on button "..self.id)
    	self:removeEventListener("click", self.onClick, self)
    	self:removeFromParent()
    end
    self.buttons[i]:addEventListener("click",self.buttons[i].onClick,self.buttons[i])
    And removeFromParent works only for Sprite hierarchy, to remove element from buttons table you need to nil it like that:
    self.buttons[i] = nil
    Then there will be no reference to this sprite anywhere, and Gideros can garbage collect it, thus also removing event listener.

    But just to note, that the length of self.buttons will be decreased, so you better rewrite your loop like:
    for i=1,#self.buttons do
    instead of hardcoded 5 length ;)
Sign In or Register to comment.