tween.loop.forever doesnt even start?

hello guys,
I want to fire the callback functions of my buttons at a certain FPS. The most clean solution for me, would be misusing tweens for this. But the tween doenst seem to even start. Why is that?

    -- Long press handler
    tween(.1, {}, {}, {loop = tween.loop.forever, easing = tween.easing.linear}, function()
        if self.press_timer and self.press_timer < ElapsedTime then
            self.callback()
        end
    end)

Complete code of the class

Button = class(Label)

function Button:init(params)
    self.callback = params.callback
    Label.init(self, params)
    
    -- Long press handler
    tween(.1, {}, {}, {loop = tween.loop.forever, easing = tween.easing.linear}, function()
        if self.press_timer and self.press_timer < ElapsedTime then
            self.callback()
        end
    end)
end

function Button:draw()
    fill(47, 96, 156, 255)
    rect(self.x, self.y, self.width, self.height)
    fill(187, 159, 132, 255)
    text(self.label, self.x, self.y)
end

function Button:touched(touch)
    if touch.x > self.x - self.width*.5
    and touch.x < self.x + self.width*.5
    and touch.y > self.y - self.height*.5
    and touch.y < self.y + self.height*.5 then
        
        self.press_timer = self.press_timer or (ElapsedTime + .25)
        
        if touch.state == ENDED then
            self.press_timer = nil
            self.callback()
        end
    
    else
        self.press_timer = nil
    end
end

My first thought is that it’s because you didn’t give the tween anything to do, ie no table and no values to change.

I’m not really what this is meant to do. So when the button is pressed, you want the callback to fire once every frame for 0.25 seconds, is that right?

If so, I would get rid of the tween altogether and add something to the Button:draw method (or add a separate Button:update method if you want to keep drawing and logic separate), like this:

if self.press_timer then 
   if ElapsedTime > self.press_timer then 
       self.press_timer = nil
   else
       self.callback()
    end
end

Callbacks only call once the tween has finished, so the callbacks of looping tweens (forever or pingpong) never fire (though it would be more useful if they fired at the end of each cycle)

not quite. if the button is ?tapped? I fire the callback once. But after .25 seconds the touch is considered to be a ?long press? in which case the callback must be fired every .5 seconds

I know i can solve it with timers only, but I wanted to have a clean draw function in this class…

@yojimbo2000 I assumed that callbacks are fired after each loop cycle… ahhh I see. good to know. thank you!

@Ignatz no I tried this long ago and it worked. The tween does not need any parameters to tween, actually.

here is the working code with timers for anyone interested in

Button = class(Label)

function Button:init(params)
    self.callback = params.callback
    self.frequency = .05
    Label.init(self, params)
end

function Button:draw()
    fill(47, 96, 156, 255)
    rect(self.x, self.y, self.width, self.height)
    fill(187, 159, 132, 255)
    text(self.label, self.x, self.y)
    
    -- Long press handler
    if self.press_timer and self.press_timer < ElapsedTime then
        if not self.cb_timer or (self.cb_timer and self.cb_timer < ElapsedTime) then
            self.cb_timer = ElapsedTime + self.frequency
            self.callback()
        end
    end
end

function Button:touched(touch)
    if touch.x > self.x - self.width*.5
    and touch.x < self.x + self.width*.5
    and touch.y > self.y - self.height*.5
    and touch.y < self.y + self.height*.5 then
        self.press_timer = self.press_timer or (ElapsedTime + .25)
        if touch.state == ENDED then
            self.press_timer = nil
            self.callback()
        end
    else
        self.press_timer = nil
    end
end

@se24vad You have loop=tween.loop.forever which means that the tween isn’t going to end, and the callback function isn’t going to execute.

@dave1707 thanks, @yojimbo2000 said this already.

@se24vad OK. Apparently I just looked at the problem, I didn’t read thru all of the responses.

@se24vad! I would know for your code Button = class(Label) - could I know what happens to Label?

sure you can - I will share my project with the community, later, anyway

(The whole thing will be a animation player/exporter for procreate)

Label = class()

function Label:init(params)
    font("SourceSansPro-Light")
    fontSize(30)
    
    self.label = params.label
    local label_width, label_height = textSize(self.label)
    self.x = params.x or 0
    self.y = params.y or 0
    self.width = params.width or label_width
    self.height = params.height or label_height
end

function Label:draw()
    fill(30, 27, 25, 255)
    text(self.label, self.x, self.y)
end

@TokOut - the Label class is inherited by Button.

I explained it here.

@se24vad , a looping timer is not difficult to implement with tweens: you just need to chain them together. Here is one possible implementation:

--# Main
-- Timer Demo

function setup()
    print("START at: " .. os.date())
    
    -- create a timer with 1.5 second delay
    t1 = Timer(1.5, function()
        print(ElapsedTime)
    end)
    t1:start({ loop = true, firstNoDelay = true })
    
    -- use another timer to stop the first one
    -- after 10 seconds
    t2 = Timer(10, function()
        t1:stop()
        print("DONE at: " .. os.date())
    end)
    t2:start()
end

--# Timer
Timer = class()

function Timer:init(delay, callback)
    self.delay = delay
    self.callback = callback
end

function Timer:start(options)
    local f
    f = function()
        self.callback()
        if options and options.loop then
            self.t = tween.delay(self.delay, f)
        end
    end
    if options and options.firstNoDelay then
        f()
    else
        self.t = tween.delay(self.delay, f)
    end
end

function Timer:stop()
    if self.t then 
        tween.stop(self.t)
        self.t = nil
    end
end

thank you for this example!

I did some progress on my app and wanted to share my exitement with you :blush: (code fallows shortly in a separate thread)

The idea was to make animations with #procreate and quickly use them as assets inside #codea games. But procreate doesn’t have any animation tools. - This app allows you to import your procreate artwork and project animation frames onto it. When you are done refining your artwork and satisfied with the animation, you can simply export a clean spritesheet and use it inside your game engine.

https://youtu.be/J_DLhatQEH0
https://youtu.be/gxFSUC4nQv4

Nice work! I love hand drawn animations.