Animación por estado

Reposo, preparación, embestida y recuperación, cada uno con su tween. Entrar en un estado mata el tween del anterior, así nunca pelean por la misma propiedad.

El editor de arriba está ejecutando este proyecto. Cambia una línea y se recarga.

Clases de Godot usadas

TweenPolygon2DLabel

El código

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

Más ejemplos de Animación