Having trouble getting instanced objects in viewport to execute blueprint code on updates.

I’m trying to get instanced objects in the editor viewport to execute Blueprint code when I modify their details. Specifically, I’m changing a Dynamic Material Instance at runtime, and would like the changes to appear in the viewport.

I have PostEditChangeProperty call a Dynamic delegate that the object blueprint is hooked up to. But the blueprint code doesn’t appear to execute.

I’m seeing the C++ logs, but not the bp printed string. Any idea why?

Header:

#if WITH_EDITOR
	virtual void PostEditChangeProperty(struct FPropertyChangedEvent& InChangeEvent) override;
#endif // WITH_EDITOR

	UPROPERTY(BlueprintAssignable)
	FOnEditorUpdateDelegate OnEditorUpdateDelegate;

Implementation:

void UTP_WeaponComponent::PostEditChangeProperty(FPropertyChangedEvent& InChangeEvent)
{
	UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__));

	OnEditorUpdateDelegate.Broadcast();

	Super::PostEditChangeProperty(InChangeEvent);
}

Hi, I wanted to help reach out.

I have a few questions for you on your setup to try and fix it or give you the desired results.

  • Is your delegate being bound to anything? From what I can see you have the BP function but I don’t see a binded event for it in your C++ code or BP. Though also considering you are trying to call a delegate before you begin play, I’m not sure how it would properly bind to any function.

  • Are familiar with the Construct node in BP? You are using a BP function to change the object on an edit of a property and the Construct function can give you that same functionality.

Tell me how this works out for you. I think it will be the solution you need.

1 Like

Hi, thanks taking the time!

I’ve experimented a bit with it and am getting the same results. I think this has to do with the call order for changes in the editor.

I added both a direct call to Set Gun to Projectile Color, and a binding in the construction script. It seems the direct function call in the construction script will be called if I add an instance, or move it within the viewport:

LogBlueprintUserMessages: [BP_PickUp_Rifle_C_4] Construction Script!!!
LogBlueprintUserMessages: [BP_PickUp_Rifle_C_4] SetGunToProjecileColor Executing!!!!!!!!

But notably the callback doesn’t get executed.

I can execute the PostEditChange function by manipulating the Details of an instance directly, which I only observe via logging:

LogTemp: Warning: UTP_WeaponComponent::PostEditChangeProperty

This should be executing OnEditorUpdateDelegate.Broadcast(); But I don’t see that actually executing.

Unfortunately it appears the constuction script executes before the editor applies the custom details, so it’s not actually changing the color. (I verified that the color change code works when I run the game).

Here’s the construction script:

I’m going to play around and see if it works by broadcasting object lifecycle callbacks: Unreal Engine Actor Lifecycle | Unreal Engine 5.5 Documentation | Epic Developer Community

Edit: Spoke too soon. I can now see the edits when in the color picker of the property, but when I click out, the object instance reverts to its initial state, even though the color property is different.

Okay I got it to work by moving the SetGunToProjectileColor function into C++, and calling it directly in the overridenPostEditChangeProperty method.

I’d be curious why the event callback wasn’t working, if anyone happens to know why.

I was able to get it working by moving all the code into C++ and overloading these three UObject methods:

#if WITH_EDITOR
	virtual void PostEditChangeProperty(struct FPropertyChangedEvent& InChangeEvent) override;
	virtual void PostCDOCompiled(const FPostCDOCompiledContext& Context) override;
	virtual void PostTransacted(const FTransactionObjectEvent& TransactionEvent) override;
#endif // WITH_EDITOR

And in the body of each one, I would perform my update before calling super:

	UpdateWeaponColor();
	Super::PostEditChangeProperty(InChangeEvent);

Also, FWIW, in my UpdateWeaponColor will always create the DynamicMaterialInstance if it’s not present, so it looks like this:

void UTP_WeaponComponent::UpdateWeaponColor()
{
	auto dynamicMaterial = GetDynamicMaterialInstance();
	DJ_RETURN_IF(dynamicMaterial == nullptr);

	dynamicMaterial->SetVectorParameterValue(FName("BodyColor"), WeaponColor);
}

UMaterialInstanceDynamic* UTP_WeaponComponent::GetDynamicMaterialInstance()
{
	if (__dynamicMaterialInstance)
	{
		return __dynamicMaterialInstance;
	}

	if (__dynamicMaterialInstance = CreateDynamicMaterialInstance(0))
	{
		DJLog(Warning, "Generated DynamicMaterialInstance");
	}
	else {
		DJLog(Warning, "Failed to generate DynamicMaterialInstance");
	}
	return __dynamicMaterialInstance;
}