From eb573621993adcae66fca12a32fabf621b87cf13 Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Fri, 4 Apr 2025 14:40:42 -0600 Subject: [PATCH] Added framework for in-game sounds & listeners --- project.godot | 1 + src/equipment/beam_sfx/spray_sfx.gd | 3 ++ src/equipment/beam_sfx/spray_sfx.tscn | 17 +++++++++- src/player/footsteps/footstep_controller.gd | 5 +++ src/player/player.gd | 28 ++++++++++----- src/player/player.tscn | 16 ++++++++- src/props/bulkhead/bulkhead.gd | 2 ++ src/props/bulkhead/bulkhead.tscn | 17 +++++++++- src/props/wall_switch/wall_switch.tscn | 16 ++++++++- src/world/game_sound/game_sound_emitter.gd | 34 +++++++++++++++++++ .../game_sound/game_sound_emitter.gd.uid | 1 + src/world/game_sound/game_sound_listener.gd | 25 ++++++++++++++ .../game_sound/game_sound_listener.gd.uid | 1 + 13 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 src/world/game_sound/game_sound_emitter.gd create mode 100644 src/world/game_sound/game_sound_emitter.gd.uid create mode 100644 src/world/game_sound/game_sound_listener.gd create mode 100644 src/world/game_sound/game_sound_listener.gd.uid diff --git a/project.godot b/project.godot index 554d7bc..dc250f5 100644 --- a/project.godot +++ b/project.godot @@ -183,6 +183,7 @@ locale/translations=PackedStringArray("res://assets/text/text.en.translation") 3d_physics/layer_2="Interactive" 3d_physics/layer_3="Sprayable" 3d_physics/layer_4="Player" +3d_physics/layer_5="GameSounds" [rendering] diff --git a/src/equipment/beam_sfx/spray_sfx.gd b/src/equipment/beam_sfx/spray_sfx.gd index 253c36f..0740901 100644 --- a/src/equipment/beam_sfx/spray_sfx.gd +++ b/src/equipment/beam_sfx/spray_sfx.gd @@ -6,6 +6,8 @@ class_name SpraySFX extends AudioStreamPlayer3D var _active_this_frame := false +@onready var spray_game_sound_emitter: GameSoundEmitter = %SprayGameSoundEmitter + func activate() -> void: _active_this_frame = true @@ -23,6 +25,7 @@ func _process(_delta: float) -> void: if _active_this_frame: if not playing: play() + spray_game_sound_emitter.emit_sound_here() else: stop() diff --git a/src/equipment/beam_sfx/spray_sfx.tscn b/src/equipment/beam_sfx/spray_sfx.tscn index 14bac01..096f096 100644 --- a/src/equipment/beam_sfx/spray_sfx.tscn +++ b/src/equipment/beam_sfx/spray_sfx.tscn @@ -1,11 +1,26 @@ -[gd_scene load_steps=3 format=3 uid="uid://btq11kil0jcql"] +[gd_scene load_steps=5 format=3 uid="uid://btq11kil0jcql"] [ext_resource type="AudioStream" uid="uid://b5ik76jgl8mex" path="res://assets/sfx/tools/spray.wav" id="1_575yt"] [ext_resource type="Script" uid="uid://bhvkgqpm7sglw" path="res://src/equipment/beam_sfx/spray_sfx.gd" id="2_yajv7"] +[ext_resource type="Script" uid="uid://c5o1d2shq2qig" path="res://src/world/game_sound/game_sound_emitter.gd" id="3_rikqc"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_yaakk"] +radius = 2.0 [node name="SpraySFX" type="AudioStreamPlayer3D"] stream = ExtResource("1_575yt") attenuation_model = 1 +volume_db = -12.0 unit_size = 2.0 bus = &"SFX" script = ExtResource("2_yajv7") + +[node name="SprayGameSoundEmitter" type="Area3D" parent="."] +unique_name_in_owner = true +collision_layer = 0 +collision_mask = 16 +script = ExtResource("3_rikqc") +metadata/_custom_type_script = "uid://c5o1d2shq2qig" + +[node name="CollisionShape3D" type="CollisionShape3D" parent="SprayGameSoundEmitter"] +shape = SubResource("SphereShape3D_yaakk") diff --git a/src/player/footsteps/footstep_controller.gd b/src/player/footsteps/footstep_controller.gd index 1bc47d3..736f52c 100644 --- a/src/player/footsteps/footstep_controller.gd +++ b/src/player/footsteps/footstep_controller.gd @@ -12,9 +12,13 @@ var _on_right_foot := false @onready var right_foot: FootController = %RightFoot @onready var foot_cast: RayCast3D = %FootCast +@onready var footstep_game_sound_emitter: GameSoundEmitter = %FootstepGameSoundEmitter func play_footstep() -> void: + if player.sneaking: + return + var foot := right_foot if _on_right_foot else left_foot var relative_speed := player.velocity.length() - MUTE_VELOCITY @@ -25,6 +29,7 @@ func play_footstep() -> void: if sfx: sfx.volume_db = BASE_VOLUME + relative_speed * VELOCITY_FACTOR sfx.play() + footstep_game_sound_emitter.emit_sound_here() _on_right_foot = not _on_right_foot diff --git a/src/player/player.gd b/src/player/player.gd index 4d5cff3..ff51900 100644 --- a/src/player/player.gd +++ b/src/player/player.gd @@ -31,7 +31,9 @@ var gravity: Vector3 = ( var selected_interactive: Interactive var firing := false -var crouching := false +var sneaking := false + +var _was_on_floor := false @onready var hud: PlayerHUD = %PlayerHUD @@ -47,6 +49,8 @@ var crouching := false @onready var crouch_head_area: Area3D = %CrouchHeadArea @onready var crouch_animation: AnimationPlayer = %CrouchAnimation +@onready var jump_game_sound_emitter: GameSoundEmitter = %JumpGameSoundEmitter + #endregion ## Global static access to player singleton @@ -68,7 +72,7 @@ func get_speed() -> float: var speed := run_speed if Input.is_action_pressed("sprint"): speed = sprint_speed - if crouching: + if sneaking: speed = sneak_speed if firing: speed = focus_speed @@ -100,23 +104,23 @@ func remove_item(item: Item, amount: int = 1) -> void: func crouch() -> void: - if not crouching and not crouch_animation.is_playing(): + if not sneaking and not crouch_animation.is_playing(): crouch_animation.play("crouch") - crouching = true + sneaking = true func uncrouch() -> void: if ( - crouching + sneaking and not crouch_animation.is_playing() and not crouch_head_area.has_overlapping_bodies() ): crouch_animation.play_backwards("crouch") - crouching = false + sneaking = false func toggle_crouch() -> void: - if crouching: + if sneaking: uncrouch() else: crouch() @@ -171,8 +175,12 @@ func _physics_process(delta: float) -> void: if Input.is_action_just_pressed("sneak"): toggle_crouch() - # Gravity - if not is_on_floor(): + if is_on_floor(): + if not _was_on_floor and not sneaking: + # just landed + jump_game_sound_emitter.emit_sound_here() + else: + # Gravity velocity += gravity * delta # Input movement @@ -190,6 +198,8 @@ func _physics_process(delta: float) -> void: velocity.x = lerpf(velocity.x, 0, friction) velocity.z = lerpf(velocity.z, 0, friction) + _was_on_floor = is_on_floor() + move_and_slide() #endregion diff --git a/src/player/player.tscn b/src/player/player.tscn index 40f1521..639012e 100644 --- a/src/player/player.tscn +++ b/src/player/player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=56 format=3 uid="uid://bwe2jdmvinhqd"] +[gd_scene load_steps=57 format=3 uid="uid://bwe2jdmvinhqd"] [ext_resource type="Script" uid="uid://buwh0g1ga2aka" path="res://src/player/player.gd" id="1_npueo"] [ext_resource type="Script" uid="uid://cx1yt0drthpw3" path="res://src/player/camera_controller.gd" id="2_veeqv"] @@ -427,6 +427,9 @@ _data = { &"crouch": SubResource("Animation_dpt0q") } +[sub_resource type="SphereShape3D" id="SphereShape3D_p6grl"] +radius = 3.0 + [node name="Player" type="CharacterBody3D"] collision_layer = 8 script = ExtResource("1_npueo") @@ -576,3 +579,14 @@ unique_name_in_owner = true libraries = { &"": SubResource("AnimationLibrary_wcxbk") } + +[node name="JumpGameSoundEmitter" type="Area3D" parent="."] +unique_name_in_owner = true +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0) +collision_layer = 0 +collision_mask = 16 +script = ExtResource("31_wcxbk") +metadata/_custom_type_script = "uid://c5o1d2shq2qig" + +[node name="CollisionShape3D" type="CollisionShape3D" parent="JumpGameSoundEmitter"] +shape = SubResource("SphereShape3D_p6grl") diff --git a/src/props/bulkhead/bulkhead.gd b/src/props/bulkhead/bulkhead.gd index 262bb97..c86ed51 100644 --- a/src/props/bulkhead/bulkhead.gd +++ b/src/props/bulkhead/bulkhead.gd @@ -3,12 +3,14 @@ extends Node3D @onready var animation: AnimationPlayer = $AnimationPlayer @onready var dust_animation: AnimationPlayer = %DustAnimation @onready var open_sfx: AudioStreamPlayer3D = %OpenSFX +@onready var bulkhead_game_sound_emitter: GameSoundEmitter = %BulkheadGameSoundEmitter func open() -> void: animation.play("open") dust_animation.play("spray") open_sfx.play() + bulkhead_game_sound_emitter.emit_sound_here() func close() -> void: diff --git a/src/props/bulkhead/bulkhead.tscn b/src/props/bulkhead/bulkhead.tscn index 402adff..ad89263 100644 --- a/src/props/bulkhead/bulkhead.tscn +++ b/src/props/bulkhead/bulkhead.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=19 format=4 uid="uid://cubwniraol1qn"] +[gd_scene load_steps=21 format=4 uid="uid://cubwniraol1qn"] [ext_resource type="PackedScene" uid="uid://bopvgd18a1dl0" path="res://assets/props/bulkhead/bulkhead.gltf" id="1_77udb"] [ext_resource type="Material" uid="uid://dim1g2sr3axr5" path="res://assets/props/bulkhead/bulkhead_frame.material" id="2_88qrs"] @@ -7,6 +7,7 @@ [ext_resource type="Material" uid="uid://ba5iycnw36138" path="res://assets/props/bulkhead/bulkhead_door.material" id="3_w7oba"] [ext_resource type="AudioStream" uid="uid://6mm0npep4ul1" path="res://assets/sfx/mechanical_door.wav" id="5_4jho1"] [ext_resource type="AudioStream" uid="uid://dkdw7viq1nqte" path="res://assets/sfx/air_hiss.wav" id="7_4jho1"] +[ext_resource type="Script" uid="uid://c5o1d2shq2qig" path="res://src/world/game_sound/game_sound_emitter.gd" id="8_bso71"] [sub_resource type="ArrayMesh" id="ArrayMesh_hx0vd"] _surfaces = [{ @@ -313,6 +314,9 @@ _data = { &"spray": SubResource("Animation_88qrs") } +[sub_resource type="SphereShape3D" id="SphereShape3D_jo25b"] +radius = 4.0 + [node name="Bulkhead" instance=ExtResource("1_77udb")] script = ExtResource("2_hknvo") @@ -386,3 +390,14 @@ libraries = { [node name="DustSFX" type="AudioStreamPlayer3D" parent="Dust" index="5"] stream = ExtResource("7_4jho1") volume_db = -24.0 + +[node name="BulkheadGameSoundEmitter" type="Area3D" parent="." index="6"] +unique_name_in_owner = true +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0) +collision_layer = 0 +collision_mask = 16 +script = ExtResource("8_bso71") +metadata/_custom_type_script = "uid://c5o1d2shq2qig" + +[node name="CollisionShape3D" type="CollisionShape3D" parent="BulkheadGameSoundEmitter" index="0"] +shape = SubResource("SphereShape3D_jo25b") diff --git a/src/props/wall_switch/wall_switch.tscn b/src/props/wall_switch/wall_switch.tscn index 60235a9..bbcbd56 100644 --- a/src/props/wall_switch/wall_switch.tscn +++ b/src/props/wall_switch/wall_switch.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=30 format=4 uid="uid://b6eg8t04rkh0c"] +[gd_scene load_steps=32 format=4 uid="uid://b6eg8t04rkh0c"] [ext_resource type="Script" uid="uid://bkmn5m47mt1gh" path="res://src/props/wall_switch/wall_switch.gd" id="2_kfvqd"] [ext_resource type="Texture2D" uid="uid://1wjcyqynwlb6" path="res://assets/props/wall_switch/wall_switch_C.png" id="2_vufqs"] @@ -12,6 +12,7 @@ [ext_resource type="Script" uid="uid://deg5xd87cy8rg" path="res://src/props/interactive.gd" id="10_qw6jt"] [ext_resource type="AudioStream" uid="uid://bgayfws34lg7q" path="res://assets/sfx/click_electronic_04.wav" id="11_7shuc"] [ext_resource type="AudioStream" uid="uid://cnje66wrijwxw" path="res://assets/sfx/wall_switch.wav" id="12_2qpft"] +[ext_resource type="Script" uid="uid://c5o1d2shq2qig" path="res://src/world/game_sound/game_sound_emitter.gd" id="13_eim2y"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_jtkkw"] resource_local_to_scene = true @@ -248,6 +249,9 @@ _data = { [sub_resource type="BoxShape3D" id="BoxShape3D_6maql"] size = Vector3(0.475, 0.65, 0.2) +[sub_resource type="SphereShape3D" id="SphereShape3D_mxsyy"] +radius = 3.0 + [node name="WallSwitch" instance=ExtResource("2_whafo")] script = ExtResource("2_kfvqd") enabled = true @@ -313,7 +317,17 @@ unique_name_in_owner = true wait_time = 2.8 one_shot = true +[node name="WallSwitchGameSoundEmitter" type="Area3D" parent="." index="7"] +collision_layer = 0 +collision_mask = 16 +script = ExtResource("13_eim2y") +metadata/_custom_type_script = "uid://c5o1d2shq2qig" + +[node name="CollisionShape3D" type="CollisionShape3D" parent="WallSwitchGameSoundEmitter" index="0"] +shape = SubResource("SphereShape3D_mxsyy") + [connection signal="animation_finished" from="AnimationPlayer" to="." method="_animation_finished"] [connection signal="clear_total_updated" from="GunkBody" to="." method="_on_gunk_body_clear_total_updated"] [connection signal="activated" from="Interactive" to="." method="_activate"] [connection signal="timeout" from="ActionDelay" to="." method="_on_action_delay_timeout"] +[connection signal="timeout" from="ActionDelay" to="WallSwitchGameSoundEmitter" method="emit_sound_here"] diff --git a/src/world/game_sound/game_sound_emitter.gd b/src/world/game_sound/game_sound_emitter.gd new file mode 100644 index 0000000..e346156 --- /dev/null +++ b/src/world/game_sound/game_sound_emitter.gd @@ -0,0 +1,34 @@ +@tool +class_name GameSoundEmitter extends Area3D +## Sends an alert to any intersecting GameSoundListener on command + + +func _set_collision() -> void: + collision_layer = 0 + collision_mask = GameSoundListener.SOUND_COLLISION_LAYER + + +func _ready() -> void: + _set_collision() + + +func _process(_delta: float) -> void: + if Engine.is_editor_hint(): + _set_collision() + + +## Trigger a sound detection event on all GameSoundListeners in range. +## +## The source of the sound will be the global position of this emitter. +func emit_sound_here() -> void: + emit_sound(global_position) + + +## Trigger a sound detection event on all GameSoundListeners in range. +## +## `source` is the global position of the source of the sound. +func emit_sound(source: Vector3) -> void: + print_debug(self, " emitted game sound at ", source) + for body: Node3D in self.get_overlapping_bodies(): + if body is GameSoundListener: + (body as GameSoundListener).detect_sound(source) diff --git a/src/world/game_sound/game_sound_emitter.gd.uid b/src/world/game_sound/game_sound_emitter.gd.uid new file mode 100644 index 0000000..42b826d --- /dev/null +++ b/src/world/game_sound/game_sound_emitter.gd.uid @@ -0,0 +1 @@ +uid://c5o1d2shq2qig diff --git a/src/world/game_sound/game_sound_listener.gd b/src/world/game_sound/game_sound_listener.gd new file mode 100644 index 0000000..cd6b2b3 --- /dev/null +++ b/src/world/game_sound/game_sound_listener.gd @@ -0,0 +1,25 @@ +@tool +class_name GameSoundListener extends Area3D +## Receives alerts from intersecting GameSoundEmitters + +signal sound_detected(source: Vector3) + +const SOUND_COLLISION_LAYER := 16 + + +func _set_collision() -> void: + collision_layer = SOUND_COLLISION_LAYER + collision_mask = 0 + + +func _ready() -> void: + _set_collision() + + +func _process(_delta: float) -> void: + if Engine.is_editor_hint(): + _set_collision() + + +func detect_sound(source: Vector3) -> void: + sound_detected.emit(source) diff --git a/src/world/game_sound/game_sound_listener.gd.uid b/src/world/game_sound/game_sound_listener.gd.uid new file mode 100644 index 0000000..8d084b5 --- /dev/null +++ b/src/world/game_sound/game_sound_listener.gd.uid @@ -0,0 +1 @@ +uid://cfsiyhhrcua6o