I have been going back to Lethal Company every once in a while and I’ve been fascinated by a lot of features in this game as a player and a developer. For this blog, I wanted to focus on one of my favourite features and it’s how voice chat manipulation is implemented technically. So I thought recreating it myself will be a fun challenge.
Tools:
Main tools I loved working with and investing in for this technical implementation:
- Dissonances VOIP: Lethal Company uses this asset so It’s pretty reliable and seems to integrate to any project just fine. It supports lots of network solutions too so it gives you flexibilty in your choices.
- FMOD For Unity: It has a really nice learning curve and any developer can set it up easily. It was my first time using FMOD but I didn’t have any problems checking documentation and adding audio myself.
- Dissonance For FMOD: Luckily, there is an integration already made for FMOD playback
- Fishnet SDK: There are multiple networking solutions out there but I have experience with fishnet and it integerates well with Dissonance. In addition, It’s an open source solution & supported by an active community which makes it reliable for me.
Approach:
FMOD voice playback integration routes players voice through an audio bus in FMOD, so we can add any effect to that audio bus. My initial thought process was to create a specific Voice bus for players and then manipulate audio through adding effects to the bus, however it didn’t seem as dynamic as I wanted.
To Create a voice bus on FMOD:
- Window→Mixer
- Create VoiceChat bus
- Add VoiceChat path to FMOD Bus playback as bus:/VoiceChat
Second approach is going a step backward in the voice emitting route and checking the channel before it reaches the bus. Since FMOD playback instantiates a unique instance for each player, that means we can use each channel (per player) and apply effects on it before routing to the VoiceChat bus. Then it gives us the flexiblity we need.
- Instantiating a player-specific channel made everything easier, so firstly I stored the FMOD voice playback channel for each player
private void SetVoicePlaybackDictionary()
{
fmodVoicePlaybacks.Clear();
fmodVoicePlaybacks = dissonanceSetup.GetComponentsInChildren<FMODVoicePlayback>().ToList();
FMODVoicePlayBackDictionary.Clear();
foreach (var voicePlayBack in fmodVoicePlaybacks)
FMODVoicePlayBackDictionary.TryAdd(voicePlayBack.PlayerName, voicePlayBack.Channel);
}
- Once channels are mapped to players, we can dynamically add or remove effects according to the game mechanics
This leaves us with the last step, modifying the channel based on the player state (e.g., adding reverb in specific zones).
To add an effect to a channel we need to create DSP effect and use addDSP() method to the player’s channel
public void AddChannelEffect(Channel channel)
{
var dsp = GetEchoEffect();
channel.addDSP(FMOD.CHANNELCONTROL_DSP_INDEX.HEAD, dsp);
}
private List<DSP> GetEchoEffect()
{
var dspEffectList = new List<DSP>();
CreateDspEffect(DSP_TYPE.SFXREVERB, out var dspEffect);
dspEffectList.Add(dspEffect);
SetEchoEffectParams(dspEffect);
return dspEffectList;
}
private void SetEchoEffectParams(DSP dspEffect)
{
dspEffect.setParameterFloat((int)FMOD.DSP_SFXREVERB.DECAYTIME, 2.0f);
}
Removing the Effect would require the DSP type to remove from the channel (e.g., DSP_TYPE.SFXREVERB
public void GetDspEffects(SfxEffectType sfxEffectType, out List<DSP_TYPE> dsp_types)
{
dsp_types = new List<DSP_TYPE>();
switch (sfxEffectType)
{
case SfxEffectType.Echo:
dsp_types.Add(DSP_TYPE.SFXREVERB);
break;
}
}
public void RemoveChannelEffect(Channel channel, SFXEffectsManager.SfxEffectType effectType)
{
GetDspEffects(effectType, out List<DSP_TYPE> dsp_types);
foreach (var dspType in dsp_types)
{
channel.getNumDSPs(out var numDSPs);
for (int i = 0; i < numDSPs; i++)
{
channel.getDSP(i, out var dsp);
dsp.getType(out var type);
if (type != dspType) continue;
channel.removeDSP(dsp);
dsp.release();
}
}
}
This concludes how all these tools were very easy to integrate together and made me reach my goal in the quickest way possible.
Thanks for reading! Tips and feedback are super welcome.
Top comments (0)