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