Refactor rhythm action architecture
This commit is contained in:
105
scenes/ui/combo_window_hud.gd
Normal file
105
scenes/ui/combo_window_hud.gd
Normal 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
|
||||
1
scenes/ui/combo_window_hud.gd.uid
Normal file
1
scenes/ui/combo_window_hud.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://byb77wwjd1a11
|
||||
7
scenes/ui/combo_window_hud.tscn
Normal file
7
scenes/ui/combo_window_hud.tscn
Normal 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
58
scenes/ui/energy_bar.gd
Normal 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
|
||||
1
scenes/ui/energy_bar.gd.uid
Normal file
1
scenes/ui/energy_bar.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://djef7asrd1q7e
|
||||
75
scenes/ui/energy_bar.tscn
Normal file
75
scenes/ui/energy_bar.tscn
Normal 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
95
scenes/ui/main_ui.gd
Normal 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
1
scenes/ui/main_ui.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b3jgnvla0u0u4
|
||||
57
scenes/ui/main_ui.tscn
Normal file
57
scenes/ui/main_ui.tscn
Normal 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
174
scenes/ui/rhythm_track.gd
Normal 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
|
||||
1
scenes/ui/rhythm_track.gd.uid
Normal file
1
scenes/ui/rhythm_track.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c43c882iifnbi
|
||||
180
scenes/ui/rhythm_track.tscn
Normal file
180
scenes/ui/rhythm_track.tscn
Normal 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
|
||||
Reference in New Issue
Block a user