From e9a09d4496bbe98d4d47c02e39bce531f426f46a Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 22 Dec 2024 13:55:12 +0200 Subject: [PATCH 1/6] Add time as 1 by default --- tweener/tweener.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tweener/tweener.lua b/tweener/tweener.lua index 409b1d4..eb5e12f 100644 --- a/tweener/tweener.lua +++ b/tweener/tweener.lua @@ -14,14 +14,15 @@ local TYPE_USERDATA = "userdata" ---Starts a tweening operation. ----@param easing_function easing_function ----@param from number ----@param to number ----@param time number ----@param callback fun(value: number, is_end: boolean) ----@param update_delta_time number|nil @Default is 1/60, the time between updates +---@param easing_function easing_function The easing function to use +---@param from number The starting value to tween from +---@param to number The target value to tween to +---@param time number|nil The duration of the tween in seconds, default is 1 +---@param callback fun(value: number, is_end: boolean) The callback function to call on each update +---@param update_delta_time number|nil Default is 1/60, the time between updates ---@return hash timer_id The created timer id, you can cancel a tween by calling timer.cancel(timer_id) function M.tween(easing_function, from, to, time, callback, update_delta_time) + time = time or 1 update_delta_time = update_delta_time or (1 / UPDATE_FREQUENCY) -- Acquire the easing function @@ -66,10 +67,11 @@ end ---@param easing_function easing_function ---@param from number ---@param to number ----@param time number +---@param time number|nil The duration of the tween in seconds, default is 1 ---@param time_elapsed number @current time [0 .. t - from] ---@return number @The result of easing function M.ease(easing_function, from, to, time, time_elapsed) + time = time or 1 if time == 0 then return to end From 19be386f3c4bff10c002e0dc16663836ab36a852 Mon Sep 17 00:00:00 2001 From: Steven McDade Date: Fri, 31 Jan 2025 13:54:46 +0000 Subject: [PATCH 2/6] Added pause, resume and exists to control tweens --- tweener/tweener.lua | 54 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/tweener/tweener.lua b/tweener/tweener.lua index 60cc927..aa828c2 100644 --- a/tweener/tweener.lua +++ b/tweener/tweener.lua @@ -12,6 +12,7 @@ local M = {} local TYPE_TABLE = "table" local TYPE_USERDATA = "userdata" +local current_tweens = {} ---Starts a tweening operation. ---@param easing_function easing_function @@ -37,28 +38,39 @@ function M.tween(easing_function, from, to, time, callback, update_delta_time) end local time_elapsed = 0 + local latest_time = socket.gettime() local timer_id = timer.delay(update_delta_time, true, function(_, handle, dt) + if time <= 0 then timer.cancel(handle) + current_tweens[handle] = nil callback(to, true) return end local current_time = socket.gettime() - time_elapsed = time_elapsed + (current_time - latest_time) + + if current_tweens[handle]["paused"] == false then + time_elapsed = time_elapsed + (current_time - latest_time) + end latest_time = current_time if time_elapsed >= time then timer.cancel(handle) + current_tweens[handle] = nil callback(easing_function(time, from, to - from, time), true) return end - callback(easing_function(time_elapsed, from, to - from, time), false) + if current_tweens[handle]["paused"] == false then + callback(easing_function(time_elapsed, from, to - from, time), false) + end end) + current_tweens[timer_id] = { ["paused"] = false } + return timer_id end @@ -85,13 +97,51 @@ function M.ease(easing_function, from, to, time, time_elapsed) end +---Check if a tween exists and is running. +---@param tween_id hash the tween handle returned by `tween` function +---@return boolean true if the tween is active, false if the tween doesn't exist +function M.exists(tween_id) + return current_tweens[tween_id] ~= nil +end + ---Cancel a previous running tween. ---@param tween_id hash the tween handle returned by `tween` function ---@return boolean true if the tween was active, false if the tween is already cancelled / complete function M.cancel(tween_id) + if current_tweens[tween_id] ~= false then + current_tweens[tween_id] = nil + end return timer.cancel(tween_id) end +---Pause a running tween. +---@param tween_id hash the tween handle returned by `tween` function +---@return boolean true if the tween was active and could be paused, false if the tween doesn't exist or already paused +function M.pause(tween_id) + if current_tweens[tween_id] == nil then + return false + end + if current_tweens[tween_id]["paused"] == true then + return false + end + current_tweens[tween_id]["paused"] = true + return true +end + +---Resume a running tween. +---@param tween_id hash the tween handle returned by `tween` function +---@return boolean true if the tween was active and could be resumed, false if the tween doesn't exist or playing +function M.resume(tween_id) + if current_tweens[tween_id] == nil then + return false + end + if current_tweens[tween_id]["paused"] == false then + return false + end + current_tweens[tween_id]["paused"] = false + return true +end + ---@private ---@param easing number[] @The array of easing values, Example: {0, 0.5, 1, 2, 1}. Must have at least two elements. From d400feb5ab3b13738e5ec81a2b891360f573e9e0 Mon Sep 17 00:00:00 2001 From: Steven McDade Date: Mon, 10 Feb 2025 12:22:59 +0000 Subject: [PATCH 3/6] Using a table for timer_id --- .editor_settings | 52 ++++++++++++++++++++++++++++++++++ tweener/tweener.lua | 68 +++++++++++++++------------------------------ 2 files changed, 75 insertions(+), 45 deletions(-) create mode 100644 .editor_settings diff --git a/.editor_settings b/.editor_settings new file mode 100644 index 0000000..2db12e7 --- /dev/null +++ b/.editor_settings @@ -0,0 +1,52 @@ +{ + :bundle { + :android { + :architecture { + :arm64-android true + :armv7-android true + } + :format "apk" + :install true + :key-pass "/Users/stevenmcdade/Documents/GitHub/de_Trivia/triviakeystorepass.txt" + :keystore "/Users/stevenmcdade/Documents/GitHub/godot_keystores/Trivia/triviaguyjustquiz.keystore" + :keystore-pass "/Users/stevenmcdade/Documents/GitHub/de_Trivia/triviakeystorepass.txt" + :launch true + } + :build-report false + :contentless false + :debug-symbols true + :ios { + :architecture { + :arm64-ios true + :x86_64-ios false + } + :code-signing-identity "Apple Development: Steven McDade (G75UA87ZS9)" + :install true + :launch true + :provisioning-profile "/Users/stevenmcdade/Desktop/embedded_trivia.mobileprovision" + :sign true + } + :liveupdate false + :texture-compression "enabled" + :variant "debug" + } + :code { + :find { + :replacement "apex_panel" + :term "type" + } + } + :search-in-files { + :exts "" + :include-libraries true + :term "random" + } + :workflow { + :recent-files [ + [ + "/tweener/tweener.lua" + :code + ] + ] + } +} \ No newline at end of file diff --git a/tweener/tweener.lua b/tweener/tweener.lua index aa828c2..71af151 100644 --- a/tweener/tweener.lua +++ b/tweener/tweener.lua @@ -12,8 +12,6 @@ local M = {} local TYPE_TABLE = "table" local TYPE_USERDATA = "userdata" -local current_tweens = {} - ---Starts a tweening operation. ---@param easing_function easing_function ---@param from number @@ -21,7 +19,7 @@ local current_tweens = {} ---@param time number ---@param callback fun(value: number, is_end: boolean) ---@param update_delta_time number|nil @Default is 1/60, the time between updates ----@return hash timer_id The created timer id, you can cancel a tween by calling timer.cancel(timer_id) +---@return table timer_id The created timer information, you can cancel a tween by calling tweener.cancel(timer_id) function M.tween(easing_function, from, to, time, callback, update_delta_time) update_delta_time = update_delta_time or (1 / UPDATE_FREQUENCY) @@ -38,40 +36,36 @@ function M.tween(easing_function, from, to, time, callback, update_delta_time) end local time_elapsed = 0 - local latest_time = socket.gettime() + local timer_table = { id = nil, is_paused = false } - local timer_id = timer.delay(update_delta_time, true, function(_, handle, dt) + timer_table.id = timer.delay(update_delta_time, true, function(_, handle, dt) if time <= 0 then - timer.cancel(handle) - current_tweens[handle] = nil + timer.cancel(timer_table.id) callback(to, true) return end local current_time = socket.gettime() - if current_tweens[handle]["paused"] == false then + if timer_table.is_paused == true then time_elapsed = time_elapsed + (current_time - latest_time) end latest_time = current_time if time_elapsed >= time then - timer.cancel(handle) - current_tweens[handle] = nil + timer.cancel(timer_table.id) callback(easing_function(time, from, to - from, time), true) return end - if current_tweens[handle]["paused"] == false then + if timer_table.is_paused == false then callback(easing_function(time_elapsed, from, to - from, time), false) end end) - current_tweens[timer_id] = { ["paused"] = false } - - return timer_id + return timer_table end @@ -98,48 +92,32 @@ end ---Check if a tween exists and is running. ----@param tween_id hash the tween handle returned by `tween` function +---@param tween_id table the tween handle returned by `tween` function ---@return boolean true if the tween is active, false if the tween doesn't exist function M.exists(tween_id) - return current_tweens[tween_id] ~= nil + return tween_id.id ~= nil end ---Cancel a previous running tween. ----@param tween_id hash the tween handle returned by `tween` function +---@param tween_id table the tween handle returned by `tween` function ---@return boolean true if the tween was active, false if the tween is already cancelled / complete function M.cancel(tween_id) - if current_tweens[tween_id] ~= false then - current_tweens[tween_id] = nil - end - return timer.cancel(tween_id) + return timer.cancel(tween_id.id) +end + +---Check if a tween exists and is paused. +---@param tween_id table the tween handle returned by `tween` function +---@return boolean true if the tween is active and paused, false if the tween is running, nil if the tween doesn't exist +function M.is_paused(tween_id) + return tween_id.is_paused end ----Pause a running tween. ----@param tween_id hash the tween handle returned by `tween` function +---Toggles pause on a running tween. +---@param tween_id table the tween handle returned by `tween` function ---@return boolean true if the tween was active and could be paused, false if the tween doesn't exist or already paused function M.pause(tween_id) - if current_tweens[tween_id] == nil then - return false - end - if current_tweens[tween_id]["paused"] == true then - return false - end - current_tweens[tween_id]["paused"] = true - return true -end - ----Resume a running tween. ----@param tween_id hash the tween handle returned by `tween` function ----@return boolean true if the tween was active and could be resumed, false if the tween doesn't exist or playing -function M.resume(tween_id) - if current_tweens[tween_id] == nil then - return false - end - if current_tweens[tween_id]["paused"] == false then - return false - end - current_tweens[tween_id]["paused"] = false - return true + tween_id.is_paused = not tween_id.is_paused + return tween_id.is_paused end From 92cb4ee520e4a0571814eefb420593583f7cc0ba Mon Sep 17 00:00:00 2001 From: Steven McDade Date: Mon, 17 Feb 2025 11:19:30 +0000 Subject: [PATCH 4/6] Suggestion changes, including timer_state rename --- .editor_settings | 52 ------------------------------------------- .vscode/settings.json | 15 ------------- tweener/tweener.lua | 46 +++++++++++++++++++------------------- 3 files changed, 23 insertions(+), 90 deletions(-) delete mode 100644 .editor_settings delete mode 100644 .vscode/settings.json diff --git a/.editor_settings b/.editor_settings deleted file mode 100644 index 2db12e7..0000000 --- a/.editor_settings +++ /dev/null @@ -1,52 +0,0 @@ -{ - :bundle { - :android { - :architecture { - :arm64-android true - :armv7-android true - } - :format "apk" - :install true - :key-pass "/Users/stevenmcdade/Documents/GitHub/de_Trivia/triviakeystorepass.txt" - :keystore "/Users/stevenmcdade/Documents/GitHub/godot_keystores/Trivia/triviaguyjustquiz.keystore" - :keystore-pass "/Users/stevenmcdade/Documents/GitHub/de_Trivia/triviakeystorepass.txt" - :launch true - } - :build-report false - :contentless false - :debug-symbols true - :ios { - :architecture { - :arm64-ios true - :x86_64-ios false - } - :code-signing-identity "Apple Development: Steven McDade (G75UA87ZS9)" - :install true - :launch true - :provisioning-profile "/Users/stevenmcdade/Desktop/embedded_trivia.mobileprovision" - :sign true - } - :liveupdate false - :texture-compression "enabled" - :variant "debug" - } - :code { - :find { - :replacement "apex_panel" - :term "type" - } - } - :search-in-files { - :exts "" - :include-libraries true - :term "random" - } - :workflow { - :recent-files [ - [ - "/tweener/tweener.lua" - :code - ] - ] - } -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index f77ddf1..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Lua.diagnostics.globals": [ - "tweener", - "describe", - "it", - "sys", - "init", - "timer", - "socket", - "go", - "before", - "gui", - "vmath" - ] -} \ No newline at end of file diff --git a/tweener/tweener.lua b/tweener/tweener.lua index 71af151..ed4bb27 100644 --- a/tweener/tweener.lua +++ b/tweener/tweener.lua @@ -19,7 +19,7 @@ local TYPE_USERDATA = "userdata" ---@param time number ---@param callback fun(value: number, is_end: boolean) ---@param update_delta_time number|nil @Default is 1/60, the time between updates ----@return table timer_id The created timer information, you can cancel a tween by calling tweener.cancel(timer_id) +---@return table timer_state The created timer information, you can cancel a tween by calling tweener.cancel(timer_state) function M.tween(easing_function, from, to, time, callback, update_delta_time) update_delta_time = update_delta_time or (1 / UPDATE_FREQUENCY) @@ -37,35 +37,36 @@ function M.tween(easing_function, from, to, time, callback, update_delta_time) local time_elapsed = 0 local latest_time = socket.gettime() - local timer_table = { id = nil, is_paused = false } + local timer_state = { timer_id = nil, is_paused = false } - timer_table.id = timer.delay(update_delta_time, true, function(_, handle, dt) + timer_state.timer_id = timer.delay(update_delta_time, true, function(_, handle, dt) if time <= 0 then - timer.cancel(timer_table.id) + timer.cancel(timer_state.timer_id) callback(to, true) return end local current_time = socket.gettime() - if timer_table.is_paused == true then + if timer_state.is_paused == true then time_elapsed = time_elapsed + (current_time - latest_time) end latest_time = current_time if time_elapsed >= time then - timer.cancel(timer_table.id) + timer.cancel(timer_state.timer_id) + timer_state.timer_id = nil callback(easing_function(time, from, to - from, time), true) return end - if timer_table.is_paused == false then + if timer_state.is_paused == false then callback(easing_function(time_elapsed, from, to - from, time), false) end end) - return timer_table + return timer_state end @@ -90,34 +91,33 @@ function M.ease(easing_function, from, to, time, time_elapsed) return easing_function(time_elapsed, from, to - from, time) end - ---Check if a tween exists and is running. ----@param tween_id table the tween handle returned by `tween` function +---@param timer_state table the tween handle returned by `tween` function ---@return boolean true if the tween is active, false if the tween doesn't exist -function M.exists(tween_id) - return tween_id.id ~= nil +function M.is_exists(timer_state) + return timer_state.timer_id ~= nil end ---Cancel a previous running tween. ----@param tween_id table the tween handle returned by `tween` function +---@param timer_state table the tween handle returned by `tween` function ---@return boolean true if the tween was active, false if the tween is already cancelled / complete -function M.cancel(tween_id) - return timer.cancel(tween_id.id) +function M.cancel(timer_state) + return timer.cancel(timer_state.timer_id) end ---Check if a tween exists and is paused. ----@param tween_id table the tween handle returned by `tween` function +---@param timer_state table the tween handle returned by `tween` function ---@return boolean true if the tween is active and paused, false if the tween is running, nil if the tween doesn't exist -function M.is_paused(tween_id) - return tween_id.is_paused +function M.is_paused(timer_state) + return timer_state.is_paused end ----Toggles pause on a running tween. ----@param tween_id table the tween handle returned by `tween` function +---Sets the pause on a running tween. +---@param timer_state table the tween handle returned by `tween` function ---@return boolean true if the tween was active and could be paused, false if the tween doesn't exist or already paused -function M.pause(tween_id) - tween_id.is_paused = not tween_id.is_paused - return tween_id.is_paused +function M.set_pause(timer_state, is_paused) + timer_state.is_paused = is_paused + return timer_state.is_paused end From 0ce99e7e877d4037aeb348922fc1e078b0c389f8 Mon Sep 17 00:00:00 2001 From: Insality Date: Fri, 7 Mar 2025 23:17:58 +0200 Subject: [PATCH 5/6] Code clean --- tweener/tweener.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tweener/tweener.lua b/tweener/tweener.lua index 3afa61a..3a2f41b 100644 --- a/tweener/tweener.lua +++ b/tweener/tweener.lua @@ -12,6 +12,10 @@ local M = {} local TYPE_TABLE = "table" local TYPE_USERDATA = "userdata" +-- Local versions +local math_floor = math.floor +local math_min = math.min + ---Starts a tweening operation. ---@param easing_function easing_function The easing function to use ---@param from number The starting value to tween from @@ -26,6 +30,8 @@ function M.tween(easing_function, from, to, time, callback, update_delta_time) -- Acquire the easing function easing_function = M.DEFOLD_EASINGS[easing_function] or M[easing_function] or easing_function + + -- Check custom easings local easing_type = type(easing_function) if easing_type == TYPE_USERDATA or easing_type == TYPE_TABLE then local number_values = easing_function --[[ @as number[] ]] @@ -131,10 +137,9 @@ end ---Sets the pause on a running tween. ---@param timer_state tween the tween handle returned by `tween` function ----@return boolean true if the tween was active and could be paused, false if the tween doesn't exist or already paused +---@param is_paused boolean the tween paused state function M.set_pause(timer_state, is_paused) timer_state.is_paused = is_paused - return timer_state.is_paused end @@ -164,8 +169,8 @@ function M.custom_ease(easing, t, b, c, d) end local sample_index = sample_count - 1 - local index1 = math.floor(time_progress * sample_index) - local index2 = math.min(index1 + 1, sample_index) + local index1 = math_floor(time_progress * sample_index) + local index2 = math_min(index1 + 1, sample_index) local diff = (time_progress - index1 * (1 / sample_index)) * sample_index local progress = easing[index1 + 1] * (1.0 - diff) + easing[index2 + 1] * diff From 24209f7ffb9b24755f9c84a94ca2ac95f1cb45d3 Mon Sep 17 00:00:00 2001 From: Insality Date: Sun, 9 Mar 2025 17:11:54 +0100 Subject: [PATCH 6/6] Code clean-up, add tests and fill docs --- README.md | 24 ++++++++++----- game.project | 2 +- test/test_tweener.lua | 29 ++++++++++++++++++ tweener/tweener.lua | 70 ++++++++++++++++++++++++------------------- 4 files changed, 86 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 55c37e9..d7e99d0 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ - **Tweening**: Create tweens for any action your want. - **Easing Functions**: Provides a set of easing functions for different types of easings. -- **Custom Update Frequency**: Option to define update frequency for the tween. - **Callbacks**: Callbacks for each tween update. - **Custom Easings**: Support for custom easing functions. +- **Custom Update Frequency**: Option to define update frequency for the tween. ## Setup @@ -25,10 +25,10 @@ Open your `game.project` file and add the following line to the dependencies field under the project section: -**[Tweener](https://github.com/Insality/defold-tweener/archive/refs/tags/4.zip)** +**[Tweener](https://github.com/Insality/defold-tweener/archive/refs/tags/5.zip)** ``` -https://github.com/Insality/defold-tweener/archive/refs/tags/4.zip +https://github.com/Insality/defold-tweener/archive/refs/tags/5.zip ``` ### Library Size @@ -41,10 +41,12 @@ https://github.com/Insality/defold-tweener/archive/refs/tags/4.zip | Desktop / Mobile | **6.21 KB** | -### Global Update Frequency +### Global Update Frequency [Optional] Optionally, you can setup global default update frequency in your game.project. It's `60` by default. +This is a global settings for the tweener. You can specify the update frequence parameter in the `tweener.tween` function for special tween operation. + Add next `tweener` section to your `game.project` in text mode: ```ini @@ -98,7 +100,7 @@ This function initiates a tween operation immediately. Here's how to use it: - `dt` (optional): The time interval for updating the tween, in seconds. - **Return Value:** - - `tween`: A tween object. You can use it to cancel the tween, check if it exists, or pause it. + - `tween`: A tween object. You can use it to cancel the tween, check if it still running, or pause it. - **Usage Example:** @@ -183,7 +185,7 @@ tweener.is_paused(tween) This function returns `true` if the tween is paused, `false` otherwise. - **Parameters:** - - `tween`: The tween object to check. + - `tween`: The tween object to check. It returned from `tweener.tween` function. - **Return Value:** - `true` if the tween is paused, `false` otherwise. @@ -198,7 +200,7 @@ tweener.set_pause(tween, is_paused) This function sets the pause state of the tween. - **Parameters:** - - `tween`: The tween object to set the pause state. + - `tween`: The tween object to set the pause state. It returned from `tweener.tween` function. - `is_paused`: The new pause state. - **Return Value:** @@ -225,7 +227,7 @@ tweener.is_active(tween) This function returns `true` if the tween is running, `false` is the tween is finished. - **Parameters:** - - `tween`: The tween object to check. + - `tween`: The tween object to check. It returned from `tweener.tween` function. - **Return Value:** - `true` if the tween is running, `false` is the tween is finished. @@ -355,6 +357,12 @@ If you have any issues, questions or suggestions please [create an issue](https: - Code cleanup and better performance - Fix if total time is 0, then callback will be called immediately +### **V5** +- [Breaking]: `tweener.tween` now returns a tween object instead of a timer id. So if you used `timer.cancel` to cancel the tween, you need to use `tweener.cancel` instead. +- Added `tweener.is_paused` function to check if a tween is paused +- Added `tweener.is_active` function to check if a tween is active + + diff --git a/game.project b/game.project index 0b40c98..355841c 100644 --- a/game.project +++ b/game.project @@ -1,5 +1,5 @@ [bootstrap] -main_collection = /example/example.collectionc +main_collection = /test/test.collectionc [script] shared_state = 1 diff --git a/test/test_tweener.lua b/test/test_tweener.lua index abc4e74..fb0ffc6 100644 --- a/test/test_tweener.lua +++ b/test/test_tweener.lua @@ -83,7 +83,36 @@ return function() assert(tweener.ease(go.EASING_OUTQUART, 0, 1, 1, 0) < epsilon) assert(tweener.ease(go.EASING_OUTQUINT, 0, 1, 1, 0) < epsilon) assert(tweener.ease(go.EASING_OUTSINE, 0, 1, 1, 0) < epsilon) + end) + + it("Tween can be cancelled", function() + local tween = tweener.tween(go.EASING_LINEAR, 0, 100, 1, function() end) + assert(tweener.is_active(tween)) + tweener.cancel(tween) + assert(not tweener.is_active(tween)) + end) + + it("Tween can be paused", function() + local tween = tweener.tween(go.EASING_LINEAR, 0, 100, 1, function() end) + assert(tweener.is_active(tween)) + tweener.set_pause(tween, true) + assert(tweener.is_paused(tween)) + end) + + it("Tween can be resumed", function() + local tween = tweener.tween(go.EASING_LINEAR, 0, 100, 1, function() end) + assert(tweener.is_active(tween)) + tweener.set_pause(tween, true) + assert(tweener.is_paused(tween)) + tweener.set_pause(tween, false) + assert(not tweener.is_paused(tween)) + end) + it("Tween can be checked if it is active", function() + local tween = tweener.tween(go.EASING_LINEAR, 0, 100, 1, function() end) + assert(tweener.is_active(tween)) + tweener.cancel(tween) + assert(not tweener.is_active(tween)) end) end) end diff --git a/tweener/tweener.lua b/tweener/tweener.lua index 3a2f41b..e5bb7cf 100644 --- a/tweener/tweener.lua +++ b/tweener/tweener.lua @@ -1,9 +1,11 @@ local UPDATE_FREQUENCY = sys.get_config_int("tweener.update_frequency", 60) +---Describe a struct of tween object returned by the `tween` function ---@class tween ---@field timer_id number The timer id handle from the `timer.delay` function ---@field is_paused boolean Whether the tween is paused +---A tweener module to manage tweening operations ---@class tweener local M = {} @@ -16,7 +18,7 @@ local TYPE_USERDATA = "userdata" local math_floor = math.floor local math_min = math.min ----Starts a tweening operation. +---Starts a tweening operation. Return a tween object to manage the tween. ---@param easing_function easing_function The easing function to use ---@param from number The starting value to tween from ---@param to number The target value to tween to @@ -42,44 +44,52 @@ function M.tween(easing_function, from, to, time, callback, update_delta_time) easing_function = custom_easing end + -- Create the tween object local time_elapsed = 0 local latest_time = socket.gettime() - local timer_state = { + local tween = { timer_id = nil, is_paused = false } - timer_state.timer_id = timer.delay(update_delta_time, true, function(_, handle, dt) - if timer_state.is_paused then + -- Create the timer + tween.timer_id = timer.delay(update_delta_time, true, function(_, handle, dt) + -- If the tween is paused, update the latest time and return + if tween.is_paused then latest_time = socket.gettime() return end + -- Cancel the tween if the time is zero from the start if time <= 0 then - timer.cancel(timer_state.timer_id) + M.cancel(tween) callback(to, true) return end + -- Update the time elapsed local current_time = socket.gettime() - time_elapsed = time_elapsed + (current_time - latest_time) latest_time = current_time + -- If the tween is finished, cancel it and call the callback if time_elapsed >= time then - timer.cancel(timer_state.timer_id) - timer_state.timer_id = nil - callback(easing_function(time, from, to - from, time), true) + M.cancel(tween) + local value = easing_function(time, from, to - from, time) + callback(value, true) return end - callback(easing_function(time_elapsed, from, to - from, time), false) + -- Update the tween and call the callback + local value = easing_function(time_elapsed, from, to - from, time) + callback(value, false) end) - return timer_state + return tween end +---Returns the result of an easing function. ---@param easing_function easing_function ---@param from number ---@param to number @@ -105,41 +115,41 @@ end ---Check if a tween exists ----@param timer_state tween the tween handle returned by `tween` function +---@param tween tween the tween handle returned by `tween` function ---@return boolean true if the tween is active, false if the tween finished -function M.is_active(timer_state) - return timer_state.timer_id ~= nil +function M.is_active(tween) + return tween.timer_id ~= nil end ---Cancel a previous running tween. ----@param timer_state tween the tween handle returned by `tween` function +---@param tween tween the tween handle returned by `tween` function ---@return boolean true if the tween was active, false if the tween is already cancelled / complete -function M.cancel(timer_state) - if not timer_state.timer_id then +function M.cancel(tween) + if not tween.timer_id then return false end - timer.cancel(timer_state.timer_id) - timer_state.timer_id = nil - timer_state.is_paused = false + timer.cancel(tween.timer_id) + tween.timer_id = nil + tween.is_paused = false return true end ---Check if a tween is paused ----@param timer_state tween the tween handle returned by `tween` function +---@param tween tween the tween handle returned by `tween` function ---@return boolean is_paused true if the tween is active and paused, false if the tween is running -function M.is_paused(timer_state) - return timer_state.is_paused +function M.is_paused(tween) + return tween.is_paused end ---Sets the pause on a running tween. ----@param timer_state tween the tween handle returned by `tween` function +---@param tween tween the tween handle returned by `tween` function ---@param is_paused boolean the tween paused state -function M.set_pause(timer_state, is_paused) - timer_state.is_paused = is_paused +function M.set_pause(tween, is_paused) + tween.is_paused = is_paused end @@ -161,11 +171,11 @@ function M.custom_ease(easing, t, b, c, d) end local time_progress = t / d - if time_progress >= 1 then - return c * easing[sample_count] + b - end if time_progress <= 0 then - return b + (c * easing[1]) + return b + c * easing[1] + end + if time_progress >= 1 then + return b + c * easing[sample_count] end local sample_index = sample_count - 1