Animation par état
Repos, préparation, ruée et récupération, chacun avec son tween. Entrer dans un état tue le tween du précédent, ils ne se disputent donc jamais la même propriété.
L'éditeur ci-dessus fait tourner ce projet. Changez une ligne et il se recharge.
Classes Godot utilisées
TweenPolygon2DLabel
Le code
scripts/main.gd
extends Node
enum State { IDLE, WINDUP, DASH, RECOVER }
const HOME: float = 760.0
const ACTOR_Y: float = 380.0
const SIZE: float = 76.0
const NAMES: Array = ['IDLE', 'WINDUP', 'DASH', 'RECOVER']
const COLORS: Array = ['#8da5f3', '#f3d48d', '#8eef97', '#fc7f7f']
const DIM: Color = Color('#4a5162')
var state: State = State.IDLE
var tween: Tween
var actor: Polygon2D
var rows: Array[Label] = []
var pointer: Polygon2D
func _ready() -> void:
_add_background()
_add_label('one enum, four tweens, the state picks which one runs', Vector2(40, 40), Color('#999999'))
for i in NAMES.size():
var row: Label = _add_label(NAMES[i], Vector2(110, 130 + i * 60), DIM)
row.add_theme_font_size_override('font_size', 30)
rows.append(row)
pointer = Polygon2D.new()
pointer.polygon = PackedVector2Array([Vector2(0, -13), Vector2(21, 0), Vector2(0, 13)])
pointer.position = Vector2(70, 152)
add_child(pointer)
var rail: ColorRect = ColorRect.new()
rail.color = Color('#2a2f3d')
rail.position = Vector2(600, ACTOR_Y + SIZE * 0.5)
rail.size = Vector2(600, 4)
add_child(rail)
var home_mark: ColorRect = ColorRect.new()
home_mark.color = Color('#4a5162')
home_mark.position = Vector2(HOME - 2, ACTOR_Y + SIZE * 0.5 - 10)
home_mark.size = Vector2(4, 24)
add_child(home_mark)
actor = _make_square()
actor.position = Vector2(HOME, ACTOR_Y)
add_child(actor)
_set_state(State.IDLE)
print('the cycle runs on its own, each state kills the tween of the previous one')
func _set_state(next: State) -> void:
state = next
actor.color = Color(COLORS[state])
_highlight()
if tween and tween.is_valid():
tween.kill()
tween = create_tween()
var following: State
match state:
State.IDLE:
tween.tween_property(actor, 'scale', Vector2(1.08, 0.92), 0.3).set_trans(Tween.TRANS_SINE)
tween.tween_property(actor, 'scale', Vector2.ONE, 0.3).set_trans(Tween.TRANS_SINE)
following = State.WINDUP
State.WINDUP:
tween.tween_property(actor, 'position:x', HOME - 90.0, 0.3).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
tween.parallel().tween_property(actor, 'scale', Vector2(0.78, 1.22), 0.3)
following = State.DASH
State.DASH:
tween.tween_property(actor, 'position:x', 1150.0, 0.4).set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_OUT)
tween.parallel().tween_property(actor, 'scale', Vector2(1.5, 0.7), 0.2)
following = State.RECOVER
State.RECOVER:
tween.tween_property(actor, 'position:x', HOME, 0.55).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_IN_OUT)
tween.parallel().tween_property(actor, 'scale', Vector2.ONE, 0.55)
following = State.IDLE
tween.finished.connect(_set_state.bind(following), CONNECT_ONE_SHOT)
func _highlight() -> void:
var active: Color = Color(COLORS[state])
for i in rows.size():
rows[i].add_theme_color_override('font_color', active if i == state else DIM)
pointer.color = active
pointer.position.y = 152 + state * 60
func _make_square() -> Polygon2D:
var square: Polygon2D = Polygon2D.new()
square.polygon = PackedVector2Array(
[
Vector2(-SIZE, -SIZE) * 0.5,
Vector2(SIZE, -SIZE) * 0.5,
Vector2(SIZE, SIZE) * 0.5,
Vector2(-SIZE, SIZE) * 0.5,
]
)
return square
func _add_background() -> void:
var background: ColorRect = ColorRect.new()
background.color = Color('#12141a')
add_child(background)
background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
func _add_label(text: String, at: Vector2, color: Color) -> Label:
var label: Label = Label.new()
label.text = text
label.position = at
label.add_theme_color_override('font_color', color)
add_child(label)
return label