Iterating through all static meshes of an actor?

Hey, so I have an actor where I wish to override the colors of all its materials at certain events. (EG flashing a damaged entity red when hurt)
Currently I accomplish that like this:

	TArray<class UMaterialInterface*> materials = GetMesh()->GetMaterials();
	for (int i = 0; i < materials.Num(); i++)
	{
		UMaterialInstanceDynamic* matInstance = UMaterialInstanceDynamic::Create(materials[i], this);

		matInstance->SetVectorParameterValue("ColorEffect", StatusColor);
		GetMesh()->SetMaterial(i, matInstance);
	}

However, in addition to multiple materials, the actors can also have a variable amount of static meshes, and not necessarily just one.

How would you suggest that I also get and iterate through all the actors meshes so that I can apply the color universally, and not just to the base mesh?

1 Like

This is theoretical; it hasn’t been tested. If you must make modifications, please post them to help the community.


This should loop through all static mesh components:

TArray<UStaticMeshComponent*> MeshComponents;
GetComponents<UStaticMeshComponent>(MeshComponents);
for (UStaticMeshComponent* MyMesh : MeshComponents) {
    // Loops through the MeshComponents, with MyMesh as the component
}

Note: GetComponents is an AActor member function.

Hope this helps!

2 Likes

I recommend this approach. The above is fine, but doing this avoids any copying and creating a temporary array.

for (UActorComponent* CompItr : Actor->GetOwnedComponents())
{
	if (UStaticMeshComponent* Mesh = Cast<UStaticMeshComponent>(CompItr))
	{
		Mesh.DoStuff()
	}
}
3 Likes

Thanks for the suggestions! I was able to use them to figure out a way to use them to get all the meshes of and entitity and arrange all their materials into a single array so that I can modify them universally on the fly.

I ended up with this:

void AWinstickPawn::BeginPlay()
{
	Super::BeginPlay();

       // other stuff

	int MeshMatIndex = 0;
	int TempI = 0;
	for (UActorComponent* CompItr : this->GetComponents())
	{
		if (UStaticMeshComponent* Mesh = Cast<UStaticMeshComponent>(CompItr))
		{
			TArray<class UMaterialInterface*> materials = Mesh->GetMaterials();
			for (int i = 0; i < materials.Num(); i++)
			{
				DynaMats.Add(UMaterialInstanceDynamic::Create(materials[i], this));
				
				Mesh->SetMaterial(i, DynaMats[i + MeshMatIndex]);
				TempI = i; //get final material index of this mesh to add to total materials for entity
			}
			MeshMatIndex = MeshMatIndex + TempI + 1; //add one to account for arrays starting at index 0 when counting materials
		}
	}



}

void AWinstickPawn::DoColor(FColor color, float DeltaSeconds)
{
	FTimerHandle Timer;
	for (int i = 0; i < DynaMats.Num(); i++)
	{
		DynaMats[i]->SetVectorParameterValue("ColorEffect", color);
	}
	if (DeltaSeconds != 0)
	GetWorldTimerManager().SetTimer(Timer, this, &AWinstickPawn::UndoColor, DeltaSeconds, false);
}

void AWinstickPawn::UndoColor()
{
	DoColor(FColor(0,0,0,0), 0.0f);
}

Cool! One thing though… You’re creating a new material for every mesh. Why can’t you just create one DynamicMaterialInstance, and then assign that to every mesh?

Because my meshes use multiple different materials with different textures, etc. But I want them all to be affected the same from some universal things.

Okay. I should have noticed that from the code :stuck_out_tongue_closed_eyes:

Just one thing to flag - this code is making a local copy of that materials array. You should use const ref here!

Just one thing to flag - this code is making a local copy of that materials array. You should use const ref here!

Ah, right! Thanks! I’ve been learning from doing a lot and keep forgetting about these kinds of things!