Problem with the touch state

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

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.

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

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.

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.

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.

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.

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

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!