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

Plus d'exemples de Animation