Cannot figure out what is wrong with this code to change the material of a basic mesh in C++

Greetings
In my assessment that I am going through to determine if I am going to use UE4, I have a problem trying to get the basic default UE4 cube mesh to change its default material in using C++.

There is no tutorial, no guide no nothing on how to do this except some suggestions on which I have found in this forum of which the below code is the one that looks like it is the best solution, but it crashes the project. Then I have to rebuild or as I did several times copy a backup to recover the project. This code sits in a .cpp file in the class constructor below where the cube mesh is defined as in a previous post.



		       UMaterial                *the_box_Material;
		       UMaterialInstanceDynamic *box_Material_dyn;

	            static ConstructorHelpers::FObjectFinder<UMaterial> Material(_T("Material'/Game//StarterContent/Materials/M_Metal_Steel.M_Metal_Steel'"));
		   if (Material.Succeeded()) {
	                 the_box_Material = (UMaterial*)Material.Object;
	          if (the_box_Material == NULL)
				 GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("the_box_Material == NULL "));
  			else {
				 box_Material_dyn = UMaterialInstanceDynamic::Create(the_box_Material, this);
				 if (box_Material_dyn == NULL)
					 GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("box_Material_dyn == NULL"));
				else
					//BoxVisual->SetMaterial(0, the_box_Material);//** is it this**

                                        BoxVisual->SetMaterial(0, box_Material_dyn); // **or as I believe this that should be used **
			}


Is this code correct because I have seen variations on this that are claimed to work and I have spent the best part of a day trying out several different things and angles with no better success ? If I cannot get something as basic and what should be trivial as this to work I will have to consider not using UE4. Especially if there are going to be other unexpected incidences or prospect of loosing hours or days of work because it unexpectedly crashes.

Thanks in advance for any help or guidance here.

Regards

DominioN

Where does it crash the project? Have you stepped through the code to see exactly where the problem is occurring?

Generally you would be better using a UPROPERTY and assigning the asset in blueprints, that is the ‘preferred’ workflow, rather than sticking magic strings in your C++.

I assume BoxVisual != nullptr? (looking at the way you have written that code, I would think you have checked that).

This is something I am playing with myself, and I use CreateDynamicMaterialInstance():



static ConstructorHelpers::FObjectFinder<UMaterial> Material(TEXT("/Game/StarterContent/Materials/M_Metal_Steel"));
if (Material.Succeeded()) {
    // Save the return value as an instance variable if you want to set material params later...
    BoxVisual->CreateDynamicMaterialInstance(0, Material.Object);
}


This works some of the time however more often than not the material does not appear to be applied to the mesh. I have this working all of the time in a Blueprint, so I have come to the conclusion that I must use a C++ base class to provide the functionality I want (I want to set lots of different material parameters to different values depending on the value of a enum - something that’s impractical in a BP). However I will be using the BP subclass to set-up the mesh and create and save the Material Instance Dynamic (MID), as that works. It will be more flexible that way anyway.

I’m pretty sure the problem is UMaterialInstanceDynamic::Create or something called indirectly from there. I’ve tried to use this function in a constructor before as well and the project would no longer load anymore and crash at startup. Moving the MaterialInstanceDynamic (MID) creation to my Actor’s BeginPlay function worked for me. Not really sure what the “preferred” function/place for creating them in code is. Usually when you just need to create them and update their parameters once you can do it “on demand” whenever you need it but what if you want to constantly update them? Creating the MID and initializing the parameters to the desired/resonable defaults in the constructor seems like the ideal place.

Other than that using the UPROPERTY approach only really works if you can actually use a Material or MaterialInstanceConstant but if you want to change the parameters at runtime you “have” to use UMaterialInstanceDynamic (see workaround here: Set Material of Landscape). You can still use the UPROPERTY as the parent for the MID though. It’s a bit limiting if UMaterialInstanceDynamic can indeed not be created from UCLASS constructors (or did I miss something?).

