I’m building a co-op game and I’m trying to ensure that there are some tasks in the game that are achievable together. One of those tasks is lighting a fire. However, I’m having problems replicating the fire to the clients.
When a character goes to use an item. The particle effect doesn’t spawn on his screen or any of the other clients.
void AHiddenFire::UseTheItem()
{
//This code also does not work. Validated its not just particle effects.
//FVector NewLocation = this->GetActorLocation() + FVector(0, 0, 300.0f);
//this->SetActorLocation(NewLocation);
UGameplayStatics::SpawnEmitterAtLocation(this, FireSpawnFX, GetActorLocation(), GetActorRotation());
}
}
I have also tried multicast. I set the UseItem to Netmulticast and then removed the if (Role < ROLE_Authority) code from the UseItem_Implementation and it worked on just the client that called it. If I left the if (Role < ROLE_Authority) in there, it was never called at all and spawned on no clients.
Any help would be GREATLY appreciated. Even pointing me to a particular location in an example project would be helpful. Thank you for your time.
You’ll need to make sure that it’s only clients trying to SpawnEmitterAtLocation, as the server doesn’t care about anything graphical.
Secondly, you’ll need to make sure that the actor is relicated, so all the clients have a copy of it.
Personally, I would setup a repnotify to show that the AHiddenItem has been “used”. The absolute most simple way to do it would be to have a bool replicated variable. Make sure it is the server setting this as true (or false) and then in the OnRep_ function, use the SpawnEmitterAtLocation.
You also have the case of if the hosted game is as a ListenServer, where that would also need to use SpawnEmitterAtLocation, but only in the case of the hosting “client” / listen server. You will only need to do this if you plan on having players hosting games and not only using dedicated servers.
Make sure you have “OnlineSubsystem” in your MYPROJECT.Build.cs, PublicDependencyModuleNames;
[h]
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "HiddenItem.generated.h"
UCLASS()
class PLAYGROUND_API AHiddenItem : public AActor
{
GENERATED_BODY()
public:
AHiddenItem();
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
// Replicated as notify so clients get event when toggled
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Unlocked, Category = "Hidden Actor")
bool bUnlocked;
// The particle system to spawn
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Hidden Actor")
class UParticleSystem *UnlockedSystemTemplate;
// The collider used to know when a character has come close to the hidden actor
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "Hidden Actor")
class UBoxComponent *CollisionBox;
// Scene component to use as a spawn location instead of relying on GetActorLocation( ) + Offset
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "Hidden Actor")
class USceneComponent *SpawnLocation;
private:
// ParticleSystemComponent to keep reference of what SpawnEmitterAtLocation created
UPROPERTY( )
class UParticleSystemComponent *UnlockedSystemComponent;
// RepNotify function to know if bUnlocked has been set by server
UFUNCTION( )
void OnRep_Unlocked( );
// Function to call to create particle system
UFUNCTION( )
void ToggleUnlockedSystem( );
// Overlap bind for CollisionBox
UFUNCTION( )
void OnBeginOvlerlap(UPrimitiveComponent *OverlappedComponent,
AActor *OtherActor,
UPrimitiveComponent *OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult &SweepResult);
};
[.cpp]
#include "HiddenItem.h"
// Includes for all the types we are using
#include "UnrealNetwork.h"
#include "Components/BoxComponent.h"
#include "Particles/ParticleSystem.h"
#include "GameFramework/Character.h"
#include "Particles/ParticleSystemComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Components/SceneComponent.h"
AHiddenItem::AHiddenItem()
{
// Probably not needed but default
PrimaryActorTick.bCanEverTick = true;
// Create box and bind overlap / set default size and set as root
CollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("Box Collider"));
CollisionBox->SetBoxExtent(FVector(128.f, 128.f, 128.f));
CollisionBox->OnComponentBeginOverlap.AddDynamic(this, &AHiddenItem::OnBeginOvlerlap);
SetRootComponent(CollisionBox);
// Child component to use as location to create particle system
SpawnLocation = CreateDefaultSubobject<USceneComponent>(TEXT("Spawn Location"));
SpawnLocation->SetupAttachment(CollisionBox);
// Replicated so other clients get copy
bReplicates = true;
}
void AHiddenItem::BeginPlay()
{
Super::BeginPlay();
}
void AHiddenItem::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AHiddenItem::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> &OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// set bUnlocked to replicate
DOREPLIFETIME(AHiddenItem, bUnlocked);
}
void AHiddenItem::OnRep_Unlocked( )
{
ToggleUnlockedSystem( );
}
void AHiddenItem::ToggleUnlockedSystem( )
{
UWorld *World = GetWorld( );
// Check for world
if(World)
{
// If bUnlocked got set to true
if(bUnlocked)
{
UnlockedSystemComponent = UGameplayStatics::SpawnEmitterAtLocation(World, UnlockedSystemTemplate,
SpawnLocation->GetComponentLocation( ),
SpawnLocation->GetComponentRotation( ),
true);
}
else
{
// If bUnlocked got set to false
if(UnlockedSystemComponent)
{
UnlockedSystemComponent->Deactivate( );
UnlockedSystemComponent = nullptr;
}
}
}
}
void AHiddenItem::OnBeginOvlerlap(UPrimitiveComponent *OverlappedComponent,
AActor *OtherActor,
UPrimitiveComponent *OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult &SweepResult)
{
// Overlap only on authority
if(Role == ROLE_Authority)
{
ACharacter *OverlappingCharacter = Cast<ACharacter>(OtherActor);
// Check if character overlapping
if(OverlappingCharacter)
{
// If not set true to bUnlocked
if(!bUnlocked)
{
// Set true
bUnlocked = true;
// Handle listen server case
if(GetNetMode() == ENetMode::NM_ListenServer)
{
ToggleUnlockedSystem( );
}
}
}
}
}