How do I set an actor's static mesh in the editor, but change the material in code?

I’m making a game of checkers. I would like to have a c++ class that is the base for the pieces, and then create two blueprint derivatives of this base class for the two players’ pieces, since they have different colors and move in different directions. My goal is to have the functions and data common for all the pieces defined in code, but the staticmesh, materials, and possible relative moves for the two different players defined in the blueprint derivatives in the editor.

My problem is in trying to write a function to change the material of a given piece in the base c++ class. I have a Blueprint Visible UStaticMeshComponent and UMaterial so that I can select them in the editor:

(In Piece.h)

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GamePiece")
	UStaticMesh* NormalMesh;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GamePiece")
	UMaterial* NormalMaterial;

In the constructor of the base class, I set this mesh to the RootComponent, like so:

(In Piece.cpp)

UStaticMeshComponent* PieceMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("RootComponent"));
PieceMeshComponent->SetStaticMesh(NormalMesh);
PieceMeshComponent->SetMaterial(0, NormalMaterial);
RootComponent = PieceMeshComponent;

My problem, is that I want to have a function that I can call to change the material to show that the piece has been selected, like this:

(In Piece.cpp)
void APiece::ToggleSelected()
{	
	if (!Highlighted)
	{
		this->GetRootComponent()->SetMaterial(0, HighlightMaterial);
		this->GetRootComponent()->SetMaterial(0, HighlightMaterial);
	}
	else
	{
		this->GetRootComponent()->SetMaterial(0, NormalMaterial);
	}
}

VStudio tells me that the RootComponent is a USceneComponent, and so I can’t call SetMaterial on it.

How do I create an actor base class with an editor-definable static mesh, while still being able to change the material in code? I tried to create a separate variable to hold a UStaticMeshComponent, and then change the material on that and re-set the RootComponent every time. Like this:

(In Piece.h)

	UStaticMeshComponent* RootMeshComp;

(In Piece.cpp)
APiece::APiece()
{

	RootMeshComp->SetStaticMesh(NormalMesh);
	RootMeshComp->SetMaterial(0, NormalMaterial);
	RootComponent = RootMeshComp;

	PrimaryActorTick.bCanEverTick = false;
	isKing = false;
	Highlighted = false;
}

void APiece::ToggleSelected()
{	
	if (!Highlighted)
	{
            RootMeshComp->SetMaterial(0, HighlightMaterial);
	}
	else
	{
		RootMeshComp->SetMaterial(0, NormalMaterial);
	}
    RootComponent = RootMeshComp;
}

I planned to connect this ToggleSelected() to click events in the editor, but when I compiled with this addition, the whole editor crashed, and now it crashes every time I try to re-open the project!!

I am very stumped and I feel like I’m missing something, changing the material of an actor on a click doesn’t seem like it’s supposed to be this complicated! Any help would be appreciated very very much!!

Also, if anyone can help me recover a project that crashed while trying to open, after crashing during a compile, that would be great!

Hey,

not knowing much of that topic, i like your first approach. I believe you’ll just have to cast your root component to a UStaticMeshComponent.

if (Cast<UStaticMeshComponent>(GetRootComponent()))
        Cast<UStaticMeshComponent>(GetRootComponent())->SetMaterial(0, HighlightMaterial);

Sadly i got no idea on the Editor crashes.
The Log, or the Visual Studio Output might help.

Hmmmm. Very curious. I believe your problem is this:

Rootcomponent is by default a USceneComponent, hence it won’t work (even though you’ve defined it as a static mesh, the editor still thinks it’s getting a USceneComponent, and they don’t have the SetMaterial function). Rootcomponent is there to store your actor’s transform, not to function as a StaticMeshComponent.

Easily solved by:
Make a separate rootcomponent as a USceneComponent and attach your static mesh to it and your code will work.

RootComponent needs to be set in the constructor, resetting it at runtime might result in that crash (I doubt it though, but it shouldn’t work).

Thanks, I didn’t realize RootComponent can only be set in the constructor!

I do have a couple of basic followups - if I set a StaticMeshComponent as just another component, but not the root, will it:

  • be placed at this parent actor’s origin?
  • translate and rotate along with the parent actor?
  • handle collisions properly, since the collision is already on this mesh?

I read in one of the C++ tutorials that RootComponents are for collision, but I’m guessing I can set the RootComponent to have no collision and the StaticMeshComponent to have the collision? I would check myself but I’m still working on recovering the crashed project :frowning:

Thank you!

Yes to all. However, you need to use the following line: YourStaticMeshName->AttachTo(RootComponent);

You can set other properties as well in the constructor (or in the code), like toggle collision on/off, make a custom collision profile, put some offset to the transform from the rootcomponent etc. if you so wish. But when you attach, the mesh rotates and translates with the actor.