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;
}
}
}