Hi all, I seem to be having issues understand why my multicast RPC isn’t firing on my connected client (I think that’s the issue anyways). My understanding was that if I wanted to fire off some sound and particle FX when I fire my gun, I’d want to go like: Function-> Server_Function->Multi_Function and the Multi_Function would actually contain the code to fire off the FX. The problem is that it doesn’t appear to work for me and I don’t know why. Here’s some code as well as an accompanying video. As you can see, the Listening Server can see it’s own and the connected client’s actions, but the Client can see neither.
CharacterBase.cpp
void ACharacterBase::PrimaryAttack_Pull()
{
GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Green, FString::Printf(TEXT("Client called Primary attack, calling Server RPC")));
Server_PrimaryAttack_Pull();
}
void ACharacterBase::Server_PrimaryAttack_Pull_Implementation()
{
GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Green, FString::Printf(TEXT("Server calling Multicast primary attack RPC")));
Multi_PrimaryAttack_Pull();
}
void ACharacterBase::Multi_PrimaryAttack_Pull_Implementation()
{
GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Green, FString::Printf(TEXT("Multicast fired on %s"), *UEnum::GetValueAsString(GetRemoteRole())));
if (EquippedWeapon)
EquippedWeapon->PullTrigger();
}
void ACharacterBase::PrimaryAttack_Release()
{
Server_PrimaryAttack_Release();
}
void ACharacterBase::Server_PrimaryAttack_Release_Implementation()
{
Multi_PrimaryAttack_Release();
}
void ACharacterBase::Multi_PrimaryAttack_Release_Implementation()
{
if (EquippedWeapon)
EquippedWeapon->ReleaseTrigger();
}
GunBase.cpp
void AGunBase::PullTrigger_Implementation()
{
if (GetWorldTimerManager().TimerExists(BurstRetriggerHandle) || CurMagazine <= 0 || bReloading)
return;
BulletsFired = 0;
GetWorldTimerManager().SetTimer(FireHandle, this, &AGunBase::Fire, 60/FireRate, true, 0);
GetWorldTimerManager().SetTimer(BurstRetriggerHandle, BurstRetriggerDelay, false);
}
void AGunBase::ReleaseTrigger_Implementation()
{
if (BulletsFired >= BurstAmount)
GetWorldTimerManager().ClearTimer(FireHandle);
}
void AGunBase::SpawnBullet_Implementation()
{
UAISense_Hearing::ReportNoiseEvent(GetWorld(), GetActorLocation(), 1.0f, GetOwner(), 0.0f);
APawn* OwningPawn = Cast<APawn>(GetOwner());
if (ACharacterBase* OwningChar = Cast<ACharacterBase>(GetOwner()))
{
OwningChar->GetMesh()->GetAnimInstance()->Montage_Play(OwningChar->FiringAnim);
}
if (APlayerCharacter* PlayerChar = Cast<APlayerCharacter>(GetOwner()))
{
if (FireAnimation1P) PlayerChar->GetMesh1P()->GetAnimInstance()->Montage_Play(FireAnimation1P);
if (APlayerController* PC = Cast<APlayerController>(PlayerChar->GetController())) PC->ClientPlayForceFeedback(FireFeedback);
}
for (int i = 0; i < MultiShot; ++i)
{
if (ProjectileClass)
{
FVector Location = Mesh->DoesSocketExist("Muzzle") ? Mesh->GetSocketLocation("Muzzle") : GetActorLocation() + GetActorForwardVector()*50000.0f;
FRotator Rotation;
if (OwningPawn)
{
Rotation = OwningPawn->GetBaseAimRotation() + FRotator(FMath::RandRange(-VerticalSpread, VerticalSpread), FMath::RandRange(-HorizontalSpread, HorizontalSpread),0);
} else
{
Rotation = GetActorRotation();
}
FActorSpawnParameters ActorSpawnParameters;
ActorSpawnParameters.Owner = this;
ActorSpawnParameters.Instigator = Cast<ACharacterBase>(this->GetOwner());
GetWorld()->SpawnActor(ProjectileClass, &Location, &Rotation, ActorSpawnParameters);
} else
{
FHitResult Hit;
FVector TraceStart;
FRotator EyeRotation;
if (OwningPawn && OwningPawn->GetController())
{
OwningPawn->GetController()->GetPlayerViewPoint(TraceStart, EyeRotation);
EyeRotation = EyeRotation + FRotator(FMath::RandRange(-VerticalSpread, VerticalSpread), FMath::RandRange(-HorizontalSpread, HorizontalSpread),0);
//OwningPawn->GetActorEyesViewPoint(TraceStart, EyeRotation);
//EyeRotation = OwningPawn->GetBaseAimRotation() + FRotator(FMath::RandRange(-VerticalSpread, VerticalSpread), FMath::RandRange(-HorizontalSpread, HorizontalSpread),0);
} else
{
TraceStart = GetActorLocation();
EyeRotation = GetActorRotation();
}
TArray<AActor*> ActorsToIgnore;
ActorsToIgnore.Add(this);
ActorsToIgnore.Add(GetOwner());
// The actual bullet trace, with a width for accuracy forgiveness
//UKismetSystemLibrary::SphereTraceSingle(GetWorld(), TraceStart, TraceEnd, 20, UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_Camera), false, ActorsToIgnore, EDrawDebugTrace::None, Hit, true, FLinearColor::Red, FLinearColor::Green, 5);
AController* EventInstigator = nullptr;
if (OwningPawn)
{
EventInstigator = OwningPawn->GetController();
}
UMyCustomBlueprintFunctionLibrary::FireHitScanBullet(Hit, GetWorld(), ActorsToIgnore, TraceStart, EyeRotation.Vector(), Range, FalloffCurve, Damage, Force, this, EventInstigator);
if (Mesh->DoesSocketExist("Muzzle") && TrailPFX)
{
FVector TrailEnd = (Hit.bBlockingHit) ? Hit.ImpactPoint : Hit.TraceEnd;
UNiagaraComponent* TrailPFXComponent = UNiagaraFunctionLibrary::SpawnSystemAttached(TrailPFX, Mesh, "Muzzle", FVector(0,0,0), FRotator(0,0,0), EAttachLocation::SnapToTarget, true);
TrailPFXComponent->SetVectorParameter("BeamEnd", TrailEnd);
}
if (Hit.bBlockingHit)
{
if (HitSound) UGameplayStatics::PlaySoundAtLocation(GetWorld(), HitSound, Hit.Location);
if (ImpactDecal && !Cast<IDamageableInterface>(Hit.GetActor()))
{
FVector Location = Hit.ImpactPoint;
FRotator Rotation = Hit.Normal.Rotation() + FRotator(-90, 0, 0);
AActor* Decal = GetWorld()->SpawnActor(ImpactDecal, &Location, &Rotation);
Decal->AttachToComponent(Hit.GetComponent(), FAttachmentTransformRules::KeepWorldTransform);
//UGameplayStatics::SpawnDecalAttached(GetWorld(), FVector(10,10,10), ) //Perhaps optimize this in the future
}
}
}
}
BulletsFired++;
if (BulletsFired==BurstAmount)
{
ReleaseTrigger();
}
SpawnMuzzleFX();
}
void AGunBase::SpawnMuzzleFX_Implementation()
{
if (APlayerCharacter* Char = Cast<APlayerCharacter>(GetOwner()))
if (FiringCameraShake && Char->IsLocallyControlled())
Cast<APlayerController>(Char->GetController())->PlayerCameraManager->StartCameraShake(FiringCameraShake, 1, ECameraShakePlaySpace::CameraLocal);
if (FiringSound)
UGameplayStatics::SpawnSoundAttached(FiringSound, GetRootComponent());
if (Mesh->DoesSocketExist("Muzzle") && MuzzlePFX && !ScopeActive)
UNiagaraFunctionLibrary::SpawnSystemAttached(MuzzlePFX, Mesh, "Muzzle", FVector(0,0,0), FRotator(0,0,0), EAttachLocation::SnapToTarget, true);
}
Also, do I need to do the Function->Server_Function->Multi_Function calls in my GunBase if its been initiated by the multicast in the CharacterBase anyways?