Is this possible to change, static mesh components created in construction script, during runtime?

Hi,

I have blueprint with 20x20 static meshes created in construction script. I add all the meshes to the array in order to interact with them in runtime. The problem is when I try to, for example, change material of any mesh in array during runtime. Nothing happens so it does not seem to work. Array elements seems to non-modificable. Do you have idea for workaround?

Here is my BP creating 20x20 meshes array in contruction script:

Here is level blueprint where I try to modify array element (change its material):

Try to make instanced material first out of your material.

I am not sure, but maybe level blueprint does not get player input, so P key event never happens in level bp.
To test this add Print node after set material to see if that event happens in level bp

Easiest way (for learning, and checking it) is to do this material change on event tick.
Then add delay of some seconds.
You are playing with arrays, so create array of material references.
Create some integer variable, loop it trough all material array indexes.
And feed elements of array to set material pin.

You can however use dispatchers to pass events to level blueprint from player controller.
Create dispatcher in player controller. Call its function when P is pressed.
Then in level blueprint get player controller (do this on begin play, in construction script sometimes other blueprints are missing (they not constructed yet), but BeginPlay has everything in level already)
From that cast to BP_PlayerController (your custome player controller blueprint)
Then drag blue pin ad write assign (find BP_playerController again)
And you will have event that happens when you press P

I am afraid thats not the problem with event itself. It gets called properly. I think its problem with array elements. It seems that none part of them cannot be modified after being created in construction script. I dont know how to properly use instanced materials to change material during runtime (after pressing).

That “P” event graph in level blueprint generates “accessed none” errors for getting values out of array.
For some reason level BP cannot read that array (or gets wrong values, its length was 48 when i read from level, and 16 when i read from inside blueprint).

Solution for all that is to create function inside squares blueprint that sets material and call that function instead of reading reference array.

Function in Squares BP:

Calling it from level BP:

They definitely can be modified after being created in the construction script, Nawrot’s way is how I would do it as well, but it does seem odd that the level blueprint can’t parse the array properly though.

Level blueprint is getting wrong references, that shows in log. That may be “Feature” or bug. But doing functions and calling them is more elegant way, so i really never manipulated (after first 2 learning weeks) variables inside another blueprint, its messy.

Great idea! Thank you so much. Works like a charm.

One thing that i just remembered (for folks that may search this in future):

If you have LODed mesh then element index in set material node gives acess to higher lod meshes.

I have one more question for those who write code in C++. I have rewritten this BP to C++ but I think I am missing some basic Actor functionalities because it does not work in editor (nothing visible after dragging onto level).

.h


#pragma once

#include "GameFramework/Actor.h"
#include "SomeGame.generated.h"

UCLASS()
class SOMEGAME_API AInteractableSquares : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AInteractableSquares();

	/** Root component */
	UPROPERTY()
		USceneComponent* Root;

	/** Static mesh to create */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Some Actor")
		UStaticMesh* SomeMesh;

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	virtual void OnConstruction(const FTransform& Transform) override;

	void setSquareMaterial(int Index, FMaterial& Material);	

	/** Storage for all mesh components */
	UPROPERTY(Transient)
		TArray<UStaticMeshComponent *> Squares;
};


.cpp



#include "SomeGame.h"
#include "InteractableSquares.h"
#include "MaterialShared.h"

// Sets default values
AInteractableSquares::AInteractableSquares()
{
 	// 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;

	RootComponent = Root;

	Squares = TArray<UStaticMeshComponent *>();
}

// Called when the game starts or when spawned
void AInteractableSquares::BeginPlay()
{
	Super::BeginPlay();
}

// Called every frame
void AInteractableSquares::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

}

void AInteractableSquares::OnConstruction(const FTransform& Transform)
{	
	Super::OnConstruction(Transform);
	Squares.Empty();

	SomeMesh = LoadObject<UStaticMesh>(NULL, TEXT("/Game/Geometry/Meshes/Square"), NULL, LOAD_None, NULL);

	FVector Translation = FVector(0.0f, 0.0f, 0.0f);
	FRotator Rotation = FRotator(0.0f, 0.0f, 0.0f);
	FVector Scale = FVector(1.0f, 1.0f, 1.0f);

	FTransform meshTransform;

	//Adding meshes to 20x20 array
	for (int i = 0; i < 20; i++) {
		for (int j = 0; j < 20; j++) {
			Translation.X += i * 205;
			Translation.Y += j * 205;
			meshTransform = FTransform(Rotation, Translation, Scale);

			UStaticMeshComponent* mesh = NewObject<UStaticMeshComponent>(this);
			mesh->AttachTo(RootComponent);
			mesh->RegisterComponent();

			// now you can do whatever setup you need to do
			mesh->SetStaticMesh(SomeMesh);
			mesh->SetRelativeTransform(meshTransform);

			// keep our own reference to the mesh
			Squares.Add(mesh);
		}
	}
}

void AInteractableSquares::setSquareMaterial(int Index, FMaterial& Material)
{

}


PS. I know C++ related posts should be in different thread but I dont want to mess with another post regarding the same topic.