Download

Play a montage over the network

This is not a yet another play-a-montage-over-the-network question. I am going to share something I learned when I was working on a personal project of fighting game and see if anyone can give me some suggestions.

The easiest way to play a montage over the network seems to use a **Server **function to call a **NetMulticast **function:


UFUNCTION(Server, Reliable, WithValidation, Category=Animation)
void ServerPlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate = 1.f, FName StartSectionName = NAME_None);

UFUNCTION(NetMulticast, Reliable, WithValidation, Category = Animation)
void MulticastPlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate = 1.f, FName StartSectionName = NAME_None);


void AMyCharacter::ServerPlayAnimMontage_Implementation(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)
{
	MulticastPlayAnimMontage(AnimMontage, InPlayRate, StartSectionName);
}

void AMyCharacter::MulticastPlayAnimMontage_Implementation(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)
{
	PlayAnimMontage(AnimMontage, InPlayRate, StartSectionName);
}

However, what if I want to play a montage locally immediately on the client and then request the server to broadcast it?
It is possible with the RPC features of UE4, but the problem is when the server broadcasts the montage play request, the owning client will also receive it, which results in double playback.
Although we can filter out the second playback somehow, the bandwidth for that network package is wasted. Unfortunately, there seems not too much I can do about it.


void AMyCharacter::NetPlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)
{
	if (UKismetSystemLibrary::IsServer(this))
	{
		MulticastPlayAnimMontage(AnimMontage, InPlayRate, StartSectionName);
	}
	else
	{
		PlayAnimMontage(AnimMontage, InPlayRate, StartSectionName);
		ServerPlayAnimMontage(AnimMontage, InPlayRate, StartSectionName);
	}
}

void AMyCharacter::ServerPlayAnimMontage_Implementation(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)
{
	MulticastPlayAnimMontage(AnimMontage, InPlayRate, StartSectionName);
}

void AMyCharacter::MulticastPlayAnimMontage_Implementation(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName)
{
	if (Role != ROLE_AutonomousProxy)
	{
		PlayAnimMontage(AnimMontage, InPlayRate, StartSectionName);
	}
}

Actually since this is a fighting game and there only one server and one client, there’s no need to broadcast it on server in the case of the playback request originated from the client. But let’s say there’s a spectator.

After I studied a document about CharacterMovementComponent, I found another way in UE4 to replicate things over the network but it is much more complex since prediction and correction are involved. Nevertheless it is for movement and unlikely suitable for montage play, it kind of does what I tended to do: carrying out a task locally and then replicate it to the server. Compared to what they did, my case is more like I completely omit the correction from the sever on the owning client in MulticastPlayAnimMontage_Implementation. But I have no idea what I should do for correction in that case.

Does my approach potentially cause serious issues? Should I go back to use the classic server-rules-everything method and blame unresponsiveness to network latency? What a networked action game using UE4 usually does? Any suggestions will be appreciated.

Does anyone have a better way?

@Isatin. What about a replicated property that has a RepUsing and then set the replication condition to Ignore Owner. I saw this for gun firing animation in the ShooterTutorial. On the Replication Callback Function, if the value is like, true, or something similiar and the montage, animation, what have you is not playing, play it. Since the owner is already playing the animation, and he ignores the replicated value due to the condition - it may not go over the network. Depends who does the filtering on replication conditions.

Thanks for your reply.

I found a web page on wiki talking about the same thing. So it’s possible with COND_SkipOwner.