I believe it’s fine from a constructor as I mocked-up what I wanted in a BP and had the MID created and assigned to the mesh in the BP’s Construction Script, which I assume is a 1-to-1 mapping to a C++ constructor, or near enough not to matter in an Actor’s lifecycle.

Welcome to Unreal :slight_smile: A few pointers, if you’ll pardon the pun…

The first issue you have is that you can’t actually set the material on the mesh in the constructor, unless you can garauntee that the mesh asset is already set and loaded on the mesh component. If you make a Blueprint of your class (which you most likely will, since there’s rarely any real reason to use FObjectFinder to hardcode values) - then the mesh asset will not be loaded in the constructor, it actually gets done later. You can’t create Dynamic Material Instances in the constructor either, though the reason escapes me right now.

The best place to do these kind of setup operations is in PostInitializeComponents(), which is a virtual function as part of every UObject, and is called once the object is fully constructed, and has all of it’s default properties set.

The easiest way to achieve what you want to do then, is the following. This assumes the correct material is already set on the cube (material index 0)



void AMyActor::PostInitializeComponents()
{
	Super::PostInitializeComponents();

	if (MeshComponent && MeshComponent->StaticMesh)
	{
		UMaterialInstanceDynamic* MyDMI = MeshComponent->CreateAndSetMaterialInstanceDynamic(0);
	}
}


Obviously, if you want to modify the DMI outside of that function, you’ll need to store it as a member variable in the header. It doesn’t neccesarily need to be a UPROPERTY(), since your mesh component is likely a UPROPERTY() already and the material won’t therefore be Garbage Collected. But, if you want to edit the material params in Blueprint, you’ll need it anyway.



UPROPERTY(BlueprintReadOnly, Category = "Material")
UMaterialInstanceDynamic* MyDMI;


Finally, although this is nit-picky - I really struggled to read your code snippet above. I would strongly recommend reading and sticking to Epic’s coding standards, which would make that snippet a lot easier to read and most folks around here will be more inclined to help: Coding Standard | Unreal Engine Documentation


The C++ counterpart for Blueprints’ ‘Construction Script’ is actually ‘OnConstruction()’, which is virtual and overrideable. Make sure you call Super() on any engine functions you override, and always make sure you call Super on the Constructor too.

Thanks Guys for the help. I have tried all the suggestions, of which moving the code was one I was going to give it a go, but within the constructor itself. It compiles and runs without crashing in most of my gos, but I haven’t got the material to change. And it seems UE4.12 handles the project crashing well. I have not had to recover the project from backup. And since No one has indicated that the code I supplied is wrong. I’ll have to assume it is correct, and I just have to find out what goes where to get it to work. Sorry about the formatting, the editor messed up the tabs in the copy and paste and changed things as I was creating the post.

My understanding and experience of UE is just at the very start and so I am trying simple tasks which is all I need to do anyway right now. My line of thinking of how things work will have to change it seems as well. When starting out I thought loading a material or texture in c++ would simply be.

1 : load material/texture file structure into memory and have a pointer point to it.
2 : run a function to overwrite whatever material/texture currently exists that the mesh has allocated to it.

or

run a function for a particular object that one is changing the material/texture on that has the path name to the material or texture file as its parameter.

Alas there seems to be a much more complicated method of design philosophy and execution than I anticipated. And then there is the possibility of legacy issues that each iteration having to deal with old code that can cause problems in simplifying or adding functionality when writing code.

If this is a sign of things to come I may have dived into a pool that is too deep for me to swim in.
Oh well. Back to trying to find out how to get this to work.

Regards

DominioN

Hope you stick with it DominioN, I didn’t know any languages 2/3 years ago and I’m very confident in C++ in Unreal now (only Unreal though, don’t know diddly-squat outside of it).

As a newcomer to it, I would recommend the following things before diving in too deep:

  1. Follow the Battery Tutorial
  2. Check out ‘ShooterGame’, ‘VehicleGame’ and ‘StrategyGame’ examples on the Learn tab. ShooterGame is the best one IMO. All of these are complete C++ example projects, and you can often use them as reference.