Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Button event problem — Gideros Forum

Button event problem

MillerszoneMillerszone Member
edited October 2011 in General questions
No matter where I touch the screen, function testButton() runs.
I just want testButton to run when I hit the helpBTN bitmap image.
Thanks

Sample code:
-- Create menu group
local menuGroup = Sprite.new()
stage:addChild(menuGroup)
 
-- Help button image
helpBTN = Bitmap.new(Texture.new("helpBtn.png"))
helpBTN:setPosition(439, 11)
helpBTN.name = "helpBTN"
menuGroup:addChild(helpBTN)
 
local function testButton()
print"BANG"
end
 
helpBTN:addEventListener(Event.MOUSE_UP, testButton, helpBTN)

Comments

  • atilimatilim Maintainer
    Hi,

    Mouse and Touch events are dispatched to all objects on the stage. Therefore you need to test whether the mouse coordinate is hitting the object or not. If you change the testButton function as:
    local function testButton(target, event)
      if target:hitTestPoint(event.x, event.y) then
          print"BANG"
      end
    end
    Everything will be ok.

    Also, there is an easy to use Button class in the examples (Graphics/Button). I recommend you to use it directly in your game.



  • this implementation is a bit odd i think. could we just add a touch event listener to a display object and get rid of those hitTestPoint calls?? yes i am lazy :)
  • I looked at Button class, and would like to use it just for my menu screen, not the
    game play screen. My concern is, if I use the Button class for my menu screen and then
    switch to my game play screen, won't all the Button class event listeners still be running
    in my game play screen, or can I delete remove the Button class after using?
    Sorry for all the questions, like I said, I'm still new to LUA. Thank you.

    Button class event listeners:
    self:addEventListener(Event.MOUSE_DOWN, self.onMouseDown, self)
    self:addEventListener(Event.MOUSE_MOVE, self.onMouseMove, self)
    self:addEventListener(Event.MOUSE_UP, self.onMouseUp, self)
    self:addEventListener(Event.TOUCHES_BEGIN, self.onTouchesBegin, self)
    self:addEventListener(Event.TOUCHES_MOVE, self.onTouchesMove, self)
    self:addEventListener(Event.TOUCHES_END, self.onTouchesEnd, self)
    self:addEventListener(Event.TOUCHES_CANCEL, self.onTouchesCancel, self)
  • Hi,
    Mouse and Touch events are dispatched to all objects on the stage. Therefore you need to test whether the mouse coordinate is hitting the object or not. If you change the testButton function as:
    local function testButton(target, event)
      if target:hitTestPoint(event.x, event.y) then
          print"BANG"
      end
    end
    Everything will be ok.

    Also, there is an easy to use Button class in the examples (Graphics/Button). I recommend you to use it directly in your game.
    It's working great. Only had to make small changes to my original code.
    Thanks.


  • atilimatilim Maintainer
    @zilog79
    It was a hard decision :) We choose this method because of efficiency. (we don't want to do hit test for every mouse/touch events and leave the decision to the programmer)

    @Millerszone
    mouse and touch events are dispatched to the sprites that are on the stage. Therefore, when you remove your menu screen from the stage, none of its children will receive mouse/touch events and everything will be ok :)
  • O.k., I was also removing all the event listeners after removing my screen groups from the stage.
    I thought the event listeners would always be running in the background if you didn't remove.
    So now I will only remove the screen, that's good to know.
  • atilimatilim Maintainer
    edited October 2011
    exactly. no need to remove mouse/touch events.

    But, ENTER_FRAME event is a little bit different. It's dispatched to all Sprites. I can recommend using ADDED_TO_STAGE and REMOVED_FROM_STAGE events to control the ENTER_FRAME event as:
    MySprite = gideros.class(Sprite)
     
    function MySprite:init()
      self:addEventListener(Event.ADDED_TO_STAGE, self.onAddedToStage, self)
      self:addEventListener(Event.REMOVED_FROM_STAGE, self.onRemovedFromStage, self)
    end
     
    function MySprite:onAddedToStage()
      self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    end
     
    function MySprite:onRemovedFromStage()
      self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    end
     
    function MySprite:onEnterFrame()
      -- do your enter frame logic
    end
  • @atilim
    i see.. but from an oo view; adding a touch listener to an object and receiving a callback from it implies that a touch event just occured on _that_ object IMHO. this confuses me. anyway.. Using _only_ Runtime object to add those listeners and get rid of sprite touch listener may be a more appropriate decision. Or you could automagically hit test only those sprites with event listeners attached. just my 2 cents 8)
  • atilimatilim Maintainer
    @zilog79

    As I said, it was a hard decision :)

    I all understand your points. Mouse down and up events are ok, but mouse move events can sometimes be problematic when they only occurs on the specified object (e.g. while dragging)

    We don't want to add mouse/touch events to a Runtime object because you can stop propagation of the event so that only the top most can receive the events.

    Still I don't know which way is better :)
  • I am new to Gideros and wanted to try out and compare..
    I've had a MOUSE_DOWN Listener before and everything worked fine, but decided then, I rather would use multitouch, so went to TOUCHES_BEGIN and since then it prints me an error
    Object.lua:65: bad argument #1 to 'hitTestPoint' (number expected, got nil)
    Here is the piece of code I'm talking about..
    -- Event.MOUSE_DOWN:
    function Object:onMouseDown( event )
    	if self:hitTestPoint( event.x, event.y ) then -- touched inside obj?
    		self:removeEventListener( "enterFrame", self.onEnterFrame, self )
    		self:updateVisualState(true)
    		self:setScale( 2, 2 )
    	end
    end
    self:addEventListener( "touchesBegin", self.onMouseDown, self )
    Owltwins. Smart design and creative code.
    »Gideros Illustrator« - [svg|xml] scene designer using Adobe Illustrator®™ Within one line of code!
  • atilimatilim Maintainer
    Hi,

    When a touch event occurs, the event table contains two fields: 'touches' and 'allTouches'. You should use this fields instead of 'x' and 'y'. For example:
    function Object:onTouchesBegin(event)
      for i=1,#event.touches do
        if event.touches[i].id == 1 then -- first touch
          local x = event.touches[i].x
          local y = event.touches[i].y
          -- use x and y here
        end
      end
    end

    Likes: yarnee

    +1 -1 (+1 / -0 )Share on Facebook
  • atilimatilim Maintainer
    (also there is an example Hardware/Touch Explorer)
  • thanks, solved :)
    Owltwins. Smart design and creative code.
    »Gideros Illustrator« - [svg|xml] scene designer using Adobe Illustrator®™ Within one line of code!
  • Hi,

    Mouse and Touch events are dispatched to all objects on the stage. Therefore you need to test whether the mouse coordinate is hitting the object or not. If you change the testButton function as:
    local function testButton(target, event)
      if target:hitTestPoint(event.x, event.y) then
          print"BANG"
      end
    end
    Everything will be ok.

    Also, there is an easy to use Button class in the examples (Graphics/Button). I recommend you to use it directly in your game.



    is this still true, having trouble getting it to work?
    Please change it
  • ar2rsawseenar2rsawseen Maintainer
    I think this is still true, but example you quoted is for mouse events. for touch events, you would do
    local function testButton(target, event)
      if target:hitTestPoint(event.touch.x, event.touch.y) then
          print"BANG"
      end
    end
  • I struggled (very briefly) with the current touch system, initially thinking only the object I added a handle to would get the event - however I can see the wisdom of @atilim's approach as without it it's very hard to add the real polish to buttons, actions like where you can press and move off before release and have the buttons work properly (as in the way they do natively - especially on iOS), also pressing elsewhere, moving and then releasing on buttons (should be a no-no) is hard to pickup otherwise as well.

    Another way to think of it would be to think of the stage as big container and just add the touch handler to that and the propagate the events yourself - I did this with my first Gideros app and it worked well, however @atilim's system is easier (and more flexible) as you don't have to explicitly manage all of your buttons in a single place.

    QUICK TIP.

    After a really close examination of how UI buttons work on iOS I discovered another little "tip", which is especially useful for smaller buttons.

    Some buttons on iOS have an "extension" around them, which is picked up when moving and releasing (but not pressing) so that if you touch a button you can move your finger slightly which might "just" go out of bounds but is still treated as a hold / press, it's a small thing but it makes a BIG difference to the user, it just makes the buttons "work" without being fiddly - as I said, it's especially useful on smaller buttons - especially tab bars - those of you with iOS devices, try it out!

    I've added that functionality to my button / widget library (beta release coming soon), and it it DOES make a difference - it's the little things like this that most people don't notice but it just makes the app "feel" better on a subconscious level.
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
Sign In or Register to comment.