Added a sample sound and some config, added tween to the src
This commit is contained in:
parent
01dec4e5c1
commit
d6143951d8
12 changed files with 960 additions and 44 deletions
239
tween/tween.odin
Normal file
239
tween/tween.odin
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package tween
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:math/ease"
|
||||
import "core:time"
|
||||
|
||||
TweenContext :: struct($T: typeid) {
|
||||
tweens: [dynamic]Tween(T),
|
||||
}
|
||||
|
||||
Tween :: struct($T: typeid) {
|
||||
value: ^T,
|
||||
start: T,
|
||||
diff: T,
|
||||
goal: T,
|
||||
delay: f64, // in seconds
|
||||
duration: time.Duration,
|
||||
progress: f64,
|
||||
rate: f64,
|
||||
type: ease.Ease,
|
||||
inited: bool,
|
||||
id: int,
|
||||
|
||||
// callbacks, data can be set, will be pushed to callback
|
||||
data: rawptr, // by default gets set to value input
|
||||
on_start: proc(ctx: ^TweenContext(T), data: rawptr),
|
||||
on_update: proc(ctx: ^TweenContext(T), data: rawptr),
|
||||
on_complete: proc(ctx: ^TweenContext(T), data: rawptr),
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
context_init_f :: proc(
|
||||
$T: typeid,
|
||||
) -> TweenContext(T) where intrinsics.type_is_float(T) {
|
||||
return {tweens = make([dynamic]Tween(T))}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
context_init_v :: proc(
|
||||
$T: typeid/[$N]$E,
|
||||
) -> TweenContext(T) where intrinsics.type_is_float(E) {
|
||||
return {tweens = make([dynamic]Tween(T))}
|
||||
}
|
||||
|
||||
context_init :: proc {
|
||||
context_init_f,
|
||||
context_init_v,
|
||||
}
|
||||
|
||||
// delete map content
|
||||
context_destroy :: proc(ctx: TweenContext($T)) {
|
||||
delete(ctx.tweens)
|
||||
}
|
||||
|
||||
// clear map content, stops all animations
|
||||
context_clear :: proc(ctx: ^TweenContext($T)) {
|
||||
clear(&ctx.tweens)
|
||||
}
|
||||
|
||||
// append / overwrite existing tween value to parameters
|
||||
// rest is initialized in tween_init, inside update
|
||||
// return value can be used to set callbacks
|
||||
@(require_results)
|
||||
tween_to :: proc(
|
||||
ctx: ^TweenContext($T),
|
||||
value: ^T,
|
||||
goal: T,
|
||||
type: ease.Ease = .Quadratic_Out,
|
||||
duration: time.Duration = time.Second,
|
||||
delay: f64 = 0,
|
||||
id: int = 0,
|
||||
) -> (
|
||||
tween: ^Tween(T),
|
||||
) {
|
||||
append(&ctx.tweens, Tween(T){})
|
||||
n := len(ctx.tweens) - 1
|
||||
tween = &ctx.tweens[n]
|
||||
|
||||
tween^ = {
|
||||
value = value,
|
||||
goal = goal,
|
||||
duration = duration,
|
||||
delay = delay,
|
||||
type = type,
|
||||
data = value,
|
||||
id = id,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// init internal properties
|
||||
_tween_init :: proc(tween: ^Tween($T), duration: time.Duration) {
|
||||
tween.inited = true
|
||||
tween.start = tween.value^
|
||||
tween.diff = tween.goal - tween.value^
|
||||
s := time.duration_seconds(duration)
|
||||
tween.rate = duration > 0 ? 1.0 / s : 0
|
||||
tween.progress = duration > 0 ? 0 : 1
|
||||
}
|
||||
|
||||
_tween_init_f :: proc(
|
||||
tween: ^Tween($T),
|
||||
duration: time.Duration,
|
||||
) where intrinsics.type_is_float(T) {
|
||||
_tween_init(tween, duration)
|
||||
}
|
||||
|
||||
_tween_init_v :: proc(
|
||||
tween: ^Tween($T/[$N]$E),
|
||||
duration: time.Duration,
|
||||
) where intrinsics.type_is_float(E) {
|
||||
_tween_init(tween, duration)
|
||||
}
|
||||
|
||||
tween_init :: proc {
|
||||
_tween_init_f,
|
||||
_tween_init_v,
|
||||
}
|
||||
|
||||
_tween_interpolate :: proc(tween: ^Tween($T), dt, delay_remainder: f64) {
|
||||
tween.progress += tween.rate * (dt + delay_remainder)
|
||||
x := tween.progress >= 1 ? 1 : ease.ease(tween.type, tween.progress)
|
||||
tween.value^ = tween.start + tween.diff * T(x)
|
||||
}
|
||||
|
||||
tween_interpolate_f :: proc(
|
||||
tween: ^Tween($T),
|
||||
dt, delay_remainder: f64,
|
||||
) where intrinsics.type_is_float(T) {
|
||||
_tween_interpolate(tween, dt, delay_remainder)
|
||||
}
|
||||
|
||||
tween_interpolate_v :: proc(
|
||||
tween: ^Tween($T/[$N]$E),
|
||||
dt, delay_remainder: f64,
|
||||
) where intrinsics.type_is_float(E) {
|
||||
_tween_interpolate(tween, dt, delay_remainder)
|
||||
}
|
||||
|
||||
tween_interpolate :: proc {
|
||||
tween_interpolate_f,
|
||||
tween_interpolate_v,
|
||||
}
|
||||
|
||||
// update all tweens, wait for their delay if one exists
|
||||
// calls callbacks in all stages, when they're filled
|
||||
// deletes tween from the map after completion
|
||||
_tween_update :: proc(ctx: ^TweenContext($T), dt: f64) {
|
||||
for &tween in ctx.tweens {
|
||||
delay_remainder := f64(0)
|
||||
|
||||
// Update delay if necessary.
|
||||
if tween.delay > 0 {
|
||||
tween.delay -= dt
|
||||
|
||||
if tween.delay < 0 {
|
||||
// We finished the delay, but in doing so consumed part of this frame's `dt` budget.
|
||||
// Keep track of it so we can apply it to this tween without affecting others.
|
||||
delay_remainder = tween.delay
|
||||
// We're done with this delay.
|
||||
tween.delay = 0
|
||||
}
|
||||
}
|
||||
|
||||
// We either had no delay, or the delay has been consumed.
|
||||
if tween.delay <= 0 {
|
||||
if !tween.inited {
|
||||
tween_init(&tween, tween.duration)
|
||||
|
||||
if tween.on_start != nil {
|
||||
tween.on_start(ctx, tween.data)
|
||||
}
|
||||
}
|
||||
|
||||
// If part of the `dt` budget was consumed this frame, then `delay_remainder` will be
|
||||
// that remainder, a negative value. Adding it to `dt` applies what's left of the `dt`
|
||||
// to the tween so it advances properly, instead of too much or little.
|
||||
tween_interpolate(&tween, dt, delay_remainder)
|
||||
// tween.progress += tween.rate * (dt + delay_remainder)
|
||||
// x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress)
|
||||
// tween.value^ = tween.start + tween.diff * T(x)
|
||||
|
||||
if tween.on_update != nil {
|
||||
tween.on_update(ctx, tween.data)
|
||||
}
|
||||
|
||||
if tween.progress >= 1 {
|
||||
// append keys to array that will be deleted after the loop
|
||||
|
||||
if tween.on_complete != nil {
|
||||
tween.on_complete(ctx, tween.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tween_update_f :: proc(ctx: ^TweenContext($T), dt: f64) where intrinsics.type_is_float(T) {
|
||||
_tween_update(ctx, dt)
|
||||
}
|
||||
|
||||
tween_update_v :: proc(ctx: ^TweenContext($T/[$N]$E), dt: f64) where intrinsics.type_is_float(E) {
|
||||
_tween_update(ctx, dt)
|
||||
}
|
||||
|
||||
tween_update :: proc {
|
||||
tween_update_v,
|
||||
tween_update_f,
|
||||
}
|
||||
|
||||
// stop a specific key inside the map
|
||||
// returns true when it successfully removed the key
|
||||
// @(require_results)
|
||||
tween_stop :: proc(ctx: ^TweenContext($T), id: int) -> bool {
|
||||
removed_tweens := false
|
||||
for i := 0; i < len(ctx.tweens); {
|
||||
if tween.id == id {
|
||||
unordered_remove(&ctx.tweens, i)
|
||||
removed_tweens = true
|
||||
} else {
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns the first tween animation with that id
|
||||
// If no tween exists with the given id, will return 0
|
||||
@(require_results)
|
||||
tween_time_left :: proc(flux: TweenContext($T), id: int) -> f64 {
|
||||
for tween in ctx.tweens {
|
||||
if tween.id == id {
|
||||
return ((1 - tween.progress) * tween.rate) + tween.delay
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue