Can't change the material of a static mesh at runtime, in a multiplayer game


I have a problem with changing the material of a static mesh at runtime, in a multiplayer game. The situation is as follows:

In my [FONT=Courier New]GameMode class, I have a [FONT=Courier New]TArray of [FONT=Courier New]TeamStarts, that are basically [FONT=Courier New]PlayerStarts but represented as a capturable area, with a flag (like a capturable flag on a Battlefield Conquest map).

In the constructor, I create the mesh, load and store the materials that will represent the 3 state of the flag (one material for the first, one for the second team and one for the neutral state), and set everything to default (so every flag is neutral at first).

Because we can place a [FONT=Courier New]TeamStart on the map and edit who controlls it, in the [FONT=Courier New]BeginPlay, I re-set the material of the flag, based on who controlls it, then add this flag to the [FONT=Courier New]TeamStarts [FONT=Courier New]TArray in the [FONT=Courier New]GameMode.

In my function, that handles the capture of a flag, I set the material of the flag with the [FONT=Courier New]SetMaterial(…) function, if it’s state has changed (i.e. someone neutralized or captured it).

When I hit play, everything is correct, because every flag has the right material set, but when I neutralize or capture a flag it remains the same as it was on start.

To solve the problem, I tried all sorts of things like:

Setting the meshes and the materials as [FONT=Courier New]Replicated in the [FONT=Courier New]TeamStart class

Creating a variable (tried with [FONT=Courier New]UMaterialInterface and with an [FONT=Courier New]FLinearColor for the [FONT=Courier New]UMaterialInstanceDynamic) with a [FONT=Courier New]ReplicatedUsing specifier that I changed from the [FONT=Courier New]GameMode, and in the [FONT=Courier New]OnRep_ function, I set the mesh’s material to this variable’s material (or when I used an [FONT=Courier New]UMaterialInstanceDynamic, I set the dynamic material to the set [FONT=Courier New]FLinearColor)

Creating a [FONT=Courier New]UMaterialInstanceDynamic from a material and just change the base white color to somehing different (for example to red) on state change with [FONT=Courier New]SetVectorParameterValue(…)

Handling the setting of the mesh material in Blueprint in a way, that is really similar to the Networking Content Example’s traffic light

but nothing seems to work.

I’m not sure, if the problem is the replication (although I launched the game with one player as the server, but the material hasn’t changed there either) or the fact that changing a mesh’s material at runtime is not possible or not possible in every way (although I tried the [FONT=Courier New]UMaterialInstanceDynamic method too, that I read here on the forum, is the right way of changing a mesh’s material’s parameters at runtime).

Is the mesh part of the game mode?
A game mode is a server side only class - it does no replicate.
If you haven’t already, check out Exi’s Free Network Compendium.

Yes, I know that the [FONT=Courier New]GameMode is server side only, but I put the [FONT=Courier New]TArray of [FONT=Courier New]TeamStarts there, because it is relevant to the [FONT=Courier New]GameMode you play, and the players shouldn’t have access to them. I have made this really similarly to the ShooterGame’s [FONT=Courier New]ShooterPickups, where there is a [FONT=Courier New]TArray of [FONT=Courier New]ShooterPickups in the [FONT=Courier New]GameMode and when a [FONT=Courier New]ShooterPickup is created, it adds itself to this [FONT=Courier New]TArray.

And as I said, it works perfectly fine, because on start, every player can see these [FONT=Courier New]TeamStarts on the map with the right mesh material (because I set it on [FONT=Courier New]BeginPlay(), based on who controlls the flag at start) and can capture/neutralize them, but the material of the mesh (the flag mesh for the [FONT=Courier New]TeamStart is in the [FONT=Courier New]TeamStart class) won’t change. One thing I thought of, that I could put the [FONT=Courier New]TArray of [FONT=Courier New]TeamStarts to the [FONT=Courier New]GameState class, so it would replicate to the clients, but then every player would ha access to these, that I don’t really want to.

So as I said in my first comment: I’m not sure if the cause of the problem is the replication, or the ways I try to change the material of the mesh at runtime. Because if the replication would be the problem, then the setting of the material of the mesh in [FONT=Courier New]BeginPlay() shouldn’t appear on client side (but it appears perfectly fine), and if the setting of the material would be the problem, then why can I set it in the [FONT=Courier New]BeginPlay(), but not anywhere else.
Or is that possible, if I have these [FONT=Courier New]TeamStarts in the [FONT=Courier New]GameMode (and they are basically part of the map/level I created, because I have to place them on the map), and changing these would appear automatically on clients (because these are part of the map the players play on).

As far as I am aware, player starts are also not replicated.
You could replicate the status of each via the game state, then have it change their mesh when received.
Or you could set each player start to be replicated and it can handle its own status.
Up to you.

I have managed to fix the problem yesterday, that was indeed the replication. At first, I have made a pointer to the [FONT=Courier New]GameMode’s [FONT=Courier New]TeamStarts [FONT=Courier New]TArray in the [FONT=Courier New]GameState and set up an [FONT=Courier New]UMaterialInterface variable in the [FONT=Courier New]TeamStart, that replicates with an [FONT=Courier New]OnRep_ function, where I set the material of the flag to the one stored in this variable (of course I change this variable whenever someone neutralize/capture the flag, so this triggers the replication).

It worked perfectly fine, but I didn’t like the fact that I had to store every [FONT=Courier New]TeamStarts in the [FONT=Courier New]GameState, and anyone could potentially reach it (of course I made it protected, and haven’t exposed them to Blueprints, but from the [FONT=Courier New]GameState code, anyone could reach it; and I couldn’t make it to a [FONT=Courier New]const [FONT=Courier New]TArray, because the [FONT=Courier New]UPROPERTY specifier doesn’t support [FONT=Courier New]const variables). So an hour ago, I realized, that I have a [FONT=Courier New]TeamStartState [FONT=Courier New]struct, where I store useful informations about the [FONT=Courier New]TeamStart like who controlls it or if someone is capturing it etc., so I made an [FONT=Courier New]UMaterialInterface variable here too, that I set every time I change the material of the flag in the [FONT=Courier New]TeamStart. So instead of having a [FONT=Courier New]TArray of [FONT=Courier New]TeamStarts in the [FONT=Courier New]GameState, I have a [FONT=Courier New]TArray of [FONT=Courier New]TeamStartStates, updated with the material of the flag, so every player can see who controlls the flag (and I don’t store data in the [FONT=Courier New]GameState, that is irrelevant to the players). :wink: