Howdy, Stranger!

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

Problem with the touch state

edited December 2012 in Questions Posts: 14

I created a class for handling sprites the purpose is to choose symbols from a menu and drag them to positions on the screen. But it seems to not work all the time, or theres got to be a better way to do this.
here is the touch part of the class.

    ScaledSprite = class()
    function ScaledSprite:init(mySprite,Ssize,x,y)
        self.pos = vec2(x,y)
        self.mySprite = mySprite
        self.Ssize = Ssize
        self.Sw,self.Sh = spriteSize(self.mySprite)
        self.action = nil
        self.selected = false
    end

    function ScaledSprite:draw()
        -- Codea does not automatically call this method
        factor = self.Ssize / 100
        sprite(self.mySprite,self.pos.x,self.pos.y,self.Sw * factor,self.Sh * factor)
    end

    function ScaledSprite:touched(touch)
        -- Codea does not automatically call this method
        if touch.state == MOVING then
           if (math.abs(self.pos.x - touch.x) <= (self.Sw/2)
           and math.abs(self.pos.y - touch.y) <= (self.Sh/2)) then
            if flying ~= "none" then
                self.selected = true
             else
                self.selected = false
            end
        elseif touch.state == ENDED then
            if self.selected == true and self.action then
                self.selected = false
                self.action()
            end
        end
    end

As I explained sometimes it gets the ENDED condition but sometimes it doesn't.

From Main this were I called the class action: (Fliying is a control variable to indicate that I hit symbol from the menu)

    if (flying == "P1") then
        if CurrentTouch.y > 680 then
            fixedy = 680
        elseif CurrentTouch.y < 100 then
            fixedy = 100
        else
            fixedy = CurrentTouch.y   
        end

        ssP1 = ScaledSprite("Dropbox:symbol-P1",symbSize * vsmFactor,CurrentTouch.x,fixedy)
        ssP1.action = function() btnSymbPRO1pressed() end

        ssP1:draw()
        dragging = true     

    end

Action function that's is called after the touch ENDED is detected

    function btnSymbPRO1pressed()
            iSymbol = iSymbol + 1
            flying = "none"
            dragging = false
            aSx[iSymbol] = CurrentTouch.x
            aSy[iSymbol] = CurrentTouch.y
            fw, fh = spriteSize("Dropbox:symbol-P1")
            aSw[iSymbol] = fw * (symbSize / 100) * vsmFactor
            aSh[iSymbol] = fh * (symbSize / 100) * vsmFactor
    end

