An [[Emitter]] is a 3D position that casts rays, and can be discovered by other emitters. Emitters can be created at any time, with customisable position and ray count properties: ```cs var emitter = new Emitter() { Name = "Player", Position = new Vector3F(10), ReverbRayCount = 128, ReverbBounceCount = 8, }; context.AddEmitter(emitter); ``` > The full list of properties is here: [[Emitter]]. ## Position Interface You can manually change the position of an emitter at any time: ```cs emitter.position = new Vector3F(10); emitter.position = new FuncPosition(() => new Vector3F(20)); ``` However it's best to implement the [[IPosition]] interface on your game objects, and then assign your object directly to the emitter. For example if you want an emitter to follow an enemy in your game, your enemy class would implement [[IPosition]]: ```cs public class Enemy : IPosition { public Vector3F position; // Implement the interface public Vector3F GetPosition() => position; } ``` Then use the enemy directly as the emitter's position ```cs var enemy = new Enemy(); var emitter = new Emitter() { Position = enemy }; ``` This emitter's position will now automatically match the enemy's position. ## The Initial Raytrace When an emitter is created, it starts in an `initialising` state where it is waiting to be raytraced. Do not play sounds in your game engine yet, as this emitter might be muffled. New emitters are raytraced when `context.Update()` is called: ```cs void Update() { var emitter1 = new Emitter(); var emitter2 = new Emitter(); var emitter3 = new Emitter(); context.AddEmitter(emitter1); context.AddEmitter(emitter2); context.AddEmitter(emitter3); // The 3 emitters above are placed into a batch and raytraced together context.Update(); } ``` > `context.Update` must be called regularly. Once per frame is typical, but you can call it as often as you like ## Raytracing Results The first time an emitter is raytraced, its `OnRaytracingComplete` callback will fire. This callback will only contain reverb and ambient permeation data: ```cs var emitter = new Emitter() { Position = new Vector3F(10), ReverbRayCount = 512, ReverbBounceCount = 8, AmbientPermeationRayCount = 128, AmbientPermeationBounceCount = 4, }; // Access stats when raytracing first completes emitter.OnRaytracingComplete = () => { // Reverb var outsidePercent = emitter.ProcessedReverb.OutsidePercent; var decayTime = emitter.EAX.DecayTime; // Ambient permeation var gainLF = emitter.AmbientPermeationGainLF; var gainHF = emitter.AmbientPermeationGainHF; }; ``` To determine how muffled an emitter is, it must be raytraced by another emitter. It's typical to have a 'listener' emitter that follows the camera, and multiple 'target' emitters that are raytraced: ```cs // Create listern and enemy var listener = new Emitter() { Name = "Camera", Position = new FuncPosition(() => cameraPosition), ReverbRayCount = 512, ReverbBounceCount = 8, // Determine how many rays to cast towards other rays OcclusionRayCount = 1024, OcclusionBounceCount = 8, }; context.AddEmitter(listener); var enemyEmitter = new Emitter() { Name = "Enemy", Position = enemy, ReverbRayCount = 32, ReverbBounceCount = 8, }; context.AddEmitter(enemyEmitter); listener.AddTarget(enemyEmitter); // Callback when raytracing completes enemyEmitter.OnRaytracedByAnotherEmitter = (Emitter other) => { var filter = other.GetTargetFilter(enemyEmitter); var gainLF = filter.gainLF; var gainLF = filter.gainHF; // PSEUDOCODE - play a sound with a low pass filter Godot.PlaySound(SoundType.Footstep, enemyEmitter.position, filter); }; ```