references/migration-notes.md
# Migration notes: godot-tweening
Incremental upgrade for topics this skill covers. Apply **one hop**, stabilize/test, then next. Never skip hops.
If the project is **< 4.0**, follow [godot-version-migration](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-version-migration/SKILL.md) era bridges (legacy → 3→4) until 4.0, then these hops. Official 3→4: [Upgrading from Godot 3 to Godot 4](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.html).
## 4.0 → 4.1
Official: [Upgrading to Godot 4.1](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.1.html)
- `AnimationNode._process` requires new `test_only` parameter; `blend_input`/`blend_node` gain optional `test_only`.
- `AnimationNodeStateMachinePlayback.get_travel_path` returns `Array[StringName]`.
- `PathFollow2D.lookahead` removed.
- `SubViewportContainer.mouse_filter` must be STOP/PASS for input to reach SubViewports.
- Layered SubViewportContainers needing mouse input may need Area2D replacements.
- `CodeEdit.add_code_completion_option` gains `location`; Tree `edit_selected` gains `force_edit`.
## 4.1 → 4.2
Official: [Upgrading to Godot 4.2](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.2.html)
- Many `AnimationPlayer`/`AnimationTree` APIs moved to `AnimationMixer` base.
- `method_call_mode` → `callback_mode_method`; `playback_process_mode`/`process_callback` → `callback_mode_process`.
- `playback_active` → `active` on mixer; `AnimationTree.tree_root` typed as `AnimationRootNode`.
- `AnimationPlayer.seek` gains optional `update_only`.
- `PopupMenu` shortcut helpers gain `allow_echo`; `clear` gains `free_submenus`.
- GraphEdit: `arrange_nodes_button_hidden` → `show_arrange_button`; snap props renamed; `get_zoom_hbox` → `get_menu_hbox`.
- GraphNode: large API move to `GraphElement`; connection query methods removed; `comment`/`show_close` removed.
## 4.2 → 4.3
Official: [Upgrading to Godot 4.3](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.3.html)
- `Animation` interpolate / `track_find_key` gain `backward`/`limit` options.
- `AnimationMixer._post_process_key_value` object arg is `uint64`.
- `Skeleton3D.bone_pose_changed` → `skeleton_updated`; `BoneAttachment3D.on_bone_pose_update` → `on_skeleton_update`.
- Capture mode replaced; see Migrating Animations 4.0→4.3 article for blend/time semantics.
- Default font outline color is black (was white).
- `auto_translate` deprecated for Node `auto_translate_mode` (inherit semantics).
- `AcceptDialog` register/remove helpers take LineEdit/Button specifically.
## 4.3 → 4.4
Official: [Upgrading to Godot 4.4](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.4.html)
- `GraphEdit.connect_node` gains `keep_alive`; `frame_rect_changed` uses `Rect2`.
## 4.4 → 4.5
Official: [Upgrading to Godot 4.5](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.5.html)
- CanvasItem/Font/TextLine draw APIs gain optional `oversampling`.
- `TreeItem.add_button` gains `alt_text`.
## 4.5 → 4.6
Official: [Upgrading to Godot 4.6](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.6.html)
- `AnimationPlayer` `assigned_animation` / `autoplay` / `current_animation` are `StringName` (C# binary break).
- `get_queue` returns `StringName[]`.
- `Control.grab_focus` / `has_focus` gain hide-focus options.
- `FileDialog.add_filter` gains `mime_type`; `SplitContainer.clamp_split_offset` gains `priority_index`.
- `EditorFileDialog` file APIs moved onto `FileDialog` base; `add_side_menu` removed.
- `PopupMenu.submenu_popup_delay` default 0.2 (was 0.3).
## 4.6 → 4.7
Official: [Upgrading to Godot 4.7](https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.7.html)
- `Animation.length` uses double metadata (C# impact).
- `AnimationNodeBlendSpace1D/2D.add_blend_point` optional `name`.
- `LookAtModifier3D.relative` default is `false` (was `true`).
- `Control.accessibility_live` uses `AccessibilityServer.AccessibilityLiveMode`.
- `TreeItem.select` gains `set_as_cursor`.
- `CanvasItem` no longer adds antialiasing feather that thickened lines — widen strokes if visuals relied on it.
references/tween-recipes-and-gotchas.md
# Tween recipes and gotchas
Expert scripts own interruptible UI — these are supporting recipes.
## Kill before recreate
```gdscript
var current_tween: Tween
func animate_to(pos: Vector2) -> void:
if current_tween and current_tween.is_valid():
current_tween.kill()
current_tween = create_tween().bind_node(self)
current_tween.tween_property(self, "position", pos, 1.0)
```
MANDATORY: [safe_tween_interruption.gd](../scripts/safe_tween_interruption.gd).
## finished signal
```gdscript
var tween := create_tween()
tween.tween_property($Sprite, "position", Vector2(100, 0), 1.0)
tween.finished.connect(_on_tween_finished)
```
## Chained fade-move-fade
```gdscript
var tween := create_tween()
tween.tween_property($Sprite, "modulate:a", 0.0, 0.5)
tween.tween_property($Sprite, "position", Vector2(200, 0), 0.0)
tween.tween_property($Sprite, "modulate:a", 1.0, 0.5)
```
## Bezier path
Tween `PathFollow2D.progress_ratio` — see Expert Patterns in SKILL.md.
## Gotchas
| Issue | Fix |
|-------|-----|
| Stops when node freed | `create_tween().bind_node(self)` |
| Conflicting tweens | Kill previous reference |
| Pause menu frozen | `set_ignore_time_scale(true)` — [time_scale_ignored_ui.gd](../scripts/time_scale_ignored_ui.gd) |
scripts/camera_shake_tween_logic.gd
# camera_shake_tween_logic.gd
# Procedural screen shake using randomized tweens
extends Camera2D
func apply_shake(intensity: float, duration: float):
var tween = create_tween().set_loops(5) # Shake 5 times
var offset_v = Vector2(randf_range(-1, 1), randf_range(-1, 1)) * intensity
tween.tween_property(self, "offset", offset_v, duration / 10.0)
tween.tween_property(self, "offset", Vector2.ZERO, duration / 10.0)
# Ensure the camera returns to 0,0 at the very end
tween.finished.connect(func(): offset = Vector2.ZERO)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# - https://docs.godotengine.org/en/stable/classes/class_camera2d.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-camera-systems/SKILL.md — shake vs follow ownership on Camera2D
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md — finished reset of offset
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/custom_curve_tween.gd
# custom_curve_tween.gd
# Driving property animations using visual Curve resources
extends Node2D
@export var bounce_curve: Curve
# EXPERT NOTE: For juice, avoid standard TransTypes and use a
# Curve resource for total control over the easing profile.
func juice_impact():
var tween = create_tween()
# The scale will follow the visual curve exactly
tween.tween_property(self, "scale", Vector2(1.5, 1.5), 0.6)\
.set_custom_interpolator(func(v): return bounce_curve.sample(v))
tween.chain().tween_property(self, "scale", Vector2.ONE, 0.2)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_propertytweener.html
# - https://docs.godotengine.org/en/stable/classes/class_curve.html
# - https://docs.godotengine.org/en/stable/tutorials/math/beziers_and_curves.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-resource-data-patterns/SKILL.md — export Curve juice profiles as Resources
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-2d-animation/SKILL.md — when Curve juice beats authored clips
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/juice_manager.gd
# skills/tweening/code/juice_manager.gd
extends Node
## Tweening Expert Pattern
## Implements Custom Interpolators (Juice) and Bezier Easing.
@export var punch_curve: Curve
# 1. Custom Interpolators (tween_method)
func punch_ui(target: Control) -> void:
var tween = create_tween().set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
# Professional pattern: Kill previous tween to avoid conflicts.
# target.set_meta("active_tween", tween) # Simplified tracking
# Animate a custom method for values that aren't simple properties
# e.g. Animate a shader uniform or a multi-step calculation.
tween.tween_method(_apply_shader_distortion, 0.0, 1.0, 0.5)
# 2. Sequence Management
tween.parallel().tween_property(target, "scale", Vector2(1.2, 1.2), 0.1)
tween.chain().tween_property(target, "scale", Vector2.ONE, 0.3)
func _apply_shader_distortion(value: float) -> void:
# Logic for manual value interpolation
pass
# 3. Bezier Easing via Curves
func curve_animate(target: Node2D, destination: Vector2) -> void:
var tween = create_tween()
# Expert logic: Use a Move-To-Value approach driven by a Curve.
# This allows for high-end, artistic motion control.
tween.tween_method(
func(t: float):
var weight = punch_curve.sample(t)
target.global_position = target.global_position.lerp(destination, weight),
0.0, 1.0, 1.0
)
## EXPERT NOTE:
## Use 'Scene Tree Independence': For UI elements that persist
## across scene changes (like a loading bar), create the Tween
## on a Global Autoload Node rather than the Control node itself.
## For 'tweening', implement 'Rhythmic Transitions': Use
## 'tween.set_speed_scale(2.0)' to match UI animation speed to
## the game's BPM or combat pace.
## NEVER forget to call 'kill()' on older tweens when a new action
## interrupts an existing one (e.g. stopping a 'Hurt' shake to start
## a 'Death' fade) to prevent property flickering.
## Use 'set_parallel()' to trigger multi-property 'Juice' (Scale,
## Rotation, and Color) in a single rhythmic burst.
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_methodtweener.html
# - https://docs.godotengine.org/en/stable/classes/class_curve.html
# - https://docs.godotengine.org/en/stable/tutorials/math/interpolation.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-shaders-basics/SKILL.md — tween_method driving shader distortion
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-resource-data-patterns/SKILL.md — Curve/JuiceConfig resource ownership
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/looped_hover_vfx.gd
# looped_hover_vfx.gd
# Infinite ping-pong animations with set_loops()
extends Sprite2D
# EXPERT NOTE: Tweens can replace AnimationPlayer for simple
# ambient effects like floating or glowing.
func _ready() -> void:
var tween = create_tween().set_loops() # Infinite
tween.tween_property(self, "position:y", 10, 2.0)\
.as_relative().set_trans(Tween.TRANS_SINE)
tween.tween_property(self, "position:y", -10, 2.0)\
.as_relative().set_trans(Tween.TRANS_SINE)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# - https://docs.godotengine.org/en/stable/classes/class_propertytweener.html
# - https://docs.godotengine.org/en/stable/tutorials/animation/introduction.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-2d-animation/SKILL.md — ambient loops without AnimationPlayer overhead
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-particles/SKILL.md — when particles replace ping-pong position juice
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/nested_subtween_cutscene.gd
# nested_subtween_cutscene.gd
# Hierarchical cutscene timing using tween_subtween()
extends Node
# EXPERT NOTE: Combine multiple complex sequences into a main
# timeline using subtweens for modular cutscene management.
func play_sequence():
var sub = create_tween()
sub.tween_property($Actor, "rotation", PI, 1.0)
sub.tween_property($Actor, "rotation", 0, 1.0)
var main = create_tween()
main.tween_property($Actor, "position:x", 500, 2.0)
# Main timeline waits for the rotation sequence to finish
main.tween_subtween(sub)
main.tween_property($Actor, "modulate:a", 0, 1.0)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_subtweentweener.html
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# - https://docs.godotengine.org/en/stable/tutorials/animation/introduction.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-animation-player/SKILL.md — authored cutscenes vs composable subtween modules
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-dialogue-system/SKILL.md — dialogue beats chained under parent timelines
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/parallel_popup_animation.gd
# parallel_popup_animation.gd
# Simultaneous property animations with set_parallel()
extends Control
# EXPERT NOTE: Use set_parallel(true) for UI popups to move, fade,
# and scale all at once, then chain() for sequential cleanup.
func show_popup():
pivot_offset = size / 2
scale = Vector2.ZERO
modulate.a = 0
var tween = create_tween().set_parallel(true)
tween.tween_property(self, "scale", Vector2.ONE, 0.4)\
.set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
tween.tween_property(self, "modulate:a", 1.0, 0.3)
# Transition back to sequential mode to run a callback at the end
tween.chain().tween_callback(_on_show_complete)
func _on_show_complete():
print("Popup fully visible")
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# - https://docs.godotengine.org/en/stable/classes/class_callbacktweener.html
# - https://docs.godotengine.org/en/stable/tutorials/ui/size_and_anchors.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-containers/SKILL.md — pivot_offset and layout before scale popups
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-theming/SKILL.md — theme-aligned popup motion
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/relative_recoil_tween.gd
# relative_recoil_tween.gd
# Relative position offsets with as_relative() and from_current()
extends Sprite2D
# EXPERT NOTE: Use as_relative() for recoil or camera nudges so you
# don't need to know the 'base' value.
func shoot_recoil(strength: float):
var tween = create_tween()
# Relative movement from WHEREVER it is now
tween.tween_property(self, "position:x", -strength, 0.05)\
.as_relative().from_current().set_trans(Tween.TRANS_SINE)
# Snap back to center
tween.chain().tween_property(self, "position:x", 0, 0.2)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_propertytweener.html
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-camera-systems/SKILL.md — relative camera nudges on fire
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-genre-shooter/SKILL.md — weapon recoil juice without base-position tracking
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/safe_tween_interruption.gd
# safe_tween_interruption.gd
# Aborting active tweens before starting new ones on the same object
extends Node2D
# EXPERT NOTE: Multiple tweens fighting over the same property cause
# erratic behavior. Always kill the previous tween if it's still running.
var _active_tween: Tween
func animate_safe_hover():
if _active_tween and _active_tween.is_valid():
_active_tween.kill() # Terminate previous animation
# bind_node ensures the tween is killed if the node is deleted
_active_tween = create_tween().bind_node(self)
_active_tween.tween_property(self, "scale", Vector2(1.2, 1.2), 0.2)
_active_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# - https://docs.godotengine.org/en/stable/classes/class_propertytweener.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md — finished cleanup after kill/recreate
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-containers/SKILL.md — hover scale on Controls without flicker
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/staggered_inventory_entry.gd
# staggered_inventory_entry.gd
# Looping through collections for sequential entry effects
extends GridContainer
func animate_items():
var tween = create_tween()
# Because set_parallel is false, these run one-by-one
for child in get_children():
child.scale = Vector2.ZERO
tween.tween_property(child, "scale", Vector2.ONE, 0.15)\
.set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
# Expert: Use a tiny interval if you want them to overlap slightly
# tween.set_parallel(true).tween_interval(0.05).set_parallel(false)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# - https://docs.godotengine.org/en/stable/classes/class_intervaltweener.html
# - https://docs.godotengine.org/en/stable/tutorials/ui/gui_containers.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-inventory-system/SKILL.md — slot entry stagger on open
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-containers/SKILL.md — GridContainer children as tween targets
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/text_counter_method_tween.gd
# text_counter_method_tween.gd
# Animating abstract values using tween_method [Score Counters]
extends Label
# EXPERT NOTE: Use tween_method to animate values that aren't
# direct properties, like UI text or custom shader parameters.
func update_score(target: int):
var curr_score = int(text.split(": ")[1])
var tween = create_tween()
# Calls '_set_score_text' with an interpolated int value
tween.tween_method(_set_score_text, curr_score, target, 1.5)\
.set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_OUT)
func _set_score_text(val: int):
text = "Score: " + str(val)
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_methodtweener.html
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-rich-text/SKILL.md — label/score presentation while values tween
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md — typed callables for tween_method
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/time_scale_ignored_ui.gd
# time_scale_ignored_ui.gd
# Ensuring UI tweens run while the game is paused [Engine.time_scale]
extends Control
# EXPERT NOTE: If you pause by setting Engine.time_scale = 0,
# standard tweens freeze. Use set_ignore_time_scale(true) for UI.
func open_pause_menu():
Engine.time_scale = 0 # Game world freezes
var tween = create_tween()
tween.set_ignore_time_scale(true) # This tween keeps running
tween.tween_property(self, "position:x", 0, 0.5)\
.set_trans(Tween.TRANS_QUART).set_ease(Tween.EASE_OUT)
func close_pause_menu():
Engine.time_scale = 1.0
queue_free()
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# - https://docs.godotengine.org/en/stable/classes/class_engine.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-containers/SKILL.md — pause-menu Control motion while world is frozen
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-scene-management/SKILL.md — pause tree vs time_scale tradeoffs
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
scripts/tween_builder.gd
# skills/tweening/scripts/tween_builder.gd
extends Node
## Tween Builder Expert Pattern
## Fluent API for complex tween chains with parallel and sequential operations.
class_name TweenBuilder
static func create_sequential() -> Tween:
var tween := Engine.get_main_loop().create_tween()
tween.set_parallel(false)
return tween
static func create_parallel() -> Tween:
var tween := Engine.get_main_loop().create_tween()
tween.set_parallel(true)
return tween
static func fade_out(node: CanvasItem, duration := 0.5) -> Tween:
var tween := create_sequential()
tween.tween_property(node, "modulate:a", 0.0, duration)
return tween
static func fade_in(node: CanvasItem, duration := 0.5) -> Tween:
var tween := create_sequential()
tween.tween_property(node, "modulate:a", 1.0, duration)
return tween
static func bounce_scale(node: Node, duration := 0.3, scale_multiplier := 1.2) -> Tween:
var original_scale = node.scale if node.has("scale") else Vector2.ONE
var tween := create_sequential()
tween.tween_property(node, "scale", original_scale * scale_multiplier, duration * 0.5)\
.set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
tween.tween_property(node, "scale", original_scale, duration * 0.5)\
.set_trans(Tween.TRANS_ELASTIC).set_ease(Tween.EASE_OUT)
return tween
static func shake(node: Node2D, duration := 0.3, intensity := 5.0) -> Tween:
var original_pos := node.position
var tween := create_sequential()
var shake_count := int(duration / 0.05)
for i in shake_count:
var offset := Vector2(
randf_range(-intensity, intensity),
randf_range(-intensity, intensity)
)
tween.tween_property(node, "position", original_pos + offset, 0.05)
tween.tween_property(node, "position", original_pos, 0.05)
return tween
static func chain_with_callback(tweens: Array[Tween], callbacks: Array[Callable]) -> void:
if tweens.size() != callbacks.size():
push_error("Tween and callback arrays must match in size")
return
for i in tweens.size():
if i < callbacks.size() and callbacks[i].is_valid():
tweens[i].finished.connect(callbacks[i])
## EXPERT USAGE:
## # Simple fade
## TweenBuilder.fade_out($Sprite)
##
## # Button press feedback
## TweenBuilder.bounce_scale($Button, 0.2, 1.1)
##
## # Complex chain
## var t1 := TweenBuilder.fade_in($Panel)
## var t2 := TweenBuilder.bounce_scale($Panel/Title)
## t1.finished.connect(func(): t2.play())
# =============================================================================
# GDSkills research links (agents) — does not affect runtime
# Official docs:
# - https://docs.godotengine.org/en/stable/classes/class_tween.html
# - https://docs.godotengine.org/en/stable/classes/class_scenetree.html
# - https://docs.godotengine.org/en/stable/classes/class_engine.html
# Related skills:
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md — fluent static builders and typed returns
# - https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md — SceneTree/main-loop create_tween ownership
# Parent skill: https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-tweening/SKILL.md
# =============================================================================
SKILL.md
---
name: godot-tweening
description: "Expert blueprint for programmatic animation using Tween for smooth property transitions, UI effects, camera movements, and juice. Covers easing functions, parallel tweens, chaining, and lifecycle management. Use when implementing UI animations OR procedural movement. Keywords Tween, easing, interpolation, EASE_IN_OUT, TRANS_CUBIC, tween_property, tween_callback."
---
## Decision Tree — Tween vs AnimationPlayer
| Situation | Choose |
|-----------|--------|
| One-off UI juice, hover, popup, score count, recoil | **Tween** (`create_tween`) |
| Authored multi-track clips, scrubbable timelines, blend trees | **AnimationPlayer** / AnimationTree |
| Camera continuous follow behind a moving target | **Camera2D.position_smoothing** / spring follow — **not** a new Tween every `_process` |
| Menu motion while `Engine.time_scale == 0` | Tween with `set_ignore_time_scale(true)` — **MANDATORY** [time_scale_ignored_ui.gd](scripts/time_scale_ignored_ui.gd) |
| Retriggerable property (button spam, dodge cancel) | Kill-before-recreate — **MANDATORY** [safe_tween_interruption.gd](scripts/safe_tween_interruption.gd) |
| Physics body / net correction motion | `TWEEN_PROCESS_PHYSICS` + `reset_physics_interpolation` |
## Available Scripts
> **MANDATORY**: Read the script for the case above before writing tween glue.
### [safe_tween_interruption.gd](scripts/safe_tween_interruption.gd)
**MANDATORY** for any retriggerable tween — kill active tweens before starting new ones.
### [time_scale_ignored_ui.gd](scripts/time_scale_ignored_ui.gd)
**MANDATORY** for pause-menu / `time_scale == 0` UI motion.
### [nested_subtween_cutscene.gd](scripts/nested_subtween_cutscene.gd)
**MANDATORY** for composable cutscene timelines via `tween_subtween`.
### [parallel_popup_animation.gd](scripts/parallel_popup_animation.gd)
`set_parallel(true)` + `chain()` for multi-property UI transitions.
### [text_counter_method_tween.gd](scripts/text_counter_method_tween.gd)
`tween_method` for non-property values (score strings).
### [custom_curve_tween.gd](scripts/custom_curve_tween.gd)
`Curve` resources for bespoke easing.
### [camera_shake_tween_logic.gd](scripts/camera_shake_tween_logic.gd)
Procedural screen shake with looping tweens (offset, not follow).
### [relative_recoil_tween.gd](scripts/relative_recoil_tween.gd)
`as_relative()` / `from_current()` for recoil nudges.
### [staggered_inventory_entry.gd](scripts/staggered_inventory_entry.gd)
Sequential collection entry on one Tween.
### [looped_hover_vfx.gd](scripts/looped_hover_vfx.gd)
Infinite ping-pong ambient juice.
### [juice_manager.gd](scripts/juice_manager.gd) / [tween_builder.gd](scripts/tween_builder.gd)
Central juice dispatch / builder helpers when many systems share feel presets.
## NEVER Do in Tweening
- **NEVER instantiate a Tween using `Tween.new()`** — Always use `create_tween()` or `get_tree().create_tween()` [3, 4].
- **NEVER attempt to reuse a finished Tween** — Single-use; recreate to replay [4].
- **NEVER manually instantiate `PropertyTweener` or `CallbackTweener`** — Only via parent Tween methods [5].
- **NEVER create an infinite loop containing only 0-duration animations** — Freezes the engine [10].
- **NEVER use multiple Tweens to animate the same property simultaneously** — `kill()` the old reference first [11, 12].
- **NEVER use linear interpolation for UI/Juice** — Prefer `EASE_OUT + TRANS_QUAD` or `EASE_IN_OUT + TRANS_CUBIC` [22].
- **NEVER create tweens in `_process` without guards** — Creating 60 tweens per second will crash the app.
- **NEVER skip `bind_node(self)` for non-global tweens** — Binding ensures death with the node [13].
- **NEVER use 0-duration tweens for state changes** — Set the property directly [20].
- **NEVER forget to call `chain()` when returning from `set_parallel(true)`** [15].
---
## Lifecycle Golden Path
```gdscript
var _tween: Tween
func animate_to(pos: Vector2) -> void:
if _tween and _tween.is_valid():
_tween.kill()
_tween = create_tween().bind_node(self)
_tween.set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_QUAD)
_tween.tween_property(self, "position", pos, 0.35)
```
**MANDATORY pattern source:** [safe_tween_interruption.gd](scripts/safe_tween_interruption.gd).
## Camera Follow (Correct)
Continuous follow is **not** a Tween job:
```gdscript
extends Camera2D
@export var target: Node2D
func _ready() -> void:
position_smoothing_enabled = true
position_smoothing_speed = 5.0
func _physics_process(_delta: float) -> void:
if target:
global_position = target.global_position
```
If you must tween a one-shot camera punch/return, keep **one** Tween reference and kill before recreate — never `create_tween()` inside unguarded `_process`.
## Expert Patterns (keep)
### Physics-Sync-Tweening
```gdscript
func apply_physics_tween(target: Node3D, start_pos: Vector3, goal: Vector3) -> void:
target.global_position = start_pos
target.reset_physics_interpolation()
var tween := create_tween().bind_node(target)
tween.set_process_mode(Tween.TWEEN_PROCESS_PHYSICS)
tween.tween_property(target, "global_position", goal, 0.5)
```
### Juice-Config-Resource
Store duration/trans/ease in a `Resource` (see juice scripts) so feel is data-driven.
### Tween-Event-Sequencing
Parallel block → `chain()` → interval/callback → exit. Prefer [nested_subtween_cutscene.gd](scripts/nested_subtween_cutscene.gd) for nested modules.
### Bezier-Path-Tween
Tween `PathFollow2D.progress_ratio` instead of hand-rolled Bezier math.
## Deep recipes (on demand)
> LLM-ignorance rule: if a general agent would not know it before reading, it lives here or in `scripts/` — never delete, only move.
| Topic | Reference |
|-------|-----------|
| Chains, kill, gotchas | [tween-recipes-and-gotchas.md](references/tween-recipes-and-gotchas.md) |
## Reference
> Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
### Official Documentation
- [Tween](https://docs.godotengine.org/en/stable/classes/class_tween.html) — create_tween, parallel/chain, loops, process mode, ignore_time_scale, and kill/lifecycle rules.
- [PropertyTweener](https://docs.godotengine.org/en/stable/classes/class_propertytweener.html) — tween_property details: as_relative, from_current, custom interpolators, and per-step ease/trans.
- [MethodTweener](https://docs.godotengine.org/en/stable/classes/class_methodtweener.html) — tween_method for non-property values (score counters, shader params, Curve-sampled motion).
- [CallbackTweener](https://docs.godotengine.org/en/stable/classes/class_callbacktweener.html) — tween_callback for sequenced side effects without inventing fake 0-duration property steps.
- [IntervalTweener](https://docs.godotengine.org/en/stable/classes/class_intervaltweener.html) — tween_interval delays inside a single Tween timeline.
- [SubtweenTweener](https://docs.godotengine.org/en/stable/classes/class_subtweentweener.html) — tween_subtween for nested cutscene modules under one parent timeline.
- [Interpolation](https://docs.godotengine.org/en/stable/tutorials/math/interpolation.html) — lerp/smoothstep foundations behind easing choices and custom interpolators.
- [Beziers, curves and paths](https://docs.godotengine.org/en/stable/tutorials/math/beziers_and_curves.html) — Curve/PathFollow patterns used when property tweens alone cannot describe the path.
- [Introduction to the animation features](https://docs.godotengine.org/en/stable/tutorials/animation/introduction.html) — when Tween juice is enough versus AnimationPlayer/AnimationTree authored tracks.
- [Using SceneTree](https://docs.godotengine.org/en/stable/tutorials/scripting/scene_tree.html) — SceneTree.create_tween and node lifetime when bind_node is not enough.
- [Idle and Physics Processing](https://docs.godotengine.org/en/stable/tutorials/scripting/idle_and_physics_processing.html) — idle vs physics process modes for TWEEN_PROCESS_PHYSICS sync.
- [Physics interpolation quick start guide](https://docs.godotengine.org/en/stable/tutorials/physics/interpolation/physics_interpolation_quick_start_guide.html) — reset_physics_interpolation when tweening transforms on interpolated bodies.
### Related Skills
#### Prerequisites
- [godot-project-foundations](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-project-foundations/SKILL.md) — scene tree, Node ownership, and resource basics before create_tween/bind_node lifecycle patterns.
- [godot-gdscript-mastery](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-gdscript-mastery/SKILL.md) — typed callables, lambdas, and await/signal idioms used in tween_method and finished handlers.
#### Complements
- [godot-2d-animation](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-2d-animation/SKILL.md) — sprite/skeleton motion that often coexists with Tween juice and needs shared kill/lifecycle discipline.
- [godot-animation-player](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-animation-player/SKILL.md) — authored clips for complex timelines; Tweens stay for runtime/procedural UI and one-off juice.
- [godot-ui-containers](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-containers/SKILL.md) — Control size/pivot/layout context for popup scale-fade and staggered inventory entry tweens.
- [godot-signal-architecture](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-signal-architecture/SKILL.md) — finished/callback wiring without dangling connections when Tweens are killed and recreated.
- [godot-camera-systems](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-camera-systems/SKILL.md) — camera follow, shake offsets, and look targets driven by procedural Tweens.
- [godot-resource-data-patterns](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-resource-data-patterns/SKILL.md) — JuiceConfig-style Resources that store duration/trans/ease outside gameplay scripts.
- [godot-particles](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-particles/SKILL.md) — burst timing and one-shot VFX that should start from tween_callback steps, not parallel ad-hoc timers.
- [godot-shaders-basics](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-shaders-basics/SKILL.md) — shader params animated via tween_method / set when property paths are not enough.
#### Downstream / consumers
- [godot-inventory-system](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-inventory-system/SKILL.md) — slot/item entry, drag feedback, and equip juice built on staggered and interruptible Tweens.
- [godot-genre-card-game](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-genre-card-game/SKILL.md) — hand arcs, draw/discard flights, and resolve polish that depend on Tween chaining.
- [godot-ui-theming](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-ui-theming/SKILL.md) — theme-driven hover/focus motion that still needs safe Tween interruption under the same Control.
#### Master
- [godot-master](https://github.com/thedivergentai/gd-agentic-skills/blob/main/skills/godot-master/SKILL.md) — library router and mirrored module entry for cross-skill discovery.