Godot Version
godot 4.3
Question
Hello, I have an issue in my game, I have been working to improve my UI in my main scene by adding controllable music, but one issue, whenever I skip a song or check the playlist, my frame rate decreases by around 25-50% and if i spam click it, my game freezes and crashes. Here is a video showing what is happening.
Here is some important code to help with this:
UI.gd (THE MAIN SCENE)
#UI.gd
extends Control
var skip_track_cooldown: bool = false
var focusable_buttons: Array = []
var update_timer: Timer
@onready var sidebar_button: TextureButton = $SidebarButton
@onready var sidebar_panel: Panel = $SidebarPanel
@onready var tween2: Tween
@onready var sidebar_playlist_label: Label = $SidebarPanel/PlaylistLabel
@onready var playlist_scroll_container: ScrollContainer = $SidebarPanel/ScrollContainer
@onready var playlist_container: VBoxContainer = $SidebarPanel/ScrollContainer/PlaylistContainer
@onready var pause_button: Button = $PauseButton
func _ready():
if not is_in_group("ui"):
add_to_group("ui")
get_viewport().size = DisplayServer.screen_get_size()
get_window().grab_focus()
if not $MarginContainer/VBoxContainer/LeaderboardButton.is_connected("pressed", Callable(self, "_on_Leaderboard_pressed")):
$MarginContainer/VBoxContainer/LeaderboardButton.connect("pressed", Callable(self, "_on_Leaderboard_pressed"))
if not credits.is_connected("pressed", Callable(self, "_on_CreditsButton_pressed")):
credits.connect("pressed", Callable(self, "_on_CreditsButton_pressed"))
sidebar_panel.position.y = get_viewport_rect().size.y
if not sidebar_button.is_connected("pressed", Callable(self, "_on_sidebar_button_pressed")):
sidebar_button.connect("pressed", Callable(self, "_on_sidebar_button_pressed"))
sidebar_playlist_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
sidebar_playlist_label.text = "Songs in playlist:"
# Initialize and start the update timer
update_timer = Timer.new()
update_timer.wait_time = 1.0 # Update every second
update_timer.connect("timeout", Callable(self, "_on_update_timer_timeout"))
add_child(update_timer)
update_timer.start()
music_progress_bar.connect("gui_input", Callable(self, "_on_music_progress_gui_input"))
pause_button.text = "Pause"
print("UI: Checking music status")
MusicManager.ensure_playing()
# Add the new buttons to the focusable_buttons array
focusable_buttons.append(ultimate_button)
focusable_buttons.append(simple_button)
# Set up the layout
setup_layout()
# Initialize AudioStreamPlayer reference and set the sound stream
button_click_player = $ButtonClickPlayer
button_click_player.stream = load("res://Sounds/click.ogg")
# Register the music player with AudioManager
AudioManager.register_music_player(MusicManager.music_player)
# Register the button click player with AudioManager
if button_click_player:
AudioManager.register_sfx_player(button_click_player)
# Load and apply audio settings
AudioManager.load_and_apply_settings()
# Connect the SkipTrack button
if not skip_track.is_connected("pressed", Callable(self, "_on_SkipTrack_pressed")):
skip_track.connect("pressed", Callable(self, "_on_SkipTrack_pressed"))
if not pause_button.is_connected("pressed", Callable(self, "_on_pause_button_pressed")):
pause_button.connect("pressed", Callable(self, "_on_pause_button_pressed"))
# Set up focusable buttons in the desired order
focusable_buttons = [play, settings, stats, leaderboard, exit, credits, skip_track, youtube_button, github_button, discord_button, sidebar_button, pause_button]
for button in focusable_buttons:
button.focus_mode = Control.FOCUS_ALL
get_tree().root.size_changed.connect(self.on_window_resized)
# Set initial focus
set_initial_focus()
update_currently_playing_label()
update_playlist_display()
MusicManager.music_player.connect("finished", Callable(self, "update_currently_playing_label"))
MusicManager.music_player.connect("finished", Callable(self, "update_playlist_display"))
var button_style = StyleBoxFlat.new()
button_style.bg_color = Color(0.2, 0.2, 0.2) # Greyish-blackish color
button_style.border_width_bottom = 4
button_style.border_color = Color(0.1, 0.1, 0.1)
ultimate_button.add_theme_stylebox_override("normal", button_style)
simple_button.add_theme_stylebox_override("normal", button_style)
ultimate_button.text = "Ultimate Tic Tac Toe"
simple_button.text = "Simple Tic Tac Toe"
func update_playlist_display():
var playlist_size = MusicManager.playlist.size()
var children = playlist_container.get_children()
var child_count = children.size()
for i in range(playlist_size):
var track_path = MusicManager.playlist[i]
var track_name = track_path.get_file().get_basename()
var audio_stream = load(track_path)
var duration = audio_stream.get_length() if audio_stream else 0.0
var item: Button
if i < child_count:
item = children[i]
else:
item = Button.new()
item.flat = true
item.alignment = HORIZONTAL_ALIGNMENT_LEFT
item.size_flags_horizontal = Control.SIZE_EXPAND_FILL
playlist_container.add_child(item)
item.text = "%s - %s" % [track_name, format_time(duration)]
item.set_meta("track_index", i)
if not item.is_connected("pressed", Callable(self, "_on_playlist_item_pressed")):
item.connect("pressed", Callable(self, "_on_playlist_item_pressed").bind(item))
if i == MusicManager.current_track_index:
item.add_theme_color_override("font_color", Color.YELLOW)
item.add_theme_color_override("font_outline_color", Color.BLACK)
item.add_theme_constant_override("outline_size", 2)
else:
item.remove_theme_color_override("font_color")
item.remove_theme_color_override("font_outline_color")
item.remove_theme_constant_override("outline_size")
# Remove excess children
while child_count > playlist_size:
var child = children[child_count - 1]
playlist_container.remove_child(child)
child.queue_free()
child_count -= 1
playlist_scroll_container.queue_sort()
func _on_playlist_item_pressed(item: Button):
var track_index = item.get_meta("track_index")
MusicManager.play_specific_track(track_index)
pause_button.text = "Pause"
call_deferred("update_playlist_display")
call_deferred("update_currently_playing_label")
func _on_music_progress_gui_input(event: InputEvent):
if event is InputEventMouseButton or (event is InputEventMouseMotion and event.button_mask & MOUSE_BUTTON_MASK_LEFT):
var ratio = clamp(event.position.x / music_progress_bar.size.x, 0, 1)
var new_position = ratio * MusicManager.music_player.stream.get_length()
MusicManager.music_player.seek(new_position)
update_progress_bar(new_position)
func update_progress_bar(current_time: float):
var track_duration = MusicManager.music_player.stream.get_length()
start_timer_label.text = format_time(current_time)
end_timer_label.text = format_time(track_duration)
if track_duration > 0:
music_progress_bar.value = (current_time / track_duration) * 100
func format_time(seconds: float) -> String:
@warning_ignore("integer_division")
var minutes = int(seconds) / 60
var secs = int(seconds) % 60
return "%02d:%02d" % [minutes, secs]
func _on_update_timer_timeout():
if MusicManager.is_playing():
var current_time = MusicManager.music_player.get_playback_position()
update_progress_bar(current_time)
func _on_SkipTrack_pressed():
if skip_track_cooldown:
return
skip_track_cooldown = true
_play_button_click_sound()
MusicManager.skip_and_shuffle()
pause_button.text = "Pause"
call_deferred("update_playlist_display")
call_deferred("update_currently_playing_label")
get_tree().create_timer(0.3).connect("timeout", func(): skip_track_cooldown = false)
func update_currently_playing_label():
if MusicManager.is_playing():
var current_track = MusicManager.playlist[MusicManager.current_track_index]
var track_name = current_track.get_file().get_basename()
currently_playing_label.text = "Now Playing: " + track_name
else:
currently_playing_label.text = "No track playing"
func _on_sidebar_button_pressed():
_play_button_click_sound()
update_playlist_display()
tween2 = create_tween()
var panel_start_x = 1 # X coordinate
var panel_start_y = 568 # Y coordinate
var panel_end_y = get_viewport_rect().size.y
if sidebar_panel.position.y >= panel_end_y:
tween2.tween_property(sidebar_panel, "position", Vector2(panel_start_x, panel_start_y), 0.3).set_ease(Tween.EASE_OUT)
else:
tween2.tween_property(sidebar_panel, "position", Vector2(panel_start_x, panel_end_y), 0.3).set_ease(Tween.EASE_IN)
func _on_pause_button_pressed():
_play_button_click_sound()
if MusicManager.music_player.stream:
if MusicManager.music_player.stream_paused:
MusicManager.music_player.stream_paused = false
pause_button.text = "Pause"
print("Music resumed")
else:
MusicManager.music_player.stream_paused = true
pause_button.text = "Resume"
print("Music paused")
else:
MusicManager.play()
pause_button.text = "Pause"
print("Music started playing")
Here is my MusicManager.gd (The autoload singleton that controls part of the music:)
#MusicManager.gd
extends Node
var music_player: AudioStreamPlayer
var is_initialized: bool = false
var playlist: Array = []
var current_track_index: int = -1
func skip_and_shuffle():
shuffle_playlist()
play_next_track()
func _enter_tree():
if not is_initialized:
music_player = AudioStreamPlayer.new()
add_child(music_player)
is_initialized = true
print("MusicManager: Initialized")
# Initialize the playlist
playlist = ["my music"]
# Shuffle the playlist
shuffle_playlist()
# Connect the finished signal
music_player.connect("finished", Callable(self, "_on_track_finished"))
func shuffle_playlist():
randomize() # Initialize random number generator
playlist.shuffle()
current_track_index = -1 # Reset the index
print("MusicManager: Playlist shuffled")
func set_volume(volume_db: float):
if music_player:
music_player.volume_db = volume_db
func play_next_track():
current_track_index = (current_track_index + 1) % playlist.size()
if current_track_index == 0:
shuffle_playlist()
var next_track = load(playlist[current_track_index])
music_player.stream = next_track
music_player.play()
print("MusicManager: Playing track - ", playlist[current_track_index])
get_tree().call_group("ui", "update_currently_playing_label")
func play():
if not is_playing():
play_next_track()
else:
print("MusicManager: Music already playing")
func stop():
if music_player and music_player.playing:
music_player.stop()
print("MusicManager: Stopping music playback")
func is_playing() -> bool:
return music_player and music_player.playing
func ensure_playing():
if not is_playing():
play()
else:
print("MusicManager: Music already playing, no action needed")
func _on_track_finished():
play_next_track()
func play_specific_track(index: int):
if index >= 0 and index < playlist.size():
current_track_index = index
var next_track = load(playlist[current_track_index])
music_player.stream = next_track
music_player.play()
print("MusicManager: Playing track - ", playlist[current_track_index])
get_tree().call_group("ui", "update_currently_playing_label")
Here is my SceneTree for UI.tscn:
Any help would be very much appreciated! Thank you, and have a great night/day!
Let me know if you need any code snippets or SceneTrees!