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);
};
```