Skip to content

Commit b807b20

Browse files
Initial release
0 parents  commit b807b20

23 files changed

+493
-0
lines changed

.gitattributes

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Normalize line endings for all files that Git considers text files.
2+
* text=auto eol=lf
3+
4+
# Only include the addons folder when downloading from the Asset Library.
5+
/** export-ignore
6+
/addons !export-ignore
7+
/addons/** !export-ignore

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Godot-specific ignores
2+
.godot/
3+
/android/
4+
.nomedia
5+
.import/
6+
export.cfg
7+
export_credentials.cfg
8+
9+
# Mono-specific ignores
10+
.mono/
11+
data_*/
12+
mono_crash.*.json

LICENSE.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2025 ProgrammerOnCoffee
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
![Button Feedback Icon](https://raw.githubusercontent.com/ProgrammerOnCoffee/Button-Feedback/refs/heads/main/icon.png)
2+
3+
4+
# Button Feedback
5+
6+
Button Feedback is a plugin for Godot Engine `v4.0+` that provides audiovisual
7+
feedback to players when they interact with buttons.
8+
9+
10+
## Features
11+
12+
- Adds sounds to buttons when they're hovered over and clicked
13+
- Scales buttons when they're hovered over
14+
- Removes buttons' focus stylebox when the focus is from a tap or click
15+
- Includes three original button sounds (hover, mouse down, and mouse up)
16+
17+
18+
## Installation
19+
20+
1. Clone or download the repository.
21+
2. Copy the `addons/button_feedback` folder into your project.
22+
3. Navigate to `Project > Project Settings... > Plugins` and enable Button Feedback.
23+
24+
25+
## Usage
26+
27+
Button Feedback will automatically set up feedback for all buttons in the scene
28+
tree when the project runs. If you create buttons or instantiate scenes during
29+
runtime, call `ButtonFeedback.setup_button` or `ButtonFeedback.setup_recursive`.
30+
31+
If desired, swap out the button sounds and edit the constants in `button_feedback.gd`.
32+
33+
34+
## License
35+
36+
This plugin is licensed under the
37+
[MIT/Expat license](https://github.com/ProgrammerOnCoffee/Button-Feedback/blob/main/LICENSE.txt).

addons/button_feedback/LICENSE.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2025 ProgrammerOnCoffee
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

addons/button_feedback/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Button Feedback
2+
3+
For information, see https://github.com/ProgrammerOnCoffee/Button-Feedback.
4+
5+
6+
## License
7+
8+
This asset is licensed under the MIT/Expat license.
9+
See the LICENSE.txt file for more details.
10.7 KB
Binary file not shown.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[remap]
2+
3+
importer="wav"
4+
type="AudioStreamWAV"
5+
uid="uid://pa182gmojod7"
6+
path="res://.godot/imported/button_down.wav-e40a2aacc743f0e5a099667b66036a76.sample"
7+
8+
[deps]
9+
10+
source_file="res://addons/button_feedback/button_down.wav"
11+
dest_files=["res://.godot/imported/button_down.wav-e40a2aacc743f0e5a099667b66036a76.sample"]
12+
13+
[params]
14+
15+
force/8_bit=false
16+
force/mono=false
17+
force/max_rate=false
18+
force/max_rate_hz=44100
19+
edit/trim=false
20+
edit/normalize=false
21+
edit/loop_mode=0
22+
edit/loop_begin=0
23+
edit/loop_end=-1
24+
compress/mode=2
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
####################################################################################
2+
## This file is part of Button Feedback. ##
3+
## https://github.com/ProgrammerOnCoffee/Button-Feedback ##
4+
####################################################################################
5+
## Copyright (c) 2025 ProgrammerOnCoffee. ##
6+
## ##
7+
## Permission is hereby granted, free of charge, to any person obtaining a copy ##
8+
## of this software and associated documentation files (the "Software"), to deal ##
9+
## in the Software without restriction, including without limitation the rights ##
10+
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ##
11+
## copies of the Software, and to permit persons to whom the Software is ##
12+
## furnished to do so, subject to the following conditions: ##
13+
## ##
14+
## The above copyright notice and this permission notice shall be included in all ##
15+
## copies or substantial portions of the Software. ##
16+
## ##
17+
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ##
18+
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ##
19+
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ##
20+
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ##
21+
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ##
22+
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ##
23+
## SOFTWARE. ##
24+
####################################################################################
25+
26+
@icon("res://addons/button_feedback/icon.svg")
27+
extends Node
28+
## Provides audiovisual feedback when players interact with buttons.
29+
##
30+
## Provides audiovisual feedback when players interact with buttons.
31+
32+
## The volume of button sounds in decibels.
33+
## Note that the hover sound will have [code]6.0[/code] dB subtracted from this number.
34+
const VOLUME_DB: float = -6.0
35+
## The target audio bus of button sounds.
36+
const AUDIO_BUS: StringName = &""
37+
## The pitch scale of button sounds.
38+
const PITCH_SCALE: float = 2.0
39+
## The amount that buttons will be scaled by when hovered.
40+
const HOVER_SCALE: float = 1.1
41+
## The duration of the tween that scales buttons when they are hovered.
42+
const HOVER_SCALE_DURATION: float = 0.05
43+
44+
# [AudioStreamPlayer2D]s are used because of a bug where pitch_scale
45+
# doesn't work on web exports for regular [AudioStreamPlayer]s.
46+
## The [AudioStreamPlayer2D] that plays the button hover sound.
47+
var button_hover_player := AudioStreamPlayer2D.new()
48+
## The [AudioStreamPlayer2D] that plays the button down sound.
49+
var button_down_player := AudioStreamPlayer2D.new()
50+
## The [AudioStreamPlayer2D] that plays the button pressed sound.
51+
var button_pressed_player := AudioStreamPlayer2D.new()
52+
53+
## The [StyleBoxEmpty] that will be set as the focus theme override for clicked
54+
## buttons.[br]
55+
## The default stylebox will be shown if the focus is not from a click
56+
## (e.g. when navigating with a keyboard.)
57+
var _stylebox_empty := StyleBoxEmpty.new()
58+
59+
60+
func _init() -> void:
61+
button_hover_player.stream = load("res://addons/button_feedback/button_hover.wav") as AudioStreamWAV
62+
button_hover_player.volume_db = VOLUME_DB - 6.0
63+
button_down_player.stream = load("res://addons/button_feedback/button_down.wav") as AudioStreamWAV
64+
button_down_player.volume_db = VOLUME_DB
65+
button_pressed_player.stream = load("res://addons/button_feedback/button_pressed.wav") as AudioStreamWAV
66+
button_pressed_player.volume_db = VOLUME_DB
67+
68+
69+
func _ready() -> void:
70+
for player in [button_hover_player, button_down_player, button_pressed_player] as Array[AudioStreamPlayer2D]:
71+
player.bus = AUDIO_BUS
72+
player.pitch_scale = PITCH_SCALE
73+
player.attenuation = 0.0
74+
player.max_distance = INF
75+
player.panning_strength = 0.0
76+
77+
setup_recursive(get_parent())
78+
79+
add_child(button_hover_player)
80+
add_child(button_down_player)
81+
add_child(button_pressed_player)
82+
83+
84+
## Calls [member setup_button] for all [BaseButton]s
85+
## that [param node] is an ancestor of.
86+
func setup_recursive(ancestor: Node) -> void:
87+
var queue := ancestor.get_children()
88+
while queue:
89+
var new_queue: Array[Node] = []
90+
for node in queue:
91+
var button := node as BaseButton
92+
if button:
93+
setup_button(button)
94+
new_queue.append_array(node.get_children())
95+
queue = new_queue
96+
97+
98+
## Sets up audiovisual feedback for [param button].
99+
func setup_button(button: BaseButton) -> void:
100+
# Return if feedback for button has already been set up
101+
if button.button_down.is_connected(button_down_player.play):
102+
return
103+
104+
button.focus_entered.connect(_on_button_focus_entered.bind(button), CONNECT_DEFERRED)
105+
button.focus_exited.connect(button.remove_theme_stylebox_override.bind(&"focus"))
106+
button.mouse_entered.connect(_on_button_mouse_entered.bind(button))
107+
button.mouse_exited.connect(_on_button_mouse_exited.bind(button))
108+
button.mouse_entered.connect(button_hover_player.play)
109+
button.button_down.connect(button_down_player.play)
110+
# is_class() won't error if the advanced gui module is disabled
111+
if not button.is_class("OptionButton"):
112+
button.pressed.connect(button_pressed_player.play)
113+
114+
115+
func _on_button_focus_entered(button: BaseButton) -> void:
116+
# Remove focus stylebox if focus is from a click
117+
if button.button_pressed:
118+
button.add_theme_stylebox_override(&"focus", _stylebox_empty)
119+
120+
121+
func _on_button_mouse_entered(button: BaseButton) -> void:
122+
# Set pivot offset so that button scales from center
123+
button.pivot_offset = button.size / 2
124+
# Kill tween if one is already running
125+
if button.has_meta(&"_button_feedback_scale_tween"):
126+
button.get_meta(&"_button_feedback_scale_tween").kill()
127+
var tween := button.create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC)
128+
tween.tween_property(button, ^":scale", Vector2.ONE * HOVER_SCALE,
129+
# Tween for less time if button is already partially scaled
130+
remap(button.scale.x, 1.0, HOVER_SCALE, HOVER_SCALE_DURATION, 0.0))
131+
button.set_meta(&"_button_feedback_scale_tween", tween)
132+
133+
134+
func _on_button_mouse_exited(button: BaseButton) -> void:
135+
# Ensure mouse is outside of button, not blocked by another Control
136+
if not Rect2i(Vector2.ZERO, button.size).has_point(button.get_local_mouse_position()):
137+
# Kill tween if one is already running
138+
if button.has_meta(&"_button_feedback_scale_tween"):
139+
button.get_meta(&"_button_feedback_scale_tween").kill()
140+
var tween := button.create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC)
141+
tween.tween_property(button, ^":scale", Vector2.ONE,
142+
# Tween for less time if button isn't fully scaled
143+
remap(button.scale.x, HOVER_SCALE, 1.0, HOVER_SCALE_DURATION, 0.0))
144+
button.set_meta(&"_button_feedback_scale_tween", tween)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://b6mqwnmlg422n

0 commit comments

Comments
 (0)