Comments

  • Perhaps related to the other touch problem thread.

    Can you check if you get any touch event at all by simply printing t.state? Especially look out for the undocumented state CANCELLED (which equals to 3 on Codea 1.5) and verrrrry unlikely STATIONARY (4 on Codea 1.5).

  • Codeslinger I tried what you recommended so I added the code
    if(touch.state ~= touchvar) then
    print(touchstate)
    touchvar = touch.state
    end

    the tracking is working I am getting 0 when start, 1 when moving and 2 when ended. But the ended doesnt triggers on time the function.

  • Posts: 666

    dumb question: are you passing the touch parameter from the main draw() loop?

    function draw()
         ScaledSprite:draw() -- or the instance if it's not a static class
    end
    
  • I don't fully understand your question (I am new working with objects and classes) but here is where I call the class

        if (flying == "P1") then
            if CurrentTouch.y > 680 then
                fixedy = 680
            elseif CurrentTouch.y < 100 then
                fixedy = 100
            else
                fixedy = CurrentTouch.y   
            end
    
            ssP1 = ScaledSprite("Dropbox:symbol-P1",symbSize * vsmFactor,CurrentTouch.x,fixedy)
            ssP1.action = function() btnSymbPRO1pressed() end
    
            ssP1:draw()
            dragging = true     
    
        end
    

    And I use the currentTouch property for dragging the sprite on the screen and when I release the touch it suppose to save the position in a table it sometimes does it but sometimes don't

  • Posts: 666

    CurrentTouch is a strange beast : it works as long as you never plan to do more than a single touch and you can depend on that. Otherwise, CurrentTouch gets the last touch, which in a multitouch scenario would be bad for your code; you might not get the ended state if there's a new or different touch id involved.

    Nonetheless, I don't see anywhere you call ssP1:touched(touch), and use the touch.state property in your sprite class, but based on your previous commentary, you must call it at so e point.

  • edited December 2012 Posts: 14

    Here is where I called the touch

        btnHELP:touched(touch)
    
                if (location == "new" or location == "edit") then
                    btnPROCESSsymbols:touched(touch)
                    btnMATERIALsymbols:touched(touch)
                    btnINFORMATIONsymbols:touched(touch)
                    btnGENERALsymbols:touched(touch)
                    btnCONNECTORSsymbols:touched(touch)
                    if(menustatus =="steady") then
                        btnSymbPRO1:touched(touch)
                    end
    
                    ssP1:touched(touch)
    
                end
    
        end
    

    Here is the state property being used inside the sprite class

        function ScaledSprite:touched(touch)
        -- Codea does not automatically call this method
                if touch.state == MOVING then
                        if (math.abs(self.pos.x - touch.x) <= (self.Sw/2) 
                        and math.abs(self.pos.y - touch.y) <= (self.Sh/2)) then
                                if flying ~= "none" then
                                        self.selected = true
                                else
                                        self.selected = false
                                end
                        end
    
                elseif touch.state == ENDED or (touch.state == MOVING and 
        (math.abs(math.abs(touch.deltaX) + math.abs(touch.deltaY)) < 2.6)) then
                        if self.selected == true and self.action then
                                flying = "none"
                                self.selected = false
                                self.action()
                        end
                end
        end
    

    This is driving me nuts, because it works a 50% of the time, but I cannot move forward with this app until I find a workaround which I think could be a timing issue of the execution.

  • edited December 2012 Posts: 14

    I fix it with a walk around I added this conditional in the touched function

            if touch.state == ENDED and flying == "P1" then
                btnSymbPRO1pressed()
            end
    

    It's a particular solution but Codea doesn't handle well the touch.state triggering during runtime

  • I've read your post too hastily the first time, let's go on with a more profound analysis that tries to unravel your wrongdoing since I won't let you go with a comment like "Codea doesn't handle well the touch.state triggering during runtime" (except for an edge case that I look at a moment before).

    Like aciolino mentioned you should avoid the CurrentTouch variable or at least make sure with a simple test program that it fulfills your needs. What we're not really seeing is the full program flow, starting with the global functions draw() and touched().

    Look at this part of the code:


    if (flying == "P1") then if CurrentTouch.y > 680 then fixedy = 680 elseif CurrentTouch.y < 100 then fixedy = 100 else fixedy = CurrentTouch.y end ssP1 = ScaledSprite("Dropbox:symbol-P1",symbSize * vsmFactor,CurrentTouch.x,fixedy) ssP1.action = function() btnSymbPRO1pressed() end ssP1:draw() dragging = true end

    Where do you execute this? When? I hope not when flying == "P1", in this case you would call this very often. Or do you set flying to a different value later on? Is it necessary to draw ssP1 after creation? There should be a separate code section concerned with drawing the sprite that is independent of the code section where it is created.

    Your work around operates diffently from your original intention.

    Original (abbreviated):


    function ScaledSprite:touched(touch) -- Codea does not automatically call this method if touch.state == MOVING then -- self selected can be set here elseif touch.state == ENDED then if self.selected == true and self.action then self.selected = false self.action() end end end

    You original solution requires you to move your finger and not just touch the sprite to execute the action.

    Your work around:


    if touch.state == ENDED and flying == "P1" then btnSymbPRO1pressed() end

    You're calling btnSymbPRO1pressed (why not the associated sprite action?) now when the touch ended which includes tapping without dragging. I thought this was somewhat essential.

    I assume you've got a wrong event flow in mind, so please provide more context so we have a chance to repair it.

  • Codeslinger thanks for your response; I agree with your statement that this could be generated by a wrong event flow made by me.

    But let me try to explain what I am trying to do. The app has a menu with different symbols (a total of 9) each symbol is a Class treated as a Button, my intention is to select each sprite by dragging them out of the menu, then you move around the screen to select a position. When you release your finger (touch.state == ENDED) condition stores the position and id of each symbol on a table.

    So my issue was that when I released the symbol sometimes the class action was executed and sometimes didn’t.

    Answering your questions...
    1) flying == "P1", is part of the Global Draw function, flying is just a control var to enable the drawing of the dragged symbol. Fixedy is just a modification to avoid drawing the symbol over the menu bars.
    2) btnSymbPRO1pressed() is the function that is called with the action associated to the Class action I just added as a Plan B in case that the function was not triggered by the touch state condition.

  • dave1707dave1707 Mod
    Posts: 7,470

    You say when the touch.state == ENDED, you save the position and ID. Is the ID you're saving the touch.id . Once the touch state ends, the id isn't valid for anything anymore. Another touch can use the same id as a previous one that's ended. Even if you touch the same button, a new id or sometimes the same id will be used. The touch.id is valid only as long as the touch is in progress. I'm not sure if that might be your problem, but keep that in mind when using touch.id.

  • Not Dave the id is table I use to identify each symbol I am not using Multitouch.

  • Posts: 1,238

    Try this.

    --# Circle
    Circle = class()
    
    function Circle:init(n, x, y)
        -- you can accept and set parameters here
        self.n = n
        self.x = x
        self.y = y
        self.lifted = false
    end
    
    function Circle:draw()
        fill(127, 127, 127, 255)
        ellipse(self.x, self.y, 50)
        fill(0, 0, 0, 255)
        text(self.n, self.x, self.y)
    end
    
    function Circle:touched(touch)
        if touch.state == BEGAN then
            if math.abs(touch.x - self.x) < 30 and 
                math.abs(touch.y - self.y) < 30 then
                    self.lifted = true
            end
        end
        if touch.state == MOVING and self.lifted then
            self.x = touch.x
            self.y = touch.y
        end
        if touch.state == ENDED and self.lifted then
            self.lifted = false
        end
    end
    
    
    --# Main
    -- Ttest
    
    displayMode(FULLSCREEN)
    
    function setup()
        print("Hello World!")
        circles = {}
        for i = 1, 7 do circles[i] = Circle(i, i * 100, 100) end
    end
    
    -- This function gets called once every frame
    function draw()
        -- This sets a dark background color 
        background(40, 40, 50)
    
        -- This sets the line thickness
        for i = 1, 7 do circles[i]:draw() end
    
        -- Do your drawing here
    end
    
    function touched(touch)
        for i = 1, 7 do circles[i]:touched(touch) end
    end
    
  • Thanks Mark let me try to go on that direction

  • Posts: 1,238

    Oh, and it would have probably been neater if I'd done the iteration in draw and touched using using for each. As in

    for i, circle in ipairs(circles) do circle:draw() end
    

    Good luck!

Sign In or Register to comment.