Skip to content

Commit

Permalink
Code clean-up, add tests and fill docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Insality committed Mar 9, 2025
1 parent 0ce99e7 commit 24209f7
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 39 deletions.
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@

- **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

### [Dependency](https://www.defold.com/manuals/libraries/)

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
Expand All @@ -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
Expand Down Expand Up @@ -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:**

Expand Down Expand Up @@ -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.
Expand All @@ -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:**
Expand All @@ -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.
Expand Down Expand Up @@ -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


</details>


Expand Down
2 changes: 1 addition & 1 deletion game.project
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bootstrap]
main_collection = /example/example.collectionc
main_collection = /test/test.collectionc

[script]
shared_state = 1
Expand Down
29 changes: 29 additions & 0 deletions test/test_tweener.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
70 changes: 40 additions & 30 deletions tweener/tweener.lua
Original file line number Diff line number Diff line change
@@ -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 = {}

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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


Expand All @@ -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
Expand Down

0 comments on commit 24209f7

Please # to comment.