Added framework for in-game sounds & listeners

This commit is contained in:
Rob Kelly 2025-04-04 14:40:42 -06:00
parent 333c9c20bb
commit eb57362199
13 changed files with 153 additions and 13 deletions

View File

@ -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]

View File

@ -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()

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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:

View File

@ -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")

View File

@ -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"]

View File

@ -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)

View File

@ -0,0 +1 @@
uid://c5o1d2shq2qig

View File

@ -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)

View File

@ -0,0 +1 @@
uid://cfsiyhhrcua6o