How to update my Pawn when Property changes in the editor

Hello!

I have created a class


BlobPawn

. In theory, it will be visually represnted by some sphere-like mesh, but I want to allow the designer to change the mesh of the Pawn, so I have added this property in my code:



// Mesh to use for Blob
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Blob|Visuals")
UStaticMesh* BlobMesh;


I have this in my constructor:



UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
SphereVisual->SetupAttachment(RootComponent);
SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -BlobRadius));


Of course the component will not get represented by that mesh, because by default it is


null

. So how can I react to property change?

I found out that I can use this event:



virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e);


But it turns out to be a complete mistery from here… Here is what I’ve tried:



void ABlobPawn::PostEditChangeProperty(FPropertyChangedEvent & e) {
    FName PropertyName = (e.Property) ? e.Property->GetFName() : NAME_None;
    TArray<UActorComponent*> MyComponents;
    GetComponents(MyComponents);


    if (PropertyName == GET_MEMBER_NAME_CHECKED(ABlobPawn, BlobMesh)) {
        FMultiComponentReregisterContext ReregisterContext(MyComponents);


        for (UActorComponent* Comp : MyComponents) {
            if (UStaticMesh* MeshComp = Cast<UStaticMesh>(Comp)) {
                SphereVisual->SetStaticMesh(MeshComp); // I don't know how to get the SphereVisual socket from RootComponent
            }
        }
    }


    Super::PostEditChangeProperty(e);
}


Help me out with this, please!

Have you assigned the return value of “CreateDefaultSubobject” to the “BlobMesh” variable?
You should not have to do anything for the mesh to be reflected as you just change the values of “BlobMesh”.

HTH

Should my [FONT=Lucida Console]BlobMesh be [FONT=Lucida Console]UStaticMeshComponent or [FONT=Lucida Console]UStaticMesh then?

This is the code for my constructor and no, it does not work just by itself apparently:



ABlobPawn::ABlobPawn()
{
	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
	RootComponent = SphereComponent;
	SphereComponent->InitSphereRadius(BlobMeshRadius);
	SphereComponent->SetCollisionProfileName(TEXT("BlobCollision"));
	
	BlobMesh = CreateDefaultSubobject<UStaticMesh>(TEXT("BlobMesh"));

	UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
	SphereVisual->SetupAttachment(RootComponent);
	SphereVisual->SetRelativeScale3D(FVector(1.0f));
	SphereVisual->SetStaticMesh(BlobMesh);

	USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
	SpringArm->SetupAttachment(RootComponent);
	SpringArm->RelativeRotation = FRotator(-CameraPitch, 0, 0.f);
	SpringArm->TargetArmLength = 300.0f;
	SpringArm->bEnableCameraLag = true;
	SpringArm->CameraLagSpeed = 2.0f;

	UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("ActualCamera"));
	Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

	AutoPossessPlayer = EAutoReceiveInput::Player0;
}


I am using the [UStaticMeshComponent] type.

You should be able to change the blob mesh’s mesh in the Editors detail panel (when working on the level/map) by selecting the blob mesh component and finding the mesh section.
You may want to attach the blob mesh to the root component (called RootComponent).

What you have should work, You just arn’t setting a default mesh for it.

I offer a suggestion: Create the base class in C++(when/where applicable) and then use Blueprint to wire it up and set defaults along with simple/basic logic.

HTH

You can use the OnConstruction(Transform) method to update the static mesh component on property change

.h
virtual void OnConstruction(const FTransform & Transform) override;

ABlobPawn::OnConstruction(const FTransform & Transform){
if(BlobMesh) SphereVisual->SetStaticMesh(BlobMesh);
}

Remove this: BlobMesh = CreateDefaultSubobject<UStaticMesh>(TEXT(“BlobMesh”));
SphereVisual will be the Component, can be a private/protected variable with UPROPERTY()
BlobMesh the mesh that is exposed to blueprints

Also remove the SphereVisual->SetStaticMesh(BlobMesh) in your constructor or at least test for BlobMesh being null first

ABlobPawn::ABlobPawn()
{
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don’t need it.
PrimaryActorTick.bCanEverTick = true;

//SphereComponent should be private UPROPERTY
USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT(“RootComponent”));
RootComponent = SphereComponent;
SphereComponent->InitSphereRadius(BlobMeshRadius);
SphereComponent->SetCollisionProfileName(TEXT(“BlobCollision”));

BlobMesh = CreateDefaultSubobject&lt;UStaticMesh&gt;(TEXT("BlobMesh"));//remove

   //SphereVisual should be private UPROPERTY
UStaticMeshComponent* SphereVisual = CreateDefaultSubobject&lt;UStaticMeshComponent&gt;(TEXT("VisualRepresentation"));
SphereVisual-&gt;SetupAttachment(RootComponent);
SphereVisual-&gt;SetRelativeScale3D(FVector(1.0f));
SphereVisual-&gt;SetStaticMesh(BlobMesh);//test null

//SpringArm should be private UPROPERTY
USpringArmComponent* SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT(“SpringArm”));
SpringArm->SetupAttachment(RootComponent);
SpringArm->RelativeRotation = FRotator(-CameraPitch, 0, 0.f);
SpringArm->TargetArmLength = 300.0f;
SpringArm->bEnableCameraLag = true;
SpringArm->CameraLagSpeed = 2.0f;

//Camera should be private UPROPERTY
UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT(“ActualCamera”));
Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

AutoPossessPlayer = EAutoReceiveInput::Player0;

}

ABlobPawn::OnConstruction(const FTransform & Transform){
if(BlobMesh) SphereVisual->SetStaticMesh(BlobMesh);
}