Custom SoundNode for Multiplayer (Local & Third-person)

Ever since the first public release of Unreal Engine 4 I’ve been using a neat little custom SoundNode that makes life easier for audio designers when dealing with mono/stereo sound playback in (networked) games. The node originates from the official ShooterGame sample content available on the Learn-tab of the UE4 launcher. I’ve used it in several others projects too including Switch and the SurvivalGame sample project.

The core functionality of the node is to check whether the owner that plays the sound is a local player or a remotely replicated client. This makes it really easy to play stereo sounds for your controlled Pawn and use 3D-attenuated sounds for any other player or AI controlled bot that exists in the same session.

0059aa1131dd37a9d8af49539aa0a101

Below you’ll find a code dump of the source I’ve been using. The compiler will complain about a few pieces including SURVIVALGAME_API and the #include “SurvivalGame.h” update these to your own project equivalent.

Header File

#pragma once

#include "Sound/SoundNode.h"
#include "SoundNodeLocalPlayer.generated.h"

/**
* Choose different branch for sounds attached to locally controlled player
*
* Originates from ShooterGame project by Epic Games.
*/
UCLASS(hidecategories = Object, editinlinenew)
class SURVIVALGAME_API USoundNodeLocalPlayer : public USoundNode
{
// Make sure you update the *_API above to match your own project eg MYPROJECT_API
	GENERATED_BODY()

	virtual void ParseNodes(FAudioDevice* AudioDevice, const UPTRINT NodeWaveInstanceHash, FActiveSound& ActiveSound, const FSoundParseParameters& ParseParams, TArray<FWaveInstance*>& WaveInstances) override;

	virtual void CreateStartingConnectors(void) override;

	virtual int32 GetMaxChildNodes() const override;

	virtual int32 GetMinChildNodes() const override;

#if WITH_EDITOR

	virtual FText GetInputPinName(int32 PinIndex) const override;

#endif

};

Source File

// Update this header to your own project (eg. MyProject.h)
#include "SurvivalGame.h"
#include "SoundDefinitions.h"
#include "SoundNodeLocalPlayer.h"


void USoundNodeLocalPlayer::ParseNodes(FAudioDevice* AudioDevice, const UPTRINT NodeWaveInstanceHash, FActiveSound& ActiveSound, const FSoundParseParameters& ParseParams, TArray<FWaveInstance*>& WaveInstances)
{
	// The accesses to the Pawn will be unsafe once we thread audio, deal with this at that point
	check(IsInGameThread());

	AActor* SoundOwner = ActiveSound.GetAudioComponent() ? ActiveSound.GetAudioComponent()->GetOwner() : nullptr;
	APlayerController* PCOwner = Cast<APlayerController>(SoundOwner);
	APawn* PawnOwner = (PCOwner ? PCOwner->GetPawn() : Cast<APawn>(SoundOwner));

	const bool bLocallyControlled = PawnOwner && PawnOwner->IsLocallyControlled() && Cast<APlayerController>(PawnOwner->Controller);
	const int32 PlayIndex = bLocallyControlled ? 0 : 1;

	if (PlayIndex < ChildNodes.Num() && ChildNodes[PlayIndex])
	{
		ChildNodes[PlayIndex]->ParseNodes(AudioDevice, GetNodeWaveInstanceHash(NodeWaveInstanceHash, ChildNodes[PlayIndex], PlayIndex), ActiveSound, ParseParams, WaveInstances);
	}
}


void USoundNodeLocalPlayer::CreateStartingConnectors()
{
	InsertChildNode(ChildNodes.Num());
	InsertChildNode(ChildNodes.Num());
}


#if WITH_EDITOR
FText USoundNodeLocalPlayer::GetInputPinName(int32 PinIndex) const
{
	return (PinIndex == 0) ? NSLOCTEXT("Survival", "InputPinName", "Local") : NSLOCTEXT("Survival", "InputPinName", "Remote");
}
#endif


int32 USoundNodeLocalPlayer::GetMaxChildNodes() const
{
	return 2;
}


int32 USoundNodeLocalPlayer::GetMinChildNodes() const
{
	return 2;
}

Code was last updated for 4.10

The node is written in C++, if you are already using a C++ project, you can drop these into your code project and after a few small changes to the file to match your project naming (see comments in the source below) you should be good to go.

For Blueprint users, you can still use this code quite easily. Make sure you have Visual Studio 2015 (see url for install instructions) installed before you proceed. Afterwards, go into the editor and go to File > New C++ Class… This prompts you to add a new source file to your project, name it “SoundNodeLocalPlayer” and base it on “USoundNode” (that’s the parent class that we are extending) I won’t go into detail on compiling your code.

If you have any questions or comments, feel free you leave a reply below! While you’re at it, follow me on Twitter!

2 Responses

  1. Maybe I did something wrong but the node won’t show up in “Sound Cue” Did everything as instructed in this tutorial. changed things where they were necessary,but I also had to change following line
    AActor* SoundOwner = ActiveSound.GetAudioComponent() ? ActiveSound.GetAudioComponent()->GetOwner() : nullptr;
    to this
    UAudioComponent *audioComp = UAudioComponent::GetAudioComponentFromID(ActiveSound.GetAudioComponentID());
    otherwise I would have gotten compiler errors

Leave a comment on this post!