Refactor rhythm action architecture

This commit is contained in:
wxm
2026-07-02 09:47:52 -07:00
parent fc941cf08d
commit e62ed84518
124 changed files with 7516 additions and 2440 deletions

View File

@@ -0,0 +1,105 @@
class_name ComboWindowHud
extends HBoxContainer
@export var slot_count := 4
var panels: Array[PanelContainer] = []
var labels: Array[Label] = []
var clear_tween: Tween
func _ready() -> void:
_build_slots()
var bus := _event_bus()
bus.connect("combo_updated", refresh)
bus.connect("combo_cleared", _on_combo_cleared)
func refresh(inputs: Array) -> void:
if labels.is_empty():
_build_slots()
for index: int in range(labels.size()):
var filled := index < inputs.size()
labels[index].text = str(inputs[index]) if filled else "."
labels[index].modulate = Color(1.0, 1.0, 1.0, 1.0 if filled else 0.35)
panels[index].modulate = Color(1.0, 1.0, 1.0, 1.0 if filled else 0.48)
if filled:
_pulse_slot(panels[index])
func _on_combo_cleared(_reason: StringName) -> void:
refresh([])
_flash_clear()
func _build_slots() -> void:
if not labels.is_empty():
return
for index: int in range(slot_count):
var panel := PanelContainer.new()
panel.custom_minimum_size = Vector2(64, 56)
panel.pivot_offset = Vector2(32, 28)
panel.modulate = Color(1.0, 1.0, 1.0, 0.48)
panel.add_theme_stylebox_override("panel", _make_slot_style())
var label := Label.new()
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
label.text = "."
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
label.size_flags_vertical = Control.SIZE_EXPAND_FILL
label.add_theme_color_override("font_color", Color(0.94, 0.98, 1.0, 1.0))
label.add_theme_color_override("font_shadow_color", Color(0.0, 0.0, 0.0, 0.9))
label.add_theme_constant_override("shadow_offset_x", 2)
label.add_theme_constant_override("shadow_offset_y", 2)
label.add_theme_font_size_override("font_size", 26)
panel.add_child(label)
add_child(panel)
panels.append(panel)
labels.append(label)
func _pulse_slot(panel: PanelContainer) -> void:
var tween := create_tween()
panel.scale = Vector2(1.08, 1.08)
tween.tween_property(panel, "scale", Vector2.ONE, 0.09)
func _flash_clear() -> void:
if clear_tween != null and clear_tween.is_valid():
clear_tween.kill()
clear_tween = create_tween()
clear_tween.set_parallel(true)
for panel: PanelContainer in panels:
panel.scale = Vector2(1.16, 1.16)
panel.modulate = Color(1.0, 1.0, 1.0, 1.0)
clear_tween.tween_property(panel, "scale", Vector2.ONE, 0.20)
clear_tween.tween_property(panel, "modulate", Color(1.0, 1.0, 1.0, 0.48), 0.20)
func _make_slot_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.content_margin_left = 6.0
style.content_margin_top = 4.0
style.content_margin_right = 6.0
style.content_margin_bottom = 4.0
style.bg_color = Color(0.04, 0.07, 0.09, 0.82)
style.border_width_left = 2
style.border_width_top = 2
style.border_width_right = 2
style.border_width_bottom = 2
style.border_color = Color(0.43, 0.78, 0.88, 0.95)
style.corner_radius_top_left = 6
style.corner_radius_top_right = 6
style.corner_radius_bottom_right = 6
style.corner_radius_bottom_left = 6
return style
func _event_bus() -> Node:
var root := get_tree().root
var bus := root.get_node_or_null("EventBus")
if bus == null:
bus = load("res://autoload/event_bus.gd").new()
bus.name = "EventBus"
root.add_child(bus)
return bus

View File

@@ -0,0 +1 @@
uid://byb77wwjd1a11

View File

@@ -0,0 +1,7 @@
[gd_scene format=3]
[ext_resource type="Script" path="res://scenes/ui/combo_window_hud.gd" id="1"]
[node name="ComboWindowHud" type="HBoxContainer"]
theme_override_constants/separation = 10
script = ExtResource("1")

58
scenes/ui/energy_bar.gd Normal file
View File

@@ -0,0 +1,58 @@
class_name EnergyBar
extends HBoxContainer
@export var segment_count := 10
var segments: Array[Panel] = []
func _ready() -> void:
_build_segments()
_event_bus().connect("player_energy_changed", refresh)
func refresh(current: float, maximum: float) -> void:
if segments.is_empty():
_build_segments()
var filled := clampi(int(round(current)), 0, min(segment_count, int(maximum)))
for index: int in range(segments.size()):
segments[index].modulate = Color(1.0, 1.0, 1.0, 1.0 if index < filled else 0.35)
func _build_segments() -> void:
if not segments.is_empty():
return
for child: Node in get_children():
var panel := child as Panel
if panel != null:
segments.append(panel)
if segments.size() >= segment_count:
return
for index: int in range(segments.size(), segment_count):
var panel := Panel.new()
panel.custom_minimum_size = Vector2(23, 16)
panel.modulate = Color(1.0, 1.0, 1.0, 0.35)
panel.add_theme_stylebox_override("panel", _make_segment_style())
add_child(panel)
segments.append(panel)
func _make_segment_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color(0.18, 0.66, 0.95, 1.0)
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = Color(0.66, 0.92, 1.0, 0.9)
return style
func _event_bus() -> Node:
var root := get_tree().root
var bus := root.get_node_or_null("EventBus")
if bus == null:
bus = load("res://autoload/event_bus.gd").new()
bus.name = "EventBus"
root.add_child(bus)
return bus

View File

@@ -0,0 +1 @@
uid://djef7asrd1q7e

75
scenes/ui/energy_bar.tscn Normal file
View File

@@ -0,0 +1,75 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scenes/ui/energy_bar.gd" id="1"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_segment"]
bg_color = Color(0.18, 0.66, 0.95, 1)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0.66, 0.92, 1, 0.9)
[node name="EnergyBar" type="HBoxContainer"]
theme_override_constants/separation = 4
script = ExtResource("1")
[node name="Segment01" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")
[node name="Segment02" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")
[node name="Segment03" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")
[node name="Segment04" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")
[node name="Segment05" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")
[node name="Segment06" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")
[node name="Segment07" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")
[node name="Segment08" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")
[node name="Segment09" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")
[node name="Segment10" type="Panel" parent="."]
modulate = Color(1, 1, 1, 0.35)
custom_minimum_size = Vector2(23, 16)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_segment")

95
scenes/ui/main_ui.gd Normal file
View File

@@ -0,0 +1,95 @@
class_name MainUI
extends CanvasLayer
@onready var health_bar: ProgressBar = $StatusBars/HealthBar
@onready var charge_bar: ProgressBar = $StatusBars/ChargeBar
@onready var combo_skill_label: Label = $ComboSkillLabel
var charge_bar_ready := false
var charge_flash := 0.0
func _ready() -> void:
_apply_bar_styles()
var bus := _event_bus()
bus.connect("player_health_changed", _on_health_changed)
bus.connect("player_charge_changed", _on_charge_changed)
bus.connect("skill_executed", _on_skill_executed)
func _process(delta: float) -> void:
_update_charge_bar_flash(delta)
func _on_health_changed(current: int, maximum: int) -> void:
health_bar.max_value = max(1, maximum)
health_bar.value = clampi(current, 0, maximum)
func _on_charge_changed(current: float, maximum: float, ready: bool, active: bool) -> void:
charge_bar.max_value = maxf(0.01, maximum)
charge_bar.value = clampf(current, 0.0, maximum)
charge_bar_ready = ready and active
if charge_bar_ready:
return
charge_bar.modulate = Color(1.0, 1.0, 1.0, 1.0 if active or ready else 0.45)
func _on_skill_executed(skill: Resource, _judgement: StringName) -> void:
var display_name := str(skill.get("display_name"))
if display_name.is_empty():
display_name = str(skill.get("id")).to_upper()
combo_skill_label.text = display_name
var tween := create_tween()
combo_skill_label.scale = Vector2(1.12, 1.12)
tween.tween_property(combo_skill_label, "scale", Vector2.ONE, 0.12)
func _update_charge_bar_flash(delta: float) -> void:
if not charge_bar_ready:
charge_flash = 0.0
return
charge_flash = fmod(charge_flash + delta * 7.0, TAU)
var alpha := 0.62 + 0.38 * absf(sin(charge_flash))
charge_bar.modulate = Color(1.0, 1.0, 1.0, alpha)
func _apply_bar_styles() -> void:
health_bar.add_theme_stylebox_override(
"background",
_make_style(Color(0.12, 0.08, 0.08, 0.86), Color(0.6, 0.12, 0.16, 0.95))
)
health_bar.add_theme_stylebox_override(
"fill",
_make_style(Color(0.86, 0.11, 0.18, 1.0), Color.TRANSPARENT, false)
)
charge_bar.add_theme_stylebox_override(
"background",
_make_style(Color(0.08, 0.07, 0.12, 0.86), Color(0.42, 0.36, 0.75, 0.9))
)
charge_bar.add_theme_stylebox_override(
"fill",
_make_style(Color(0.92, 0.72, 0.25, 1.0), Color.TRANSPARENT, false)
)
func _make_style(bg_color: Color, border_color: Color, has_border := true) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = bg_color
if has_border:
style.border_width_left = 1
style.border_width_top = 1
style.border_width_right = 1
style.border_width_bottom = 1
style.border_color = border_color
return style
func _event_bus() -> Node:
var root := get_tree().root
var bus := root.get_node_or_null("EventBus")
if bus == null:
bus = load("res://autoload/event_bus.gd").new()
bus.name = "EventBus"
root.add_child(bus)
return bus

1
scenes/ui/main_ui.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://b3jgnvla0u0u4

57
scenes/ui/main_ui.tscn Normal file
View File

@@ -0,0 +1,57 @@
[gd_scene format=3]
[ext_resource type="Script" path="res://scenes/ui/main_ui.gd" id="1"]
[ext_resource type="PackedScene" path="res://scenes/ui/rhythm_track.tscn" id="2"]
[ext_resource type="PackedScene" path="res://scenes/ui/combo_window_hud.tscn" id="3"]
[ext_resource type="PackedScene" path="res://scenes/ui/energy_bar.tscn" id="4"]
[node name="UI" type="CanvasLayer"]
script = ExtResource("1")
[node name="RhythmTrack" parent="." instance=ExtResource("2")]
[node name="ComboWindow" parent="." instance=ExtResource("3")]
offset_left = 492.0
offset_top = 222.0
offset_right = 788.0
offset_bottom = 282.0
pivot_offset = Vector2(148, 30)
[node name="ComboSkillLabel" type="Label" parent="."]
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -240.0
offset_top = 286.0
offset_right = 240.0
offset_bottom = 322.0
theme_override_colors/font_color = Color(1, 0.84, 0.26, 1)
theme_override_colors/font_shadow_color = Color(0, 0, 0, 0.85)
theme_override_constants/shadow_offset_x = 2
theme_override_constants/shadow_offset_y = 2
theme_override_font_sizes/font_size = 18
horizontal_alignment = 1
vertical_alignment = 1
[node name="StatusBars" type="VBoxContainer" parent="."]
offset_left = 24.0
offset_top = 9.0
offset_right = 294.0
offset_bottom = 69.0
theme_override_constants/separation = 8
[node name="HealthBar" type="ProgressBar" parent="StatusBars"]
custom_minimum_size = Vector2(270, 18)
layout_mode = 2
value = 100.0
show_percentage = false
[node name="EnergyBar" parent="StatusBars" instance=ExtResource("4")]
custom_minimum_size = Vector2(270, 16)
layout_mode = 2
[node name="ChargeBar" type="ProgressBar" parent="StatusBars"]
custom_minimum_size = Vector2(270, 10)
layout_mode = 2
max_value = 1.1
show_percentage = false

174
scenes/ui/rhythm_track.gd Normal file
View File

@@ -0,0 +1,174 @@
class_name RhythmTrack
extends Control
@onready var judgement_label: Label = $JudgementLabel
@onready var center_base: TextureRect = $CenterBase
@onready var center_flash: TextureRect = $CenterFlash
@onready var left_mover: TextureRect = $LeftMover
@onready var right_mover: TextureRect = $RightMover
@onready var chart_marker_container: Control = $ChartMarkerContainer
@export var bpm := 80.0
var chart_markers: Array[Control] = []
var track_center := Vector2.ZERO
var left_mover_start := Vector2.ZERO
var right_mover_start := Vector2.ZERO
var mover_size := Vector2.ZERO
var center_flash_size := Vector2.ZERO
var center_flash_color := Color.WHITE
var beat_flash := 0.0
var beat_age := 0.0
var feedback_flash := 0.0
func _ready() -> void:
_cache_rhythm_track_layout()
center_flash.modulate = Color(1.0, 1.0, 1.0, 0.0)
var bus := _event_bus()
bus.connect("beat_ticked", _on_beat_ticked)
bus.connect("action_judged", _on_action_judged)
bus.connect("chart_event_upcoming", _on_chart_event_upcoming)
bus.connect("chart_event_triggered", _on_chart_event_triggered)
func _process(delta: float) -> void:
var visual_delta := minf(delta, 1.0 / 30.0)
beat_age += delta
beat_flash = maxf(0.0, beat_flash - visual_delta * 8.0)
_update_movers()
if feedback_flash > 0.0:
feedback_flash = maxf(0.0, feedback_flash - visual_delta * 4.0)
judgement_label.scale = Vector2.ONE * (1.0 + feedback_flash * 0.18)
func _on_beat_ticked(_beat_index: int) -> void:
center_flash_color = Color.WHITE
beat_flash = 1.0
beat_age = 0.0
_update_movers()
func _on_action_judged(action_name: StringName, rating: Dictionary) -> void:
var diff := float(rating.get("diff", INF))
var color: Color = rating.get("color", Color("ff0055")) as Color
judgement_label.text = "%s %s %s" % [
str(action_name).to_upper(),
str(rating.get("label", "miss")).to_upper(),
_format_signed_ms(diff),
]
judgement_label.modulate = color
judgement_label.scale = Vector2(1.18, 1.18)
feedback_flash = 1.0
func _on_chart_event_upcoming(event: Resource, time_to_event: float) -> void:
var marker := Label.new()
marker.text = _chart_marker_text(event)
marker.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
marker.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
marker.add_theme_font_size_override("font_size", 14)
marker.add_theme_color_override("font_color", _chart_marker_color(event))
marker.custom_minimum_size = Vector2(54, 24)
marker.position = _chart_marker_position(time_to_event)
chart_marker_container.add_child(marker)
chart_markers.append(marker)
var tween := create_tween()
tween.tween_property(marker, "modulate:a", 0.25, maxf(0.1, time_to_event))
tween.tween_callback(marker.queue_free)
func _on_chart_event_triggered(event: Resource) -> void:
if StringName(str(event.get("event_type"))) == &"camera_pulse":
center_flash_color = Color(1.0, 0.84, 0.26, 1.0)
else:
center_flash_color = _chart_marker_color(event)
beat_flash = 1.0
_update_movers()
func _update_movers() -> void:
var seconds_per_beat := 60.0 / maxf(1.0, bpm)
var progress := clampf(beat_age / seconds_per_beat, 0.0, 1.0)
if beat_flash > 0.15:
progress = 1.0
_set_control_center(left_mover, left_mover_start.lerp(track_center, progress), mover_size)
_set_control_center(right_mover, right_mover_start.lerp(track_center, progress), mover_size)
_set_control_center(center_flash, track_center, center_flash_size)
center_flash.modulate = Color(center_flash_color.r, center_flash_color.g, center_flash_color.b, beat_flash)
func _cache_rhythm_track_layout() -> void:
track_center = _control_center(center_base)
left_mover_start = _control_center(left_mover)
right_mover_start = _control_center(right_mover)
mover_size = left_mover.size
center_flash_size = center_flash.size
func _control_center(control: Control) -> Vector2:
return Vector2(
(control.offset_left + control.offset_right) * 0.5,
(control.offset_top + control.offset_bottom) * 0.5
)
func _set_control_center(control: Control, center: Vector2, size: Vector2) -> void:
control.offset_left = center.x - size.x * 0.5
control.offset_top = center.y - size.y * 0.5
control.offset_right = center.x + size.x * 0.5
control.offset_bottom = center.y + size.y * 0.5
func _chart_marker_text(event: Resource) -> String:
match StringName(str(event.get("event_type"))):
&"show_accent_marker":
return "ACC"
&"enemy_prepare_attack":
return "WARN"
&"enemy_attack_active":
return "ATK"
&"enemy_recovery":
return "REC"
&"camera_pulse":
return "CAM"
return str(event.get("event_type")).to_upper()
func _chart_marker_color(event: Resource) -> Color:
match StringName(str(event.get("event_type"))):
&"show_accent_marker":
return Color("ffd84a")
&"enemy_prepare_attack":
return Color("ff7a33")
&"enemy_attack_active":
return Color("ff3355")
&"enemy_recovery":
return Color("8aa0ff")
&"camera_pulse":
return Color("ffffff")
return Color("00f2ff")
func _chart_marker_position(time_to_event: float) -> Vector2:
var seconds_per_beat := 60.0 / maxf(1.0, bpm)
var beat_distance := clampf(time_to_event / seconds_per_beat, 0.0, 4.0)
var x := track_center.x + beat_distance * 92.0
return Vector2(x - 27.0, track_center.y + 34.0)
func _format_signed_ms(seconds: float) -> String:
if is_inf(seconds):
return "-- ms"
return "%+.0f ms" % (seconds * 1000.0)
func _event_bus() -> Node:
var root := get_tree().root
var bus := root.get_node_or_null("EventBus")
if bus == null:
bus = load("res://autoload/event_bus.gd").new()
bus.name = "EventBus"
root.add_child(bus)
return bus

View File

@@ -0,0 +1 @@
uid://c43c882iifnbi

180
scenes/ui/rhythm_track.tscn Normal file
View File

@@ -0,0 +1,180 @@
[gd_scene format=3 uid="uid://csydrlqpqyx3s"]
[ext_resource type="Script" uid="uid://c43c882iifnbi" path="res://scenes/ui/rhythm_track.gd" id="1"]
[ext_resource type="Texture2D" uid="uid://brqr1gyyxth8p" path="res://assets/ui/rhythm/center.png" id="2_center"]
[ext_resource type="Texture2D" uid="uid://bkqec7mh5yfrd" path="res://assets/ui/rhythm/center_flash.png" id="3_center_flash"]
[ext_resource type="Texture2D" uid="uid://cj5pa4c3arevn" path="res://assets/ui/rhythm/rod.png" id="4_rod"]
[ext_resource type="Texture2D" uid="uid://dbmdivnpjf48l" path="res://assets/ui/rhythm/blue_ball.png" id="5_blue_ball"]
[ext_resource type="Texture2D" uid="uid://ewr8k3lwpcna" path="res://assets/ui/rhythm/yellow_ball.png" id="6_yellow_ball"]
[node name="RhythmTrack" type="Control" unique_id=1294325361]
layout_mode = 3
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -520.0
offset_top = 28.0
offset_right = 520.0
offset_bottom = 222.0
grow_horizontal = 2
script = ExtResource("1")
[node name="LeftRod" type="TextureRect" parent="." unique_id=956987652]
layout_mode = 0
offset_left = 64.0
offset_top = 60.0
offset_right = 464.0
offset_bottom = 84.0
texture = ExtResource("4_rod")
expand_mode = 1
stretch_mode = 5
[node name="LeftRod" type="TextureRect" parent="LeftRod" unique_id=1055715767]
layout_mode = 0
offset_left = 130.0
offset_top = 1.0
offset_right = 530.0
offset_bottom = 25.0
texture = ExtResource("4_rod")
expand_mode = 1
stretch_mode = 5
[node name="LeftRod" type="TextureRect" parent="LeftRod/LeftRod" unique_id=475076301]
layout_mode = 0
offset_left = 257.0
offset_top = 1.0
offset_right = 657.0
offset_bottom = 25.0
texture = ExtResource("4_rod")
expand_mode = 1
stretch_mode = 5
[node name="RightRod" type="TextureRect" parent="." unique_id=1615082011]
layout_mode = 0
offset_left = 576.0
offset_top = 60.0
offset_right = 976.0
offset_bottom = 84.0
texture = ExtResource("4_rod")
expand_mode = 1
stretch_mode = 5
[node name="BlueBallLeft1" type="TextureRect" parent="." unique_id=1528330935]
layout_mode = 0
offset_left = 184.0
offset_top = 49.0
offset_right = 228.0
offset_bottom = 93.0
texture = ExtResource("5_blue_ball")
expand_mode = 1
stretch_mode = 5
[node name="BlueBallLeft2" type="TextureRect" parent="." unique_id=1648598230]
layout_mode = 0
offset_left = 309.0
offset_top = 50.0
offset_right = 353.0
offset_bottom = 94.0
texture = ExtResource("5_blue_ball")
expand_mode = 1
stretch_mode = 5
[node name="BlueBallLeft3" type="TextureRect" parent="." unique_id=1872499202]
layout_mode = 0
offset_left = 427.0
offset_top = 51.0
offset_right = 471.0
offset_bottom = 95.0
texture = ExtResource("5_blue_ball")
expand_mode = 1
stretch_mode = 5
[node name="BlueBallRight1" type="TextureRect" parent="." unique_id=1519743424]
layout_mode = 0
offset_left = 567.0
offset_top = 52.0
offset_right = 611.0
offset_bottom = 96.0
texture = ExtResource("5_blue_ball")
expand_mode = 1
stretch_mode = 5
[node name="BlueBallRight2" type="TextureRect" parent="." unique_id=1004523117]
layout_mode = 0
offset_left = 687.0
offset_top = 52.0
offset_right = 731.0
offset_bottom = 96.0
texture = ExtResource("5_blue_ball")
expand_mode = 1
stretch_mode = 5
[node name="BlueBallRight3" type="TextureRect" parent="." unique_id=1902582723]
layout_mode = 0
offset_left = 813.0
offset_top = 52.0
offset_right = 857.0
offset_bottom = 96.0
texture = ExtResource("5_blue_ball")
expand_mode = 1
stretch_mode = 5
[node name="LeftMover" type="TextureRect" parent="." unique_id=790581017]
layout_mode = 0
offset_left = 183.0
offset_top = 47.0
offset_right = 227.0
offset_bottom = 91.0
texture = ExtResource("6_yellow_ball")
expand_mode = 1
stretch_mode = 5
[node name="RightMover" type="TextureRect" parent="." unique_id=46330219]
layout_mode = 0
offset_left = 815.0
offset_top = 52.0
offset_right = 859.0
offset_bottom = 96.0
texture = ExtResource("6_yellow_ball")
expand_mode = 1
stretch_mode = 5
[node name="CenterBase" type="TextureRect" parent="." unique_id=652811094]
layout_mode = 0
offset_left = 464.0
offset_top = 16.0
offset_right = 576.0
offset_bottom = 128.0
texture = ExtResource("2_center")
expand_mode = 1
stretch_mode = 5
[node name="CenterFlash" type="TextureRect" parent="." unique_id=1409206211]
modulate = Color(1, 1, 1, 0)
layout_mode = 0
offset_left = 440.0
offset_top = -8.0
offset_right = 600.0
offset_bottom = 152.0
texture = ExtResource("3_center_flash")
expand_mode = 1
stretch_mode = 5
[node name="ChartMarkerContainer" type="Control" parent="."]
layout_mode = 0
offset_left = 0.0
offset_top = 0.0
offset_right = 1040.0
offset_bottom = 128.0
mouse_filter = 2
[node name="JudgementLabel" type="Label" parent="." unique_id=1712665799]
layout_mode = 0
offset_left = 280.0
offset_top = 146.0
offset_right = 760.0
offset_bottom = 194.0
theme_override_font_sizes/font_size = 24
text = "READY"
horizontal_alignment = 1
vertical_alignment = 1