Implement SFX for void enemy - #2862
Conversation
|
Hi, I've tried adding sound effects for the “void” enemy, but I can't really say if they go well with the background music, since I'm not very familiar with these sound-related issues. I look forward to your suggestions so I can improve what I've done. |
|
Play this branch at https://play.threadbare.game/branches/IDynamixI985/void_sfx/. (This launches the game from the start, not directly at the change(s) in this pull request.) |
wjt
left a comment
There was a problem hiding this comment.
This is a great start!
As well as the comments below, this is missing licensing annotations. See https://github.com/endlessm/threadbare/wiki/Licensing#license-annotations for a quick summary. You'll need to add a .license file alongside each sound effect. For example, assuming I understand correctly that the alert sound is an edited version of https://freesound.org/people/BadWolf23/sounds/726209/ you should create assets/third_party/sounds/void_enemy/void_sfx_alert.wav.license with text like this:
SPDX-FileCopyrightText: BadWolf23
SPDX-License-Identifier: CC-BY-4.0
Void Wind Effect by BadWolf23 -- https://freesound.org/s/726209/ -- License: Attribution 4.0
Then we'll need to update the game's credits for the files for which attribution is required.
I reviewed the samples you listed in #1975 and unfortunately we can't use https://freesound.org/people/dkristian/sounds/31478/. It is under the Sampling+ license, which says:
To perform, display, and distribute copies of this whole work for noncommercial purposes (e.g., file-sharing or noncommercial webcasting).
Although Threadbare is not currently sold, we would like to keep open the option of making a paid version of Threadbare available, so we can't use assets under "non-commercial" licenses.
There was a problem hiding this comment.
These .m4a files are not understood by Godot and hence not used, please remove.
| @@ -0,0 +1,3 @@ | |||
| version https://git-lfs.github.com/spec/v1 | |||
| oid sha256:367a32e5ab2cba7d7a52dfa81f894ebc48bb8ad3be8602863d2ec1f01cca1142 | |||
| size 11102380 | |||
There was a problem hiding this comment.
This sound effect is too long. It's also too big as a result: 11 MB is more than 10% of the full size of the game!
It should be much shorter, and and be configured in the import settings to loop.
Then, you'll need to edit it to make it loop cleanly, without an audible jump when it loops. You can do this by:
- Clip it to slightly longer than the length you want, I suggest 2-4 seconds.
- Cut the clip in half.
- Swap the order of the two shorter clips around, with a little bit of overlap.
- Cross-fade across the overlap.
Reducing the length of the clip will go a long way towards reducing its size, but we may also consider compressing it with Ogg Vorbis.
There was a problem hiding this comment.
This one feels a little bit too long. It's hard to tell in-game though, see code comment below...
There was a problem hiding this comment.
I love this one: it feels right, the player is unravelling into the nothingness.
There was a problem hiding this comment.
Same as for the chasing sample: I think this is too long; and it needs to loop
| State.DEFEATED: | ||
| path_walk_behavior.process_mode = Node.PROCESS_MODE_DISABLED | ||
| follow_walk_behavior.process_mode = Node.PROCESS_MODE_DISABLED | ||
| await void_sfx.finished |
There was a problem hiding this comment.
What you're trying to achieve here is to keep the sound effect playing even once the enemy is defeated by delaying freeing the enemy until the sample is finished. Unfortunately it doesn't work because the code that sets this property doesn't await the setter:
func defeat() -> void:
state = State.DEFEATED
if _live_particles == 0:
queue_free()
# else wait for `_emit_particles` to free this node after all particles are finished.Execution continues immediately after state = State.DEFEATED. You'll need some logic combined with how we wait for the particle emission to finish:
- Connect a one-shot signal handler to
void_sfx.finished - Set a flag for "waiting for sound effect to finish"
- In that signal handler, clear that flag, and call a new "maybe free" function
- At the end of
_emit_particles, replace theif state...with a call to this new "maybe free" function, which needs to look like this:
func _maybe_free() -> void:
if state == State.DEFEATED and _live_particles == 0 and not _awaiting_sfx:
queue_free()
An alternative approach is to reparent the AudioStreamPlayer node to the enemy's parent and arrange for it to be freed when it finishes:
void_sfx.reparent(get_parent())
void_sfx.finished.connect(void_sfx.queue_free)| state_sounds = Dictionary[int, AudioStream]({ | ||
| 0: ExtResource("15_fb75g"), | ||
| 1: ExtResource("16_t0uu3"), | ||
| 2: ExtResource("17_w6xpg"), | ||
| 3: ExtResource("18_t0uu3") | ||
| }) | ||
|
|
||
| [node name="Void_SFX" type="AudioStreamPlayer" parent="OnTheGround/VoidSpreadingEnemy" unique_id=2029654747] |
There was a problem hiding this comment.
This property & node should be part of the res://scenes/game_elements/characters/enemies/void_spreading_enemy/void_spreading_enemy.tscn scene, not added to this particular instance. This enemy is used in many other scenes, e.g. res://scenes/quests/lore_quests/quest_002/3_void_grappling/void_grappling.tscn.
| void_sfx.stream = state_sounds[state] | ||
| void_sfx.play() |
There was a problem hiding this comment.
I understand the desire to use just one AudioStreamPlayer node for all SFX but actually I don't think this is quite what we want. The Alerted sound effect should be played by an AudioStreamPlayer node, because it should be heard at the centre of the screen regardless of the position of the enemy. But the other sound effects are being emitted by the enemy so should be panned to their position and be attenuated as you walk away. This matters in scenes like res://scenes/quests/lore_quests/quest_002/2_grappling_hook/grappling_hook_powerup.tscn where there is an enemy patrolling off to one side: its patrol/idle sfx should be louder the closer you are to it, and should pan as you move past it. (I'm also interested in how scenes like res://scenes/quests/lore_quests/quest_002/3_void_grappling/void_grappling_round_2.tscn sound where you have multiple patrolling enemies - will the sound effect sound OK with multiple instances playing at once?) The way to do this is AudioStreamPlayer2D.
Personally I would approach this by having a separate player(2d) for each state, setting the streams on them, and then calling .stop() on the current one when moving to a new state and .play() on the new one. (In fact you may want to tween the volume of the one you're stopping quickly to 0 before stopping it, rather than stopping it abruptly.)
|
Please add: to the description here to link it to the issue. |
|
Hi! I've pushed the requested changes and updated the PR description. |
Audio Assets & Licensing:
assets/third_party/sounds/void_enemy/with processed audio files for idle, alert, chasing, caught, and defeated states..licensefiles alongside each audio asset specifying original Freesound authors and appropriate SPDX identifiers (CC-BY-4.0/CC0-1.0).Base Scene (
void_spreading_enemy.tscn):AudioStreamPlayer2Dnodes (IdleSFX,ChasingSFX,CaughtSFX,DefeatedSFX) for positional panning and distance attenuation.AnimationPlayeralert track to use the newvoid_sfx_alert.wav.Script (
void_spreading_enemy.gd):_set_stateto transition cleanly between states by stopping previous positional SFX before starting the active one.defeat()to reparentDefeatedSFXtoget_parent()and connect itsfinishedsignal toqueue_free(), ensuring the sound completes cleanly without delaying or breaking node destruction.Pending:
Resolves #1975