Godot Tween With Custom Easing Functions

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Boris Šehovac

Since I couldn’t figure out how to add custom easing functions to existing Tween, I decided to make custom Tween class where I could implement them. Actually the only reason for this is that Tween.TRANS_ELASTIC is not configurable in any way. Anyway, I wanted to check is there any performance drawbacks with this, or did anyone else implemented it in original Tween. I like Godot very much, but it really misses some basic functionalities…

extends Node

class_name TweenCustom

var tween_id = 0

var tween_data = {
	'id': 0,
	'start': 0,
	'duration': 0,
	'object': null,
	'update': null,
	'complete': null,
	'easing': null,
}

var tweens = {}

func _ready():
	set_process(false)

func _process(delta):
	for id in tweens.keys():
		update(id)

func update(id):
	var now = OS.get_ticks_msec() / 1000.0
	var tween = tweens[id]
	var delta = now - tween.start
	var time = 0.0
	if tween.duration != 0.0:
		time = delta / tween.duration
	else:
		time = 1.0
	var complete = false
	var value = tween.easing.call_func(time, tween.config)
	if time >= 1.0:
		complete = true
		value = 1.0
	tween.update.call_func(value)
	if complete:
		tween.complete.call_func()
		tweens.erase(tween.id)
		if tweens.size() < 1:
			set_process(false)

func start(object = Node, duration = 1.0, easing = LINEAR(), callback = null, complete = null):
	var tween = tween_data.duplicate()
	tween.id = get_id()
	tween.start = OS.get_ticks_msec() / 1000.0
	tween.duration = duration
	tween.object = object
	tween.update = funcref(object, callback)
	tween.easing = funcref(self, easing.func)
	tween.complete = funcref(object, complete)
	tween.config = easing.config
	
	tweens[tween.id] = tween
	
	update(tween.id)
	set_process(true)

func get_id():
	var id = tween_id
	tween_id += 1
	return id

func LINEAR():
	return { 'func': 'ease_linear', 'config': null }

func ELASTIC_OUT(amplitude = 0.8, period = 0.6):
	var PI2 = PI * 2
	var p1 = amplitude if amplitude >= 1 else 1
	var p2 = period / amplitude if amplitude < 1 else period
	var p3 = p2 / PI2 * asin(1/ p1) if p1 != 0 else 0
	p2 = PI2 / p2
	return { 'func': 'ease_elastic_out', 'config': { 'p1': p1, 'p2': p2, 'p3': p3 } }

func ease_elastic_out(t, c):
	return c.p1 * pow(2, -10 * t) * sin((t - c.p3) * c.p2) + 1

func ease_linear(t):
	return t

It would be triggered like this:

tween.start(self, 3.0, tween.ELASTIC_OUT(0.8, 0.6), 'on_update', 'on_complete')