HammeriteAudioEmitter3D

Audio. Inherits Node3D

A thing in the world that makes a sound, as a node.

The manager's handle API is the machinery: create a sound, hold its handle, push the position in before every play, remember to delete it. That is fine for the systems that own a sound's whole lifetime and wrong for the common case, which is "this object makes this noise when it does that". A footstep ended up as a handle created at startup, a position hand-synced on every step, and a delete nobody remembered - all of it bookkeeping about a sound that simply belongs to the player. Add one of these as a child of whatever makes the noise, point it at a file, and call play(). It follows its own transform, so a moving source needs nothing said about it; it finds the audio manager itself, so no caller digs through HammeriteMap3D.get_runtime_data(); and it cleans up after itself when it leaves the tree. For a noise with no object behind it - an impact at a point in space, a sound whose emitter is about to be freed - use HammeriteAudio.play_one_shot() instead.

@onready var _steps: HammeriteAudioEmitter3D = $Footsteps
func _on_step() -> void:
_steps.play()

Properties

TypeNameDefault
Stringsound_file""
StringNamesound_type&""
floatvolume_db0.0
boolloopfalse
boolautoplayfalse
boolpropagatetrue

Methods

ReturnsMethod
voidplay()
voidstop()
boolis_playing()

Signals

finished

signal finished()

Emitted when a non-looping sound has played through.

Property descriptions

sound_file

var sound_file: String = ""

The recording to play, by its name in HammeriteAudioRuntime.sounds - doors/creak.ogg under one of its search paths - or by the path the inspector's file picker writes. Changing it drops the current sound.

sound_type

var sound_type: StringName = &""

What kind of sound this is, for anything that might hear it. Empty means the sound is audible to the player but invisible to gameplay - ambience, music, feedback - which is the right default: most sounds are not events. A named type is reported to every HammeriteAudioReceiver3D subscribed to it whenever this emitter plays, and costs nothing when none are.

volume_db

var volume_db: float = 0.0

Mix level in dB.

loop

var loop: bool = false

Loop until stopped, rather than playing once. Changing it drops the current sound.

autoplay

var autoplay: bool = false

Play as soon as this node enters the tree - each time it does - or as soon as there is a manager to play it on, if there is none yet.

propagate

var propagate: bool = true

Route the sound through the audio graph - reverb, occlusion, propagation round corners. Turn it off for sounds that should always be heard as if they were next to the listener.

Method descriptions

play()

func play() -> void

Start the sound. Restarts it if it is already playing.

stop()

func stop() -> void

Stop the sound, if it is playing.

is_playing()

func is_playing() -> bool

Whether the sound is playing in the world. A one-shot played too far away to be heard is, for its length, as is finished then: what waits on it waits as long wherever the camera is.

Hammerite 1.0 · built from a6dd634, 2026-10-10