King3802
(碎碎念King)
1
问题描述:地图中有很多蓝色小方块,我希望随机选择几个小方块更换材质。在BeginPlay中调用多播函数进行更改。尝试多次仍无法解决,仅有服务器端的材质被正确设置。
在BeginPlay中仅有服务器端可以获取到GameMode,之后就是服务器端调用多播函数,将在服务器端和所有客户端调用设置新材质。(应该理解正确?)
头文件
UCLASS()
class MYPROJECT_API ATargetCube : public AActor
{
GENERATED_BODY()
public:
ATargetCube();
protected:
virtual void BeginPlay() override;
public:
UFUNCTION(NetMulticast, Reliable)
void Multicast_SetMaterial();
private:
UPROPERTY(EditDefaultsOnly)
UBoxComponent* BoxComponent;
UPROPERTY(EditDefaultsOnly)
UStaticMeshComponent* BoxMesh;
UPROPERTY(EditDefaultsOnly)
UMaterialInstance* CubeMaterial;
};
CPP文件
ATargetCube::ATargetCube()
{
PrimaryActorTick.bCanEverTick = false;
bReplicates = true;
BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComponent"));
BoxComponent->SetBoxExtent(FVector(50.f, 50.f, 50.f));
SetRootComponent(BoxComponent);
BoxMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BoxMesh"));
BoxMesh->SetupAttachment(GetRootComponent());
}
void ATargetCube::BeginPlay()
{
Super::BeginPlay();
if (AMyProjectGameMode* GameMode = Cast(UGameplayStatics::GetGameMode(this)))
{
if (GameMode->MustSpawnDoublePointsCube() || (GameMode->CanSpawnDoublePointsCube() && FMath::RandBool()))
{
GameMode->AddDoublePointsCubeCount();
Multicast_SetMaterial();
}
GameMode->AddCubeCount();
}
}
void ATargetCube::Multicast_SetMaterial_Implementation()
{
BoxMesh->SetMaterial(0, DoublePointsCubeMaterial);
}
King3802
(碎碎念King)
3
并且我尝试修改策略,在TargetCube的BeginPlay函数中,将需要修改材质的所有Actor收集放到GameMode中,然后在Character的Beginplay函数中进行统一的多播设置材质实例。此时现象也很奇怪,开启1个服务器端,1个客户端,则只有服务器端现象正确;开启1个服务器端,2个客户端,则服务器端和1个客户端现象正确。
你好,目前你的代码逻辑是如果能够成功获取Gamemode那么就尝试改变颜色,而客户端上获取不到Gamemode,你借此来判断Actor在客户端还是服务器,这段完全没有问题。有问题的是你的Actor类并没有复制,事实上多播函数并没有用。
你需要在Actor类的构造函数中将Actor标记为复制。这样Actor在客户端上面的实例才会和服务器上的Actor”相关“。
//...some code
bReplicates = true;
//...some other code
详细看一下这篇快速入门。
顺便提一下,我不建议在“BeginPlay”中执行和远程相关的函数,因为这个时候标记为“复制”的属性、Actor会从服务器复制到客户端,而这个过程取决于网络条件,所以常常会出现属性值不对、指针为空的问题。
King3802
(碎碎念King)
5
您好,感谢您的回答,但是我提供的代码里构造函数已经标记为可复制。所以可能的问题是您所说的指针为空?
除了使用多播函数,我所能想到的就是属性同步。所以我做了如下更改。
UCLASS()
class MYPROJECT_API ATargetCube : public AActor
{
GENERATED_BODY()
public:
ATargetCube();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
protected:
virtual void BeginPlay() override;
private:
UPROPERTY(EditDefaultsOnly)
UBoxComponent* BoxComponent;
UPROPERTY(EditDefaultsOnly)
UStaticMeshComponent* BoxMesh;
UPROPERTY(EditDefaultsOnly)
UMaterialInstance* DoublePointsCubeMaterial;
UPROPERTY(ReplicatedUsing = "OnRep_UseDoublePointsCubeMaterial")
bool bUseDoublePointsCubeMaterial = false;
UFUNCTION()
void OnRep_UseDoublePointsCubeMaterial();
};
CPP文件是:
ATargetCube::ATargetCube()
{
PrimaryActorTick.bCanEverTick = false;
bReplicates = true;
BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComponent"));
BoxComponent->SetBoxExtent(FVector(50.f, 50.f, 50.f));
SetRootComponent(BoxComponent);
BoxMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BoxMesh"));
BoxMesh->SetupAttachment(GetRootComponent());
}
void ATargetCube::BeginPlay()
{
Super::BeginPlay();
if (AMyProjectGameMode* GameMode = Cast<AMyProjectGameMode>(UGameplayStatics::GetGameMode(this)))
{
Score = GameMode->GetCubeScore();
if (GameMode->MustSpawnDoublePointsCube() || (GameMode->CanSpawnDoublePointsCube() && FMath::RandBool()))
{
GameMode->AddDoublePointsCubeCount();
bUseDoublePointsCubeMaterial = true;
BoxMesh->SetMaterial(0, DoublePointsCubeMaterial);
}
GameMode->AddCubeCount();
}
else if (bUseDoublePointsCubeMaterial)
{
BoxMesh->SetMaterial(0, DoublePointsCubeMaterial);
}
}
void ATargetCube::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ThisClass, bUseDoublePointsCubeMaterial);
}
void ATargetCube::OnRep_UseDoublePointsCubeMaterial()
{
bUseDoublePointsCubeMaterial = true;
BoxMesh->SetMaterial(0, DoublePointsCubeMaterial);
}
通过这种方式成功实现了我的需求。