Skip to content

Implement SFX for void enemy - #2862

Open
IDynamixI985 wants to merge 4 commits into
endlessm:mainfrom
IDynamixI985:void_sfx
Open

Implement SFX for void enemy#2862
IDynamixI985 wants to merge 4 commits into
endlessm:mainfrom
IDynamixI985:void_sfx

Conversation

@IDynamixI985

@IDynamixI985 IDynamixI985 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor
  • Audio Assets & Licensing:

    • Created the folder assets/third_party/sounds/void_enemy/ with processed audio files for idle, alert, chasing, caught, and defeated states.
    • Added REUSE-compliant .license files alongside each audio asset specifying original Freesound authors and appropriate SPDX identifiers (CC-BY-4.0 / CC0-1.0).
    • Optimized and looped continuous sounds (such as chasing) with clean cross-fades.
  • Base Scene (void_spreading_enemy.tscn):

    • Moved SFX nodes directly into the base enemy scene rather than scene-specific instances.
    • Configured dedicated AudioStreamPlayer2D nodes (IdleSFX, ChasingSFX, CaughtSFX, DefeatedSFX) for positional panning and distance attenuation.
    • Updated the audio stream in the AnimationPlayer alert track to use the new void_sfx_alert.wav.
  • Script (void_spreading_enemy.gd):

    • Updated _set_state to transition cleanly between states by stopping previous positional SFX before starting the active one.
    • Refactored defeat() to reparent DefeatedSFX to get_parent() and connect its finished signal to queue_free(), ensuring the sound completes cleanly without delaying or breaking node destruction.
  • Pending:

    • Audio for the void consuming tiles and props is still pending.

Resolves #1975

@IDynamixI985
IDynamixI985 requested a review from a team as a code owner September 9, 2026 00:41
@IDynamixI985

Copy link
Copy Markdown
Contributor Author

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.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

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 wjt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These .m4a files are not understood by Godot and hence not used, please remove.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above.

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:367a32e5ab2cba7d7a52dfa81f894ebc48bb8ad3be8602863d2ec1f01cca1142
size 11102380

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Image

Then, you'll need to edit it to make it loop cleanly, without an audible jump when it loops. You can do this by:

  1. Clip it to slightly longer than the length you want, I suggest 2-4 seconds.
  2. Cut the clip in half.
  3. Swap the order of the two shorter clips around, with a little bit of overlap.
  4. Cross-fade across the overlap.
Image

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one feels a little bit too long. It's hard to tell in-game though, see code comment below...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this one: it feels right, the player is unravelling into the nothingness.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 the if 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)

Comment on lines +144 to +151
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]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +72 to +73
void_sfx.stream = state_sounds[state]
void_sfx.play()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@wjt

wjt commented Sep 10, 2026

Copy link
Copy Markdown
Member

Please add:

Resolves https://github.com/endlessm/threadbare/issues/1975

to the description here to link it to the issue.

@IDynamixI985

Copy link
Copy Markdown
Contributor Author

Hi! I've pushed the requested changes and updated the PR description.

@IDynamixI985
IDynamixI985 requested a review from wjt September 12, 2026 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add void enemy sound effects

2 participants