Godotでは Tween というイージング関数を使ったアニメーションノードが実装されていますが、よりお手軽に使えるようにしたものを公開しておきます。
イージング関数のサンプルコード
以下のスクリプトを Ease.gd として保存し、プロジェクトに追加します。
extends Node2D
# ===================================
# イージング関数定義
# ===================================
class_name Easing
enum eType {
LINEAR, # 線形
QUAD_IN, # 二次関数
QUAD_OUT,
QUAD_INOUT,
CUBE_IN, # 三次関数
CUBE_OUT,
CUBE_INOUT,
QUART_IN, # 四次関数
QUART_OUT,
QUART_INOUT,
QUINT_IN, # 五次関数
QUINT_OUT,
QUINT_INOUT,
SMOOTH_STEP_IN, # スムーズ曲線
SMOOTH_STEP_OUT,
SMOOTH_STEP_INOUT,
SMOOTHER_STEP_IN, # よりスムーズな曲線
SMOOTHER_STEP_OUT,
SMOOTHER_STEP_INOUT,
SIN_IN, # SIN関数
SIN_OUT,
SIN_INOUT,
BOUNCE_IN, # バウンス
BOUNCE_OUT,
BOUNCE_INOUT,
CIRC_IN, # サークル
CIRC_OUT,
CIRC_INOUT,
EXPO_IN, # 指数関数
EXPO_OUT,
EXPO_INOUT,
BACK_IN, # バック
BACK_OUT,
BACK_INOUT,
ELASTIC_IN, # 弾力関数
ELASTIC_OUT,
ELASTIC_INOUT,
}
# 一次関数
func linear(t:float) -> float:
return t
# 二次関数
func quad_in(t:float) -> float:
return t * t
func quad_out(t:float) -> float:
return -t * (t - 2)
func quad_in_out(t:float) -> float:
if t <= 0.5:
return t * t * 2
else:
return 1 - (t - 1) * (t - 1) * 2
# 三次関数
func cube_in(t:float) -> float:
return t * t * t
func cube_out(t:float) -> float:
return 1 + (t - 1) * (t - 1) * (t - 1)
func cube_in_out(t:float) -> float:
if t <= 0.5:
return t * t * t * 4
else :
return 1 + (t - 1) * (t - 1) * (t - 1) * 4
# 四次関数
func quart_in(t:float) -> float:
return t * t * t * t
func quart_out(t:float) -> float:
return 1 - (t - 1) * (t - 1) * (t - 1) * (t - 1)
func quart_in_out(t:float) -> float:
if t <= 0.5:
return t * t * t * t * 8
else:
t = t * 2 - 2
return (1 - t * t * t * t) / 2 + 0.5
# 五次関数
func quint_in(t:float) -> float:
return t * t * t * t * t
func quint_out(t:float) -> float:
t = t - 1
return t * t * t * t * t + 1
func quint_in_out(t:float) -> float:
t *= 2
if (t < 1):
return (t * t * t * t * t) / 2
else:
t -= 2
return (t * t * t * t * t + 2) / 2
# スムーズ曲線
func smooth_step_in(t:float) -> float:
return 2 * smooth_step_in_out(t / 2)
func smooth_step_out(t:float) -> float:
return 2 * smooth_step_in_out(t / 2 + 0.5) - 1
func smooth_step_in_out(t:float) -> float:
return t * t * (t * -2 + 3)
# よりスムーズな曲線
func smoother_step_in(t:float) -> float:
return 2 * smoother_step_in_out(t / 2)
func smoother_step_out(t:float) -> float:
return 2 * smoother_step_in_out(t / 2 + 0.5) - 1
func smoother_step_in_out(t:float) -> float:
return t * t * t * (t * (t * 6 - 15) + 10)
# SIN関数(0〜90度)
func sine_in(t:float) -> float:
return -cos(PI/2 * t) + 1
func sine_out(t:float) -> float:
return sin(PI/2 * t)
func sine_in_out(t:float) -> float:
return -cos(PI * t) / 2 + .5
# バウンス関数
const B1 = 1 / 2.75
const B2 = 2 / 2.75
const B3 = 1.5 / 2.75
const B4 = 2.5 / 2.75
const B5 = 2.25 / 2.75
const B6 = 2.625 / 2.75
func bounce_in(t:float) -> float:
t = 1 - t
if (t < B1): return 1 - 7.5625 * t * t
if (t < B2): return 1 - (7.5625 * (t - B3) * (t - B3) + .75)
if (t < B4): return 1 - (7.5625 * (t - B5) * (t - B5) + .9375)
return 1 - (7.5625 * (t - B6) * (t - B6) + .984375)
func bounce_out(t:float) -> float:
if (t < B1): return 7.5625 * t * t
if (t < B2): return 7.5625 * (t - B3) * (t - B3) + .75
if (t < B4): return 7.5625 * (t - B5) * (t - B5) + .9375
return 7.5625 * (t - B6) * (t - B6) + .984375
func bounce_in_out(t:float) -> float:
if (t < .5):
t = 1 - t * 2
if (t < B1): return (1 - 7.5625 * t * t) / 2
if (t < B2): return (1 - (7.5625 * (t - B3) * (t - B3) + .75)) / 2
if (t < B4): return (1 - (7.5625 * (t - B5) * (t - B5) + .9375)) / 2
return (1 - (7.5625 * (t - B6) * (t - B6) + .984375)) / 2
else:
t = t * 2 - 1
if (t < B1): return (7.5625 * t * t) / 2 + .5
if (t < B2): return (7.5625 * (t - B3) * (t - B3) + .75) / 2 + .5
if (t < B4): return (7.5625 * (t - B5) * (t - B5) + .9375) / 2 + .5
return (7.5625 * (t - B6) * (t - B6) + .984375) / 2 + .5
# 円
func circ_in(t:float) -> float:
return -(sqrt(1 - t * t) - 1)
func circ_out(t:float) -> float:
return sqrt(1 - (t - 1) * (t - 1))
func circ_in_out(t:float) -> float:
if t <= .5:
return (sqrt(1 - t * t * 4) - 1) / -2
else:
return (sqrt(1 - (t * 2 - 2) * (t * 2 - 2)) + 1) / 2
# 指数関数
func expo_in(t:float) -> float:
return pow(2, 10 * (t - 1))
func expo_out(t:float) -> float:
return -pow(2, -10*t) + 1
func expo_in_out(t:float) -> float:
if t < .5:
return pow(2, 10 * (t * 2 - 1)) / 2
else:
return (-pow(2, -10 * (t * 2 - 1)) + 2) / 2
# バック
func back_in(t:float) -> float:
return t * t * (2.70158 * t - 1.70158)
func back_out(t:float) -> float:
return 1 - (t - 1) * (t-1) * (-2.70158 * (t-1) - 1.70158)
func back_in_out(t:float) -> float:
t *= 2
if (t < 1):
return t * t * (2.70158 * t - 1.70158) / 2
else:
t -= 1
return (1 - (t - 1) * (t - 1) * (-2.70158 * (t - 1) - 1.70158)) / 2 + .5
# 弾力関数
const ELASTIC_AMPLITUDE = 1.0
const ELASTIC_PERIOD = 0.4
func elastic_in(t:float) -> float:
t -= 1
return -(ELASTIC_AMPLITUDE * pow(2, 10 * t) * sin( (t - (ELASTIC_PERIOD / (2 * PI) * asin(1 / ELASTIC_AMPLITUDE))) * (2 * PI) / ELASTIC_PERIOD))
func elastic_out(t:float) -> float:
return (ELASTIC_AMPLITUDE * pow(2, -10 * t) * sin((t - (ELASTIC_PERIOD / (2 * PI) * asin(1 / ELASTIC_AMPLITUDE))) * (2 * PI) / ELASTIC_PERIOD) + 1)
func elastic_in_out(t:float) -> float:
if (t < 0.5):
t -= 0.5
return -0.5 * (pow(2, 10 * t) * sin((t - (ELASTIC_PERIOD / 4)) * (2 * PI) / ELASTIC_PERIOD))
else:
t -= 0.5
return pow(2, -10 * t) * sin((t - (ELASTIC_PERIOD / 4)) * (2 * PI) / ELASTIC_PERIOD) * 0.5 + 1
func exec(type:int, t:float) -> float:
var function = get_function(type)
return function.call(t)
# @param v 0.0〜1.0
func step(type:int, start:float, end:float, v:float) -> float:
if start == end:
return start # 開始と終了が同じ
var a = start
var b = end
if v <= 0.0:
# 開始より小さい
return start
if v >= 1.0:
# 終了より大きい
return end
var d = b - a
var t = v
var func_name = get_function(type)
return a + (d * call(func_name, t))
func get_function(type:int) -> String:
var tbl = {
eType.LINEAR: "linear", # 線形
eType.QUAD_IN: "quad_in", # 二次関数
eType.QUAD_OUT: "quad_out",
eType.QUAD_INOUT: "quad_in_out",
eType.CUBE_IN: "cube_in", # 三次関数
eType.CUBE_OUT: "cube_out",
eType.CUBE_INOUT: "cube_in_out",
eType.QUART_IN: "quart_in", # 四次関数
eType.QUART_OUT: "quart_out",
eType.QUART_INOUT: "quart_in_out",
eType.QUINT_IN: "quint_in", # 五次関数
eType.QUINT_OUT: "quint_out",
eType.QUINT_INOUT: "quint_in_out",
eType.SMOOTH_STEP_IN: "smooth_step_in", # スムーズ曲線
eType.SMOOTH_STEP_OUT: "smooth_step_out",
eType.SMOOTH_STEP_INOUT: "smooth_step_in_out",
eType.SMOOTHER_STEP_IN: "smoother_step_in", # よりスムーズな曲線
eType.SMOOTHER_STEP_OUT: "smoother_step_out",
eType.SMOOTHER_STEP_INOUT: "smoother_step_in_out",
eType.SIN_IN: "sine_in", # SIN関数
eType.SIN_OUT: "sine_out",
eType.SIN_INOUT: "sine_in_out",
eType.BOUNCE_IN: "bounce_in", # バウンス
eType.BOUNCE_OUT: "bounce_out",
eType.BOUNCE_INOUT: "bounce_in_out",
eType.CIRC_IN: "circ_in", # サークル
eType.CIRC_OUT: "circ_out",
eType.CIRC_INOUT: "circ_in_out",
eType.EXPO_IN: "expo_in", # 指数関数
eType.EXPO_OUT: "expo_out",
eType.EXPO_INOUT: "expo_in_out",
eType.BACK_IN: "back_in", # バック
eType.BACK_OUT: "back_out",
eType.BACK_INOUT: "back_inout",
eType.ELASTIC_IN: "elastic_in", # 弾力関数
eType.ELASTIC_OUT: "elastic_out",
eType.ELASTIC_INOUT: "elastic_in_out",
}
if tbl.has(type):
return tbl[type]
else:
print("未定義のイージング関数: %d"%type)
return "linear"
そうしたら、プロジェクトの設定 > 自動読み込みから「Ease.gd」を自動読み込み有効にしておきます。
実装サンプルコード
例えば以下のように使います
extends Node2D
var timer = 0.0
onready var icon = $Icon
onready var icon2 = $Icon2
func _process(delta: float) -> void:
if Input.is_action_just_pressed("ui_accept"):
timer = 0
if timer < 3.0:
timer += delta
var t = timer / 3.0 # 0.0〜1.0に変換
# そのままイージング関数を使う場合
icon.position.x = 200 + 600 * Ease.expo_out(t)
icon.position.y = 200;
# step()で開始と終端を指定する
icon2.position.x = Ease.step(Easing.eType.BOUNCE_OUT, 200, 800, t)
icon2.position.y = 300
変化量だけ欲しい場合には、Ease.###_in/out() を使い、
開始地点と終端から位置を求めたい場合には Ease.step() を使用します。
なおイージング関数の種類についてはこちらのサイトが詳しいと思います。
完成プロジェクト
今回作成したプロジェクトを添付しておきます。