I thought it would be neat to allow players to create music simply by hovering over menu items, so I created several note samples from a PANArt Hang. When the user moves up in the menu, it attempts to randomly play a note that’s higher than the most recently played note. Moving down in the menu attempts to play a lower note. Notes wrap around if they are past the end.
This video is also available on dailymotion.
I still need to add some ambient background music to go with this, but it’s still pretty fun to play with. I may even go so far as to have different keys in the background music and only play menu hover notes that fit with that particular key. We’ll see! I have to be careful not to get too caught up in the little details and actually finish this game!
For those wondering how to hook up menu sounds in godot, I created a function to recursively go through all of the nodes in my menu and add callbacks on the buttons:
func AddMenuSoundCallbacks(MenuInstance : Node, ItemBindNumber : int) -> int:
if (MenuInstance is Button):
var ButtonInstance : Button = MenuInstance
ButtonInstance.connect("mouse_entered", self, "_on_MouseEnterButton", [ ButtonInstance ] )
ButtonInstance.connect("focus_entered", self, "_on_MenuItemHover", [ ItemBindNumber ] )
ButtonInstance.connect("button_down", self, "_on_MenuButtonPress")
ItemBindNumber += 1
var MenuChildren := MenuInstance.get_children()
if (MenuChildren.size() > 0):
for MenuChild in MenuChildren:
ItemBindNumber = AddMenuSoundCallbacks(MenuChild, ItemBindNumber)
return ItemBindNumber
The “bind number” is simply there to keep track of menus relative to each other for the different sound pitches. If you’re just doing one sound for hovering, this isn’t necessary.
The mouse enter event simply makes the mouse hover behave the same as moving through the menu with a controller or keyboard. I did this to prevent double sounds from playing when you hover over something and then click on it. This also makes the visual behavior more consistent, since there isn’t a separate concept for a “selected” button vs. a button the mouse is hovering over.
func _on_MouseEnterButton(ButtonInstance : Button):
ButtonInstance.grab_focus()
Here’s where the magic happens:
func _on_MenuItemHover(ButtonIndex : int):
if (MenuStack.size() < LastMenuStackSize):
LastMenuStackSize = MenuStack.size()
return # Don't play a hover sound when backing out of a menu
LastMenuStackSize = MenuStack.size()
if (ButtonIndex < LastMenuButtonIndex):
LastMenuRandomSoundIndex -= int(rand_range(1, 3))
else:
LastMenuRandomSoundIndex += int(rand_range(1, 3))
LastMenuButtonIndex = ButtonIndex
LastMenuRandomSoundIndex %= HoverSounds.size()
PlayMenuSound(HoverSounds[LastMenuRandomSoundIndex])
I have a special function for playing menu sounds because I have multiple audio stream players that I cycle between so the sounds don’t get cut off abruptly:
func PlayMenuSound(stream : AudioStream):
CurrentMenuSoundIndex = (CurrentMenuSoundIndex + 1) % NUM_MENU_SOUND_PLAYERS
var MenuSoundPlayer : AudioStreamPlayer = MenuSoundPlayers[CurrentMenuSoundIndex]
MenuSoundPlayer.stream = stream
MenuSoundPlayer.play()
The hover sounds are just an array of preloaded resources that I set in the _ready() function:
HoverSounds.append(preload("res://Sound/UI/menu_item_hover_1_1.wav"))
HoverSounds.append(preload("res://Sound/UI/menu_item_hover_1_2.wav"))
HoverSounds.append(preload("res://Sound/UI/menu_item_hover_1_3.wav"))
HoverSounds.append(preload("res://Sound/UI/menu_item_hover_1_4.wav"))
HoverSounds.append(preload("res://Sound/UI/menu_item_hover_1_5.wav"))
HoverSounds.append(preload("res://Sound/UI/menu_item_hover_1_6.wav"))
HoverSounds.append(preload("res://Sound/UI/menu_item_hover_1_7.wav"))
HoverSounds.append(preload("res://Sound/UI/menu_item_hover_1_8.wav"))
This is no a complete, comprehensive implementation of menu sounds that I’m sharing here, but hopefully it’s enough to get you started if you want to implement something similar!