Replicating Dynamic Materials Instance

Alright, so I have some buildings you can spawn in-game and place by clicking. Before confirming placement I wanted the building material to be partially transparent.

So, in C++ I create an ABuilding from a Server function in my PlayerController, then in the building class Constructor I set a ***UMatieral ***variable of my building material in the constructor, and then in OnConstruction() I create the UMatieralInstanceDynamic from the ***UMaterials ***and call my NetMulticast Function Server_SetBuildingMaterials(UMatieralInstanceDynamic **Mat1, UMatieralInstanceDynamic Mat2) where the created Dynamic Materials are set to a *Replicated *variable in the building class. From there, once the building is placed, *NetMulticast *function **Server_SetBuildingState(**NewState) is called from my player controller which calls BuildingMat->SetScalarParameterValue(FName(“Transparency”), 1.0f);

My problem is, in **Server_SetBuildingState(**NewState), my client side ***UMatieralInstanceDynamic ***variables are null.

Logs:



LogNetPackageMap: Warning: FNetGUIDCache::SupportsObject: MaterialInstanceDynamic /Game/TopDownCPP/Maps/UEDPIE_0_TopDownExampleMap.TopDownExampleMap:PersistentLevel.BP_UnitBuilding_C_0.MaterialInstanceDynamic_1 NOT Supported.

LogTemp: Warning: Change unit building state[ListenServer]
LogTemp: Warning: Unit building mat is NULL [Client]
LogTemp: Warning: Gold building mat is NULL [Client]


Player Controller:



// Server Function
void ARtsPlayerController::Server_CreateUnitBuilding_Implementation()
{
     // Check if they have enough gold
     if(PlayerPawn->Gold >= PlayerPawn->UnitPrice)
     {
          PlacementBuffer = GetWorld()->SpawnActor<ABuilding>(UnitBuildingClass);
          if(PlacementBuffer)
          {
               Cast<ABuilding>(PlacementBuffer)->SetOwner(this);
               Cast<ABuilding>(PlacementBuffer)->SetOwnerUserName(UserName);
               // Increment Building count
               PlayerPawn->Server_AddUnitBuilding();
               // Makes Tick call Server_PositionPlacement()
               Server_ChangePlayerState(EPlayerState::Placing);
               Cast<ABuilding>(PlacementBuffer)->Server_SetBuildingState(EBuildingState::Preview);
          }
     }
     else
     {
          Server_ChangePlayerState(EPlayerState::Default);
     }
}

void ARtsPlayerController::LeftMousePress()
{
          // Removed irrelvent code
          // Check if building
          if(Cast<ABuilding>(PlacementBuffer))
          {
               ABuilding* BuildingBuffer = Cast<ABuilding>(PlacementBuffer);
               if(BuildingBuffer->bCanPlace)
               {
                    if(BuildingBuffer->GetBuildingType() == FName("Unit Building"))
                         PlayerPawn->Gold -= PlayerPawn->UnitPrice;
                    Server_ChangePlayerState(EPlayerState::Default);
                    UE_LOG(LogTemp, Warning, TEXT("Calling Change state in left press %s"), NETMODE_WORLD);
                    Server_SetBuildingState(BuildingBuffer, EBuildingState::Built);
               }
          }
          else
          {
               Server_ChangePlayerState(EPlayerState::Default);
          }
}


ABuilding:



ABuilding::ABuilding()
{
     // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
     PrimaryActorTick.bCanEverTick = true;
     Root = CreateDefaultSubobject<USceneComponent>("Root");
     SetRootComponent(Root);
     SetReplicates(true);

     // Get materials
     static ConstructorHelpers::FObjectFinder<UMaterial> GoldBuildingMaterial(TEXT("Material'/Game/StarterContent/Materials/M_Concrete_Tiles.M_Concrete_Tiles'"));
     static ConstructorHelpers::FObjectFinder<UMaterial> UnitBuildingMaterial(TEXT("Material'/Game/StarterContent/Materials/M_Brick_Clay_Beveled.M_Brick_Clay_Beveled'"));
    
     if (GoldBuildingMaterial.Succeeded() && UnitBuildingMaterial.Succeeded())
     {
          TempGold = GoldBuildingMaterial.Object;
          TempUnit = UnitBuildingMaterial.Object;
          UE_LOG(LogTemp, Warning, TEXT("Materials found in constructor 1"));
     }
}

void ABuilding::OnConstruction(const FTransform& Transform)
{
     Super::OnConstruction(Transform);
     // Create Dynamic Instances
     if (TempGold && TempUnit)
     {
          UE_LOG(LogTemp, Warning, TEXT("Materials good in OnConstruction 2"));
          UMaterialInstanceDynamic* GoldMat = UMaterialInstanceDynamic::Create(TempGold, this);
          UMaterialInstanceDynamic* UnitMat = UMaterialInstanceDynamic::Create(TempUnit, this);
          GoldMat->SetScalarParameterValue(FName("Transparency"), .5f);
          UnitMat->SetScalarParameterValue(FName("Transparency"), .5f);
          // Put this here for testing, didn't change anything
          GoldBuildingMat = GoldMat;
          UnitBuildingMat = UnitMat;
          // Pass material instances to be set like above but on server
          Server_SetBuildingMaterials(GoldMat, UnitMat);
     }
}

// Multicast Function
void ABuilding::Server_SetBuildingMaterials_Implementation(UMaterialInstanceDynamic* Gold, UMaterialInstanceDynamic* Unit)
{
     // Check Dynamic Instances
     if (Gold && Unit)
     {
          // Set Variables
          UE_LOG(LogTemp, Warning, TEXT("Materials good in SetBuildingMaterials 3 %s"), NETMODE_WORLD);
          GoldBuildingMat = Gold;
          UnitBuildingMat = Unit;
     }
}

// Multicast Function
void ABuilding::Server_SetBuildingState_Implementation(EBuildingState NewState)
{    
     // Check mats are good before using
     if(!GoldBuildingMat || !UnitBuildingMat)
     {
          if(!UnitBuildingMat)
          {
               UE_LOG(LogTemp, Warning, TEXT("Unit building mat is NULL %s"), NETMODE_WORLD);
          }
          if(!GoldBuildingMat)
          {
               UE_LOG(LogTemp, Warning, TEXT("Gold building mat is NULL %s"), NETMODE_WORLD);
          }
          return;
     }
    
     // Set State
     BuildingState = NewState;
    
     if(GetBuildingType() == FName("Unit Building"))
     {
          UE_LOG(LogTemp, Warning, TEXT("Change unit building state %s"), NETMODE_WORLD);
          switch(NewState)
          {
               case EBuildingState::Preview :
                    // Not doing this ATM
                    // UnitBuildingMat->SetScalarParameterValue(FName("Transparency"), 0.5f);
                    break;
               case EBuildingState::Built :
                    UnitBuildingMat->SetScalarParameterValue(FName("Transparency"), 1.f);
                    break;
               case EBuildingState::Destroyed :
                    break;
          }
     }
}


Unbelievable, I fixed it! Apparently, if you replicate your dynamic material variables then they don’t work with replication. You still have to set them on the server though I believe.

2024 and this is still going strong! Thanks for the help friend.