Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Bug/Question on touch behavior

edited August 2013 in Bugs Posts: 580

Previously, I was under the impression that Codea always calls draw() after handing all new touch input for a frame. Based on my experience the last couple of days however, this does not seem to be the case.

For example, say I get a touch BEGAN for a touch. I was expecting that draw() would be called before that touch has a chance to change state to MOVING, however that does not seem to be the case, at least not always...sometimes the MOVING event will fire before the next draw(), so if I'm saving all of my touches, and handling them in draw(), then sometimes the BEGAN event gets lost, since the touch state changed to MOVING before I had a chance to handle the BEGAN event. The upshot is, I have some objects that depend on getting that initial BEGAN event, so they don't behave properly if that event gets skipped. In fact, sometimes the touch has cycled all the way to ENDED already before I even get any other events for the touch.

My questions:

  1. Is the draw() call (or the main thread) getting throttled whenever the iPad is handing touches? (i.e., if there is lots of internal touch processing going on, is draw() getting delayed until after the touch "event queue" is emptied?)
  2. If so, is there anything that can be done to Codea to alleviate this, or is this throttling the behavior of iOS itself?
  3. If the answer to #2 is that it's not a problem with Codea itself but rather iOS, any ideas for a potential workaround?

Also, a separate issue I've found: if you touch the screen with 3 simultaneous touches (no more, no less), the touches will "linger" for a bit before finally getting their ENDED events. I'm assuming this has something to do with Codea's double-tap-with-3-fingers-to-show-the-buttons behavior. It took me awhile to discern if this was an issue with my code, so I fired up the "Multi Touch" example, and the behavior occurs there as well, so there is some special 3-touch handling going on "under the hood" that can interfere with a program's multi-touch behavior.

Comments

  • Posts: 157

    It sounds like you're doing some sort of animation at the beginning of the touch (like a button_pressed sort of thing) and then moving the item under the finger, and the button isn't showing the pressed state if you move the finger right away after touching the screen?

    I've written a GUI in the past for a flight simulator, and I had to deal with all of this...

    My suggestion is to handle the touch immediately inside of the touched event. Assuming you're using classes for your buttons (or whatever you're interacting with), then you should have a touched event and a draw event in each of those visual objects. That object should save its own touched state...

    so in main:touched, you would do something like this: (this is semi-psuedocode, so it won't compile like this)

    for i,t in ipairs(touch) do
        for i,control in ipairs(controls) do
            if [touch is in the object's perimeter] then
                control:touched(t)  
            end
        end
    end
    

    in the control class,

        if t.state==BEGAN then
            set the control's "i'm being touched" state
        else if t.state==MOVING then
            calculate the new position for the control
        else if t.state==ENDED then
            set the control's state to "not being touched"
         end 
    
  • edited August 2013 Posts: 580

    Right, so, actually what I'm doing is trying to implement a generic system to make multi-touch handling easier by presenting the entire set of touches to handlers that want them (basically what the "Multi Touch" example does, but systematically, so that every class/object that wants this doesn't need to re-implement that logic). This requires me to defer delegating the touch events to handlers so that I can gather them all to present to the handlers.

    In fact, the behavior that I'm trying to implement is the native behavior for UIKit. UIKit doesn't parcel out individual touches one at a time like Codea does, it presents all of the updated touches at once. What would actually be great is if Codea could implement an alternate touch handler that just forwards all of those updated touches along in a set...then I wouldn't even need to do what I'm trying to do. In that case I wouldn't have to do anything in draw() either, since I could handle the touches en masse immediately.

    I'm brainstorming a solution right now that involves keeping a queue of "sets". Basically, I can tell whenever a touch's state has changed before I'm able to process it in draw(), and even how many times, so I'm thinking that I could do something like this:

    1. in touched(), new touches (BEGAN) are added to level 1 of the update queue
    2. in touched(), old touches (MOVING/ENDED/CANCELLED) that have already been processed in draw() are added to level 1 of the update queue
    3. in touched(), if an old touch changes state, but has not been processed yet in draw(), we add another level to the update queue. For example, if this is the 1st state change since draw() was called, we'd start a new set at level 2 (if it does not already exist), add this touch to that set, and add that set to the queue (if it's not already there). If this is the 2nd state change, we'd add a new set at level 3 (if necessary), etc, etc.
    4. In draw(), I'll process the events once for every set in the queue, and empty the queue.

    I think this will work, but it's a pretty significant rewrite, and I'm worried that it could introduce a lot of overhead (right now I've actually got this thing pretty optimized...)

  • Posts: 2,161

    @toadkick have you had a look at my touch handler? I think that the method is what you're looking for. In the touched function then the touch handler gathers all touches to itself, then in the draw function it hands them out to the various objects that have registered with it as accepting touches. So I avoid the problem of missing touch events by doing the gathering in touched but can process multi touches by doing the processing in draw. I think it pretty much does what you're looking for, plus or minus a few features.

    You can read about it at http://loopspace.mathforge.org/discussion/10/touch-tutorial

  • dave1707dave1707 Mod
    edited August 2013 Posts: 9,730

    @toadkick I found this awhile ago, but I never figured out what was happening. I'm not sure if it's a problem with touch or something else, but you might run into this with your touch code. Run this program and use your finger to draw a kind of tight sine wave across the screen at a steady rate. It appears that after every 23 or 24 plots, a plot/touch gets dropped.


    displayMode(FULLSCREEN) function setup()     backingMode(RETAINED) end function draw()     fill(255)     ellipse(CurrentTouch.x,CurrentTouch.y,10) end
  • edited August 2013 Posts: 175

    I would also suggest handle the touches immediately. I tend to only update states and other really light operations to avoid any weirdness when handling touch input.

    I use a small "tracking" class which is self culling to to keep a hold of the current touches which would normally have functions to wrap a touch with a table of functions to simplify handling.

    TouchTrack = class()
    
    function TouchTrack:init()
        self.count = 0
        self.touches = {}
    end
    
    function TouchTrack:start(touch)
         if touch.state == BEGAN then
            self.count = self.count + 1
            self.touches[touch.id] = touch
        elseif touch.state == MOVING and self.touches[touch.id]~= nil then
            self.touches[touch.id] = touch
        end
    end
    
    function TouchTrack:finish(touch)
        if touch.state == ENDED and self.touches[touch.id] ~= nil then
            self.touches[touch.id] = nil
            self.count = self.count - 1
        end
    end
    
    function TouchTrack:clear()
        self.touches={}
        self.count=0    
    end
    
    function TouchTrack:toarray()
        local new = {}
        local count = 1
        for i,v in pairs(self.touches) do
            new[count] = v
            count = count + 1
        end
        return new
    end
    

    Then in main or a class it's used like this

    function setup()
        touches = TouchTrack()
    end
    
    function touched(touch)
    
      touches.start(touch)
      -- handle touches 
      touches.finish(touch)
    
    end
    
  • edited August 2013 Posts: 580

    To further complicate things, it turns out a touch can live it's entire life (from BEGIN to END) before draw() even gets a chance to know it existed (and, even better, it's touch id might be reused immediately as well, so on the surface it looks like the same touch, unless you are checking for that condition specifically), which presents me with a weird philosophical conundrum: as far as a handler that gets updated in draw() is concerned, did that touch even exist? Well, of course it did, because any handlers handling the touch immediately would have seen the touch live out it's entire life. Sadly it's seeming that the "handle the touch immediately" approach is the only full-proof one (which I thought I was doing, until I realized that draw() can be throttled) :(

    @Simeon: What are the chances that we could get an alternate touch handler, say

    function touches(touches) end
    

    that would pass the entire set of updated touches, before Codea splits them up and parcels them out individually via touched()? If we have to assume touches must be handled immediately, that seems like this is the only way we can dispatch events with a set of updated touches reliably :(

  • edited August 2013 Posts: 580

    Eh, I rescind my request to add an additional handler to Codea. I was still able to implement a dispatcher with prioritized handlers and touch claiming, which were the main features I wanted anyway. As a pragmatic solution for providing a set of touches to handlers for convenience...I just pass the set of active touches as the second argument. Turns out this still achieves exactly what I wanted, only it's much simpler and more elegant (and a lot less code) than my original idea anyway :)

  • Posts: 1,976

    In a project I'm working on, I handle touch events with separate functions. function touch_began(touch), function touch_moved(touch), and function touch_ended(touch).

    Example:

    function touched(touch)
        if touch.state == BEGAN then
            touch_began(touch)
        end
    
        if touch.state == MOVING then
            touch_moved(touch)
        end
    
        if touch.state == ENDED or touch.state == CANCELLED then
            touch_ended(touch)
        end
    end
    
Sign In or Register to comment.