Skip to content

Commit

Permalink
Merge pull request #2 from Insality/develop
Browse files Browse the repository at this point in the history
Release v4
  • Loading branch information
Insality authored Nov 12, 2024
2 parents 8bfba34 + 6d17985 commit 5ceacb8
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 67 deletions.
67 changes: 46 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/3.zip)**
**[Tweener](https://github.com/Insality/defold-tweener/archive/refs/tags/4.zip)**

```
https://github.com/Insality/defold-tweener/archive/refs/tags/3.zip
https://github.com/Insality/defold-tweener/archive/refs/tags/4.zip
```

### Library Size
Expand All @@ -43,7 +43,7 @@ https://github.com/Insality/defold-tweener/archive/refs/tags/3.zip

### Global Update Frequency

Optionally, you can setup global default update freqency in your game.project. It's `60` by default.
Optionally, you can setup global default update frequency in your game.project. It's `60` by default.

Add next `tweener` section to your `game.project` in text mode:

Expand All @@ -57,7 +57,8 @@ update_frequency = 60
### Quick API Reference

```lua
local tween_function = tweener.linear or go.EASING_LINEAR or gui.EASING_LINEAR or {0, 0.2, 0.4, 0.8, 0.9, 1}
-- Tween function can be a string, a predefined easing function, or a custom easing function
local tween_function = "linear" or tweener.linear or go.EASING_LINEAR or gui.EASING_LINEAR or {0, 0.2, 0.4, 0.8, 0.9, 1}

tweener.tween(tween_function, from, to, time, callback, [dt])
tweener.ease(tween_function, from, to, time, time_elapsed)
Expand Down Expand Up @@ -92,7 +93,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:**
- `timer_id`: A timer id from `timer.delay` if you wish to cancel the tween.
- `tweener_id`: A tweener id from `timer.delay` if you wish to cancel the tween.

- **Usage Example:**

Expand All @@ -105,9 +106,12 @@ tweener.tween(go.EASING_OUTSINE, 0, 100, 1.5, function(value, is_final_call)
print("Tween value: " .. value)
end)

tweener.tween({0, 0.2, 0.4, 0.8, 0.9, 1}, 0, 100, 1.5, function(value, is_final_call)
local tween_id = tweener.tween({0, 0.2, 0.4, 0.8, 0.9, 1}, 0, 100, 1.5, function(value, is_final_call)
print("Tween value: " .. value)
end)

-- You can cancel the tween by calling tweener.cancel
tweener.cancel(tween_id)
```


Expand Down Expand Up @@ -139,6 +143,31 @@ local value = tweener.ease({0, 0.2, 0.4, 0.8, 0.9, 1}, 0, 100, 1.5, 0.75)
print("The tween value at halfway point is: ", value)
```


**tweener.cancel**
---
```lua
tweener.cancel(tweener_id)
```

This function cancels the tween with the given `tweener_id`. This calls `timer.cancel` internally.

- **Parameters:**
- `tweener_id`: The id of the tween to cancel.

- **Return Value:**
- `true` if the tween was successfully canceled, `false` otherwise.

- **Usage Example:**

```lua
local tween_id = tweener.tween(tweener.linear, 0, 100, 1.5, function(value, is_final_call)
print("Tween value: " .. value)
end)

tweener.cancel(tween_id)
```

These functions provide a flexible and powerful way to add animations and transitions to your projects, making them feel more dynamic and engaging. Enjoy animating with the **Tweener** module! *(Thanks, Mister ChatGPT)* 🙃


Expand Down Expand Up @@ -237,28 +266,24 @@ If you have any issues, questions or suggestions please [create an issue](https:


## Changelog

### **V1**
<details>
<summary><b>Changelog</b></summary>

- Initial release
</details>

### **V1**
- Initial release

### **V2**
<details>
<summary><b>Changelog</b></summary>

- Changed timer `delta time` to `socket.gettime` for more precise tweening
</details>

- Changed timer `delta time` to `socket.gettime` for more precise tweening

### **V3**
<details>
<summary><b>Changelog</b></summary>
- Added custom easings support

### **V4**
- Fix `update_frequency` game.project settings typo
- Now able to use string name of easing functions (ex. "linear", "insine", "outquad", etc.)
- Add `tweener.cancel` function to cancel tweening instead of `timer.cancel` (For better API and README)
- Code cleanup and better performance
- Fix if total time is 0, then callback will be called immediately

- Added custom easings support
</details>


Expand Down
7 changes: 0 additions & 7 deletions tweener/annotations_tweener.lua

This file was deleted.

106 changes: 67 additions & 39 deletions tweener/tweener.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
local UPDATE_FREQUENCY = sys.get_config_int("tweener.UPDATE_FREQUENCY", 60)
local UPDATE_FREQUENCY = sys.get_config_int("tweener.update_frequency", 60)

---@class tweener
local M = {}

---@alias easing_function (fun(current: number, from: number, to: number, time: number): number)|string|constant|number[]

---@class hash
---@class constant
---@class vector
Expand All @@ -10,60 +13,39 @@ local TYPE_TABLE = "table"
local TYPE_USERDATA = "userdata"


---@param easing number[]|vector @The array of easing values, Example: {0, 0.5, 1, 2, 1}. Must have at least two elements.
---@return easing_function @The easing function
local function get_custom_easing(easing)
local sample_count = #easing
if sample_count < 2 then
error("Easing table must have at least two elements.")
end

return function(t, b, c, d)
if d == 0 then
error("Division by zero: 'd' must not be zero.")
end

local time_progress = t / d
if time_progress >= 1 then
return (c + b) * easing[sample_count]
end
if time_progress <= 0 then
return b * easing[1]
end

local sample_index = sample_count - 1
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

return c * progress + b
end
end


---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
---@return hash @The created timer id
---@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)
update_delta_time = update_delta_time or (1 / UPDATE_FREQUENCY)

-- Acquire the easing function
easing_function = M.DEFOLD_EASINGS[easing_function] or easing_function
easing_function = M.DEFOLD_EASINGS[easing_function] or M[easing_function] or easing_function
local easing_type = type(easing_function)
if easing_type == TYPE_USERDATA or easing_type == TYPE_TABLE then
easing_function = get_custom_easing(easing_function --[[@as vector]])
---@cast easing_function number[]
local custom_easing = function(t, b, c, d)
return M.custom_ease(easing_function, t, b, c, d)
end

easing_function = custom_easing
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)
callback(to, true)
return
end

local current_time = socket.gettime()
time_elapsed = time_elapsed + (current_time - latest_time)
latest_time = current_time
Expand Down Expand Up @@ -92,15 +74,61 @@ function M.ease(easing_function, from, to, time, time_elapsed)
return to
end

easing_function = M.DEFOLD_EASINGS[easing_function] or easing_function
easing_function = M.DEFOLD_EASINGS[easing_function] or M[easing_function] or easing_function
local easing_type = type(easing_function)
if easing_type == TYPE_USERDATA or easing_type == TYPE_TABLE then
easing_function = get_custom_easing(easing_function --[[@as vector]])
---@cast easing_function number[]
return M.custom_ease(easing_function, time_elapsed, from, to - from, time)
end

return easing_function(time_elapsed, from, to - from, time)
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)
return timer.cancel(tween_id)
end


---@private
---@param easing number[] @The array of easing values, Example: {0, 0.5, 1, 2, 1}. Must have at least two elements.
---@param t number @current time [0 .. t - from]
---@param b number @beginning value
---@param c number @change in value
---@param d number @duration
---@return number @The result of easing
function M.custom_ease(easing, t, b, c, d)
if d == 0 then
error("Division by zero: 'd' must not be zero.")
end

local sample_count = #easing
if sample_count < 2 then
error("Easing table must have at least two elements.")
end

local time_progress = t / d
if time_progress >= 1 then
return (c + b) * easing[sample_count]
end
if time_progress <= 0 then
return b * easing[1]
end

local sample_index = sample_count - 1
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

return c * progress + b
end


--
-- Adapted from
-- Tweener's easing functions (Penner's Easing Equations)
Expand Down

0 comments on commit 5ceacb8

Please sign in to comment.