From 8feb7bbc47eb90f077baaec6fa7376fe58062ce5 Mon Sep 17 00:00:00 2001 From: hz Date: Fri, 31 Oct 2025 22:07:05 -0500 Subject: [PATCH] Added a sound effect when a menu option is hovered --- assets/audio/silly-menu-hover-test.ogg | Bin 0 -> 6133 bytes assets/audio/silly-menu-hover-test.ogg.import | 19 +++++ assets/audio/silly-test.ogg.import | 8 +- audio/bus_layout.tres | 12 +++ project.godot | 5 ++ scenes/Levels/menu.gd | 21 +++++ scenes/UI/Settings.gd | 75 ++++++++++++++--- scenes/UI/Settings.tscn | 37 ++++++++- scenes/UI/menu_music.gd | 16 ++-- scenes/UI/menu_music.tscn | 2 + scenes/UI/menu_sfx.gd | 76 ++++++++++++++++++ scenes/UI/menu_sfx.gd.uid | 1 + scenes/UI/menu_sfx.tscn | 13 +++ scenes/UI/start_screen.gd | 16 ++++ 14 files changed, 277 insertions(+), 24 deletions(-) create mode 100644 assets/audio/silly-menu-hover-test.ogg create mode 100644 assets/audio/silly-menu-hover-test.ogg.import create mode 100644 audio/bus_layout.tres create mode 100644 scenes/UI/menu_sfx.gd create mode 100644 scenes/UI/menu_sfx.gd.uid create mode 100644 scenes/UI/menu_sfx.tscn diff --git a/assets/audio/silly-menu-hover-test.ogg b/assets/audio/silly-menu-hover-test.ogg new file mode 100644 index 0000000000000000000000000000000000000000..2d8ad2bc5dde5d3ee971f2821ffa87238a0f40af GIT binary patch literal 6133 zcmeG=c~q0f_7j#6Rv|86hk} zL=8yT1QY}mt%@Q6WEDg#Dq2x+sc50qE>_=s3GJ~x@0{0jUjO=?-<-*0=H5H^esh<( z_xsjw+qMCyfQ3egy`+y!C5^5*C^J;j&iF9C00F4D)+2wg{eV*ZokgujcK*?jod|}% zbjwq0(7V4hM^z;&CZyQNkBeHlK7Jc9h94H75J2=K+F09ITd%fe5-Hrsh{SOIj%`G@ z9ZCH7ojYP9cO*~|&RFUBL_IpNMJtY{=yI zL2(Wud=?0dPyi9mN`A^?5rGh@0(j=*`kDv>*9imP6NU^d{jiqTZLCJQwi`Zl;`zIc z`SU*I1qbhn9ZQNGD~=rxP)q`11KVP6|G6uUPDBBzg@f3FDmJHzr|iXQX&Ln@AOcZ| zl#)GVFM8-9vgC`mCKqqZlGKz+9@ie#Qjh=?BthE!FS2L85N%5qD@I8*UsCmVJ1eS( z23Sy+-JcVb-94VHq0n#-fHq}u$=&gEcLB3o;OL&<+MVFtTj<~0x=H(YBS5pm#xkV4 zZE)-^a2ztU9U5g082b1zeMbLA-+)U|_zOG;>B!dN?Ly3ld7%om2q9a1ImSgtDG3r} zmOms)h9&a@4tkPD(puc%(aTx>hnKh4mK+ugI?R;C44O=p2?nXxO1q?`!xF)ur;bFX zoF!uqYc$l3K$yu)nV#Gv6w>iWBpS8fY=nCQhb7ZB?Kn8gDm=WrwcTIIPr+0$Si8Aw z@Up|Va7Wg};pleFU^x3ZqHK0R6M;dw3xlS@eOS+`(Q)SACf<`4qWuqev1L+CP1$nMeb7}d#OS@BF%FY49tVRdF2YQ zMwlIQOxC>P!uo&uBm1Z_hmgL&1qxb^L&2_Gcg#6<7&>(ry8TZ=7B&<9A`6}f3K}w8 zkOfZ!1^q*^{)y-Pe;)s*H2~cXr3ZgT>~hiy6`?%}>?b>miJrquVMO3CGs@K4kQivh z)Rmw|mFXSP25Sx?n7S31Jmw+^+Gii-kGc^M`4AyN`>A;#h$`pbV+igV_BO1*_@(hx z97oeAY+R`1KPy%UfDoktguw6`>yKxu*k}ZRy*NA&)))5TNE8r823EjqUo6Wq|Icjx zKJ+gFQ9=p;`=P)%LZ6B#Jdh=(Yi|hz1FBJ`EF20URzaR_3DeR@gTjok1p4aWzAvtC zgz5LtzF5Yu=*g#hjk@MX@+C8+%a{Z6-Ubyj$yJ&|^YGmK?20-Sm|mdT)e*JZMRDkQ zzEM3KKqP~XZ|#d8C09I7u85Mt`3<(P~FpWECdEY!N!?NmW|I; z7Kn<1h5Wj@I+$-%{2{@3Vx)-_+0wY84~!=Uj~7J-qVGfOwPrdrL4an1TIHeMY!6ZdmrJ9{wAeJuBCfNCtdeWPv62;Y)k#HS zPA##L%cZIV;Zl3}+lWces8<7Xu-XeplJ*Rl&0$QJ39uv?9#%HBxNxM#DHJncp|g(j zqy)m2%Od(Atbn@)DSkYuvIIy1YissnLISKmjtKPFD8N!`J3D5bHP&CD?!}^*4slD~q_JMZ1MQ)>2Ye4jXIw*0p2Q155_MM!Oz;C1C;4Og? z-hnS+&`H7(Uw|gZ)N*qj;Df;zr}9EBSK&Rd8uVpTWtF*%X$1nQPx;iyeg^~rE7igg z?z1uh+@UC3EoC?0d6Wn)hhqgY4)f@~D5-~a`2ISU9b#|!d9WGJwT(SKn{mEAzLDX~G(P{CZ9lCI@OP&8z( zq~kY0K^$t4l0Ce3Q&a&N=u=R6PDMXaU@;fiDE;$;v&oCjIvsCduCsYNn$|Emfxw{dbGsVHTfAFt4Y_? z2VvCjT|d*&){$O_a-H-cTm`=r-MMr1*paF%`e%s;s3E-rK5t6-lKy7ic;*~9Zp^cd zG_Xos^5lseWjX7u1F;6gd1>RrkfoOrG;SX5PmR?Eo7B~|Nf}T$#z2v^t2VZN)Y*2V z%EX0Hkxl9zPGiW1?a14!Ma{mdUM^PQ>g9{W(#vyg@AZ`$*nUDNM*%FW-wA7&*;BLn z?(Eju^WMgBJ5Pi(eHjWJ83b?=Lik_CcN7;X}R2 ze6(}2BkXIvsF2=+8{bar(&f*DZ9DyRY%F%*V$D;Q1CXkg2A>X4iZ_aQ_22F6W7IdS z*e20wqQS?$j;mHG0A)BjdSgneAGzl#=F#HKz+?wF9xyz_cu8Fp5UHkUU z-RQN4rg$+;w(G1ZLm$q~%ub%cpR40sMCac!Fkt;0bX5lrI!?t_mm1#iS3Oac-kQFJ zU(JjD=u|^n{g_-{7t|!jY1dDb;Ek{cXmK^HErt{w)pVntP;{H)cu4alOzF?KA$c<` zF4J0?CKu*oKg)MX)v@}9DAQe=S7DvP)`-`%xRc^ywK6nRZTQRCo6Al{#+#*=o1;Se zDF&q*4cw2D@-cqq@lnLpF`HLlT=8xtMDxj^P!pqRY*wD--1dU9XlxYg1ZxPDC$C6u^C}W2T&R$XLp>62HJvr z&AGnNqW!MXqkdh{sq8DM21d-an{(+P9R)OX0E;1NNj{y+5_LM+q0Z71UJJK3>l2)% zFIiUJI*X&1Xpj1mO`9x2)lSE4c>^dPd}!c{FWhGxhqoVDEeyl~RePXs=*Nhm`~M_5 z@w^`o2B+p?F1Coi(y0aiD!!APbm@y#o22R2reoCH?g!3wb^bbi*gC45#*6u3`KG6r zC*(D>WyWH213XAUBW~PiZ|i9Gea`+7w}SJdf>SMLT3GINd)n5%V#a?IQEw(noP13_ zBe5GDx^&D??YFm&JnoepJ68Q|bNj88t3G*qW^lv9$-a{(v`W-{8wDk&pKn@3U(`5C zeEx2){9_Gaog~z5%(Cak72lzzaOsH1GfzzGd2`9|*p&!`DACCIJ?thqET?h6$9X_uf8xknSAa=B!81v%dB=+ z$ez1P_Kex|MD(OOsQXS(u;R|Kr2E}5a=xId5iHv*-%*H{Iw z=Di+xyfBV9eKh*rgCCktV116L3H@^NZuSKx+a2>=EqZj@;@g);^={t&A*{TUC;qs> zI%D(KuVS%&CR#9ubUym|ka6+{8s0~8x;KBkOJBPWFWh|4P-h6F(pY{|Q`D!gz1~e* zJ4|ldyMD1*@tYUkFHTgk&IH;N-}&lf$ZhpF)gOM{lBe4dhZQ5Au9YpZCX$_T;beF| z|8B2DV1K&#>s2RD9^8B9*XeC0my)L+c8*=I9+>{-TI`{ void: + _register_focus_sounds() + func _input(event): if event.is_action_pressed("ui_cancel"): if get_tree().paused: @@ -22,3 +25,21 @@ func _on_quit_button_pressed(): func _on_continue_button_pressed(): resume_game() + +func _register_focus_sounds() -> void: + if pause_menu == null: + return + var vbox := pause_menu.get_node_or_null("VBoxContainer") + if vbox == null: + return + for child in vbox.get_children(): + if child is BaseButton: + var button: BaseButton = child + if not button.is_connected("focus_entered", Callable(self, "_on_menu_item_focus")): + button.focus_entered.connect(_on_menu_item_focus) + if not button.is_connected("mouse_entered", Callable(self, "_on_menu_item_focus")): + button.mouse_entered.connect(_on_menu_item_focus) + +func _on_menu_item_focus() -> void: + if MenuSfx: + MenuSfx.play_hover() diff --git a/scenes/UI/Settings.gd b/scenes/UI/Settings.gd index 3a2c0d7..2b4d84b 100644 --- a/scenes/UI/Settings.gd +++ b/scenes/UI/Settings.gd @@ -5,7 +5,11 @@ extends Node2D @onready var _music_volume_slider: HSlider = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup/MusicVolumeSlider @onready var _music_volume_value: Label = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup/MusicVolumeValue @onready var _music_mute_checkbox: CheckBox = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup/MusicMuteCheckBox +@onready var _sfx_volume_slider: HSlider = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup/SfxVolumeSlider +@onready var _sfx_volume_value: Label = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup/SfxVolumeValue +@onready var _sfx_mute_checkbox: CheckBox = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup/SfxMuteCheckBox @onready var _menu_music: AudioStreamPlayer = get_tree().get_root().get_node_or_null("MenuMusic") +@onready var _menu_sfx: AudioStreamPlayer = get_tree().get_root().get_node_or_null("MenuSfx") func _ready() -> void: _tab_bar.tab_changed.connect(_on_tab_bar_tab_changed) @@ -13,7 +17,10 @@ func _ready() -> void: _tab_container.current_tab = _tab_bar.current_tab _music_volume_slider.value_changed.connect(_on_music_volume_changed) _music_mute_checkbox.toggled.connect(_on_music_mute_toggled) + _sfx_volume_slider.value_changed.connect(_on_sfx_volume_changed) + _sfx_mute_checkbox.toggled.connect(_on_sfx_mute_toggled) _sync_audio_controls() + _register_focus_sounds() func _input(event): if event.is_action_pressed("ui_cancel"): @@ -35,29 +42,71 @@ func _sync_audio_controls() -> void: value = _menu_music.get_user_volume() if _menu_music.has_method("is_user_muted"): muted = _menu_music.is_user_muted() - _music_volume_slider.set_block_signals(true) - _music_volume_slider.value = value - _music_volume_slider.set_block_signals(false) - _music_volume_slider.editable = not muted - _music_mute_checkbox.set_block_signals(true) - _music_mute_checkbox.button_pressed = muted - _music_mute_checkbox.set_block_signals(false) - _update_music_volume_label(value, muted) + _apply_audio_control_state(_music_volume_slider, _music_mute_checkbox, _music_volume_value, value, muted) + + var sfx_value: float = 0.7 + var sfx_muted: bool = false + if _menu_sfx: + if _menu_sfx.has_method("get_user_volume"): + sfx_value = _menu_sfx.get_user_volume() + if _menu_sfx.has_method("is_user_muted"): + sfx_muted = _menu_sfx.is_user_muted() + _apply_audio_control_state(_sfx_volume_slider, _sfx_mute_checkbox, _sfx_volume_value, sfx_value, sfx_muted) func _on_music_volume_changed(value: float) -> void: if _menu_music and _menu_music.has_method("set_user_volume"): _menu_music.set_user_volume(value) - _update_music_volume_label(value, _music_mute_checkbox.button_pressed) + _update_volume_label(_music_volume_value, value, _music_mute_checkbox.button_pressed) func _on_music_mute_toggled(pressed: bool) -> void: if _menu_music and _menu_music.has_method("set_user_muted"): _menu_music.set_user_muted(pressed) _music_volume_slider.editable = not pressed - _update_music_volume_label(_music_volume_slider.value, pressed) + _update_volume_label(_music_volume_value, _music_volume_slider.value, pressed) -func _update_music_volume_label(value: float, muted: bool) -> void: +func _on_sfx_volume_changed(value: float) -> void: + if _menu_sfx and _menu_sfx.has_method("set_user_volume"): + _menu_sfx.set_user_volume(value) + _update_volume_label(_sfx_volume_value, value, _sfx_mute_checkbox.button_pressed) + +func _on_sfx_mute_toggled(pressed: bool) -> void: + if _menu_sfx and _menu_sfx.has_method("set_user_muted"): + _menu_sfx.set_user_muted(pressed) + _sfx_volume_slider.editable = not pressed + _update_volume_label(_sfx_volume_value, _sfx_volume_slider.value, pressed) + +func _apply_audio_control_state(slider: HSlider, checkbox: CheckBox, value_label: Label, value: float, muted: bool) -> void: + slider.set_block_signals(true) + slider.value = value + slider.set_block_signals(false) + slider.editable = not muted + checkbox.set_block_signals(true) + checkbox.button_pressed = muted + checkbox.set_block_signals(false) + _update_volume_label(value_label, value, muted) + +func _update_volume_label(value_label: Label, value: float, muted: bool) -> void: if muted: - _music_volume_value.text = "Muted" + value_label.text = "Muted" else: var percent: int = int(round(value * 100.0)) - _music_volume_value.text = str(percent) + "%" + value_label.text = str(percent) + "%" + +func _register_focus_sounds() -> void: + _connect_focus_recursive(self) + +func _connect_focus_recursive(node: Node) -> void: + if node is Control: + var control: Control = node + if control.focus_mode != Control.FOCUS_NONE: + if not control.is_connected("focus_entered", Callable(self, "_on_menu_item_focus")): + control.focus_entered.connect(_on_menu_item_focus) + if control.has_signal("mouse_entered") and (control is BaseButton or control.focus_mode != Control.FOCUS_NONE): + if not control.is_connected("mouse_entered", Callable(self, "_on_menu_item_focus")): + control.mouse_entered.connect(_on_menu_item_focus) + for child in node.get_children(): + _connect_focus_recursive(child) + +func _on_menu_item_focus() -> void: + if MenuSfx: + MenuSfx.play_hover() diff --git a/scenes/UI/Settings.tscn b/scenes/UI/Settings.tscn index 836a799..f325614 100644 --- a/scenes/UI/Settings.tscn +++ b/scenes/UI/Settings.tscn @@ -78,9 +78,9 @@ grow_horizontal = 2 grow_vertical = 2 theme_override_constants/separation = 10 offset_left = 120.0 -offset_top = 120.0 +offset_top = 240.0 offset_right = -120.0 -offset_bottom = -120.0 +offset_bottom = -40.0 [node name="MusicVolumeLabel" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"] layout_mode = 2 @@ -115,6 +115,39 @@ layout_mode = 2 text = "Mute" size_flags_horizontal = 1 +[node name="SfxVolumeLabel" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"] +layout_mode = 2 +text = "Menu SFX Volume" +horizontal_alignment = 1 +size_flags_horizontal = 4 + +[node name="SfxVolumeGroup" type="HBoxContainer" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"] +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_constants/separation = 12 +alignment = 1 +custom_minimum_size = Vector2(0, 40) + +[node name="SfxVolumeSlider" type="HSlider" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup"] +layout_mode = 2 +min_value = 0.0 +max_value = 1.0 +step = 0.01 +size_flags_horizontal = 3 +custom_minimum_size = Vector2(320, 0) + +[node name="SfxVolumeValue" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup"] +layout_mode = 2 +text = "70%" +size_flags_horizontal = 1 +horizontal_alignment = 1 +custom_minimum_size = Vector2(60, 0) + +[node name="SfxMuteCheckBox" type="CheckBox" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup"] +layout_mode = 2 +text = "Mute" +size_flags_horizontal = 1 + [node name="Dev" type="Control" parent="MarginContainer/VBoxContainer/TabContainer"] layout_mode = 3 anchors_preset = 15 diff --git a/scenes/UI/menu_music.gd b/scenes/UI/menu_music.gd index 098b414..23de7c4 100644 --- a/scenes/UI/menu_music.gd +++ b/scenes/UI/menu_music.gd @@ -25,22 +25,28 @@ func _process(_delta: float) -> void: var current := get_tree().current_scene if current != _last_scene: _update_playback(current) + elif _should_play_scene(current) and not playing and not _is_muted and _user_volume_linear > 0.0: + play() func _update_playback(scene: Node) -> void: if scene == null: _last_scene = null return - _last_scene = scene - var should_play := false - var scene_path := scene.get_scene_file_path() - should_play = MENU_SCENES.has(scene_path) - if should_play: + if _should_play_scene(scene): if not playing: play() elif playing: stop() +func _should_play_scene(scene: Node) -> bool: + if scene == null: + return false + var scene_path: String = scene.get_scene_file_path() + if scene_path.is_empty(): + return false + return MENU_SCENES.has(scene_path) + func set_user_volume(value: float) -> void: var clamped_value: float = clamp(value, 0.0, 1.0) if is_equal_approx(_user_volume_linear, clamped_value): diff --git a/scenes/UI/menu_music.tscn b/scenes/UI/menu_music.tscn index 1b4ed67..ab2ec7b 100644 --- a/scenes/UI/menu_music.tscn +++ b/scenes/UI/menu_music.tscn @@ -5,7 +5,9 @@ [ext_resource type="Script" path="res://scenes/UI/menu_music.gd" id="2_21d4q"] [node name="MenuMusic" type="AudioStreamPlayer"] +bus = &"Music" stream = ExtResource("1_ek0t3") autoplay = true volume_db = -3.0 +priority = 10.0 script = ExtResource("2_21d4q") diff --git a/scenes/UI/menu_sfx.gd b/scenes/UI/menu_sfx.gd new file mode 100644 index 0000000..ab57e14 --- /dev/null +++ b/scenes/UI/menu_sfx.gd @@ -0,0 +1,76 @@ +extends AudioStreamPlayer + +const CONFIG_PATH := "user://settings.cfg" +const CONFIG_SECTION := "audio" +const CONFIG_KEY_VOLUME := "menu_sfx_volume" +const CONFIG_KEY_MUTED := "menu_sfx_muted" +const DEFAULT_VOLUME := 0.7 + +var _user_volume_linear: float = DEFAULT_VOLUME +var _is_muted: bool = false + +func _ready() -> void: + _load_settings() + _apply_volume() + +func play_hover() -> void: + if stream == null or _is_muted or _user_volume_linear <= 0.0: + return + play(0.0) + +func set_user_volume(value: float) -> void: + var clamped_value: float = clamp(value, 0.0, 1.0) + if is_equal_approx(_user_volume_linear, clamped_value): + return + _user_volume_linear = clamped_value + _apply_volume() + _save_settings() + +func get_user_volume() -> float: + return _user_volume_linear + +func set_user_muted(muted: bool) -> void: + if _is_muted == muted: + return + _is_muted = muted + _apply_volume() + _save_settings() + if _is_muted: + stop() + +func is_user_muted() -> bool: + return _is_muted + +func _apply_volume() -> void: + if _is_muted or _user_volume_linear <= 0.0: + volume_db = -80.0 + else: + volume_db = linear_to_db(_user_volume_linear) + +func _load_settings() -> void: + var config: ConfigFile = ConfigFile.new() + var err: int = config.load(CONFIG_PATH) + if err == OK: + _user_volume_linear = clamp(float(config.get_value(CONFIG_SECTION, CONFIG_KEY_VOLUME, DEFAULT_VOLUME)), 0.0, 1.0) + _is_muted = bool(config.get_value(CONFIG_SECTION, CONFIG_KEY_MUTED, false)) + elif err == ERR_DOES_NOT_EXIST: + _user_volume_linear = DEFAULT_VOLUME + _is_muted = false + else: + push_warning("Failed to load settings.cfg: %s" % err) + _user_volume_linear = DEFAULT_VOLUME + _is_muted = false + +func _save_settings() -> void: + var config: ConfigFile = ConfigFile.new() + var err: int = config.load(CONFIG_PATH) + if err != OK and err != ERR_DOES_NOT_EXIST: + push_warning("Failed to load settings.cfg before saving: %s" % err) + config = ConfigFile.new() + elif err != OK: + config = ConfigFile.new() + config.set_value(CONFIG_SECTION, CONFIG_KEY_VOLUME, _user_volume_linear) + config.set_value(CONFIG_SECTION, CONFIG_KEY_MUTED, _is_muted) + var save_err: int = config.save(CONFIG_PATH) + if save_err != OK: + push_warning("Failed to save settings.cfg: %s" % save_err) diff --git a/scenes/UI/menu_sfx.gd.uid b/scenes/UI/menu_sfx.gd.uid new file mode 100644 index 0000000..d0a31e3 --- /dev/null +++ b/scenes/UI/menu_sfx.gd.uid @@ -0,0 +1 @@ +uid://c7ixr4hbh5ad6 diff --git a/scenes/UI/menu_sfx.tscn b/scenes/UI/menu_sfx.tscn new file mode 100644 index 0000000..2c07257 --- /dev/null +++ b/scenes/UI/menu_sfx.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=3 format=3 uid="uid://dt785sv7ie7uj"] + +[ext_resource type="AudioStream" uid="uid://64dplcgx2icb" path="res://assets/audio/silly-menu-hover-test.ogg" id="1_a5j5k"] +[ext_resource type="Script" path="res://scenes/UI/menu_sfx.gd" id="1_ijvfa"] + +[node name="MenuSfx" type="AudioStreamPlayer"] +bus = &"SFX" +stream = ExtResource("1_a5j5k") +volume_db = -6.0 +autoplay = false +priority = 0.5 +max_polyphony = 4 +script = ExtResource("1_ijvfa") diff --git a/scenes/UI/start_screen.gd b/scenes/UI/start_screen.gd index 6470fce..dbc509f 100644 --- a/scenes/UI/start_screen.gd +++ b/scenes/UI/start_screen.gd @@ -1,5 +1,17 @@ extends Control +func _ready(): + _register_focus_sounds() + +func _register_focus_sounds() -> void: + var button_container := $MarginContainer/MarginContainer/VBoxContainer + for child in button_container.get_children(): + if child is BaseButton: + var button: BaseButton = child + if not button.is_connected("focus_entered", Callable(self, "_on_menu_item_focus")): + button.focus_entered.connect(_on_menu_item_focus) + if not button.is_connected("mouse_entered", Callable(self, "_on_menu_item_focus")): + button.mouse_entered.connect(_on_menu_item_focus) func _on_start_button_pressed(): get_tree().change_scene_to_file("uid://dchj6g2i8ebph") @@ -9,3 +21,7 @@ func _on_settings_button_pressed(): func _on_quit_button_pressed(): get_tree().quit() + +func _on_menu_item_focus() -> void: + if MenuSfx: + MenuSfx.play_hover()