Hello everyone!
As the title says, I’m currently struggeling with PostEditChangeProperty. Below is a small class derived from AActor that shows the problem.
I’m using 4.9.1 and when I edit the properties under the Test Category for an instance of (A)MyActor placed in the level via the details panel by clicking and dragging left or right or up or down it works as expected. However when I do the same in a blueprint that subclasses AMyActor the last call to PostEditChangedProperty will pass me nullpointers for both PropertyChangedEvent::Property and PropertyChangedEvent::MemberProperty.
Am I doing something wrong or is this intended?
Digging a little deeper I can also see that PostEditChangeProperty is called multiple times even if I enter a specific value with the keyboard and confirm it by pressing enter in the Blueprint details panel (not for an instance in the level). I haven’t tested whether the value is updated correctly, or at what point between the calls the update happens. If it’s already covered by the first call then I guess the first problem is not really an issue, just that my code reacting to changes in properties runs multiple times even though it wouldn’t really be necessary (i.e. for no reason?).
My final observation is that when recompiling the Blueprint PostEditChangeProperty is only called once, with nullpoints for both PropertyChangedEvent::Property and PropertyChangedEvent::MemberProperty which could potentially be problematic again, although when recompiling the value won’t change at the same time.
Placing an instance of the Blueprint in the level then works as expected again when editing the properties of that instance.
All ideas and discussion are very much appreciated. Thank you.
.h
#pragma once
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
uint64 GetKey(uint64 Key, uint64 Offset);
void PrintPropertyDetails(UProperty* Property, FString Name, uint64 Key);
#endif
UPROPERTY(Category = "Test", EditAnywhere)
FRotator Rotation;
UPROPERTY(Category = "Test", EditAnywhere)
FQuat Quaternion;
UPROPERTY(Category = "Test", EditAnywhere)
FVector4 Vector4;
UPROPERTY(Category = "Test", EditAnywhere)
float Value;
};
.cpp
#include "MyProject.h"
#include "MyActor.h"
AMyActor::AMyActor()
{
}
#if WITH_EDITOR
uint64 AMyActor::GetKey(uint64 Key, uint64 Offset)
{
//replace with "return -1;" if you want to see the history of on screen debug messages
return Key + Offset;
}
void AMyActor::PrintPropertyDetails(UProperty* Property, FString Name, uint64 Key)
{
if (GEngine)
{
if (Property)
{
FString PropertyCppName = Property->GetNameCPP();
FString PropertyName = Property->GetName();
GEngine->AddOnScreenDebugMessage(GetKey(Key, 0), 5.0f, FColor::Black, FString::Printf(TEXT("AMyActor::PrintPropertyDetails called for %s with cpp name %s and name %s"), *Name, *PropertyCppName, *PropertyName));
UProperty* OwnerProperty = Property->GetOwnerProperty();
if (OwnerProperty)
{
FString OwnerPropertyCppName = OwnerProperty->GetNameCPP();
FString OwnerPropertyName = OwnerProperty->GetName();
GEngine->AddOnScreenDebugMessage(GetKey(Key, 1), 5.0f, FColor::Black, FString::Printf(TEXT("AMyActor::PrintPropertyDetails owner property with cpp name %s and name %s"), *OwnerPropertyCppName, *OwnerPropertyName));
}
else
{
GEngine->AddOnScreenDebugMessage(GetKey(Key, 1), 5.0f, FColor::Black, TEXT("AMyActor::PrintPropertyDetails does not have an owner property!"));
}
UField* OuterField = Property->GetOuterUField();
if (OuterField)
{
GEngine->AddOnScreenDebugMessage(GetKey(Key, 2), 5.0f, FColor::Black, FString::Printf(TEXT("AMyActor::PrintPropertyDetails outer field full name %s"), *(OuterField->GetFullName())));
}
else
{
GEngine->AddOnScreenDebugMessage(GetKey(Key, 2), 5.0f, FColor::Black, TEXT("AMyActor::PrintPropertyDetails does not have an outer field!"));
}
UStruct* OwnerStruct = Property->GetOwnerStruct();
if (OwnerStruct)
{
FString OwnerStructCppPrefix = OwnerStruct->GetPrefixCPP();
GEngine->AddOnScreenDebugMessage(GetKey(Key, 3), 5.0f, FColor::Black, FString::Printf(TEXT("AMyActor::PrintPropertyDetails owner struct full name %s with cpp prefix %s"), *(OwnerStruct->GetFullName()), *(OwnerStructCppPrefix)));
}
else
{
GEngine->AddOnScreenDebugMessage(GetKey(Key, 3), 5.0f, FColor::Black, TEXT("AMyActor::PrintPropertyDetails does not have an owner struct!"));
}
}
else
{
GEngine->AddOnScreenDebugMessage(GetKey(Key, 0), 5.0f, FColor::Black, FString::Printf(TEXT("AMyActor::PrintPropertyDetails called but Property %s is invalid nullptr!"), *Name));
GEngine->AddOnScreenDebugMessage(GetKey(Key, 1), 5.0f, FColor::Black, TEXT(""));
GEngine->AddOnScreenDebugMessage(GetKey(Key, 2), 5.0f, FColor::Black, TEXT(""));
GEngine->AddOnScreenDebugMessage(GetKey(Key, 3), 5.0f, FColor::Black, TEXT(""));
}
}
}
void AMyActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
UProperty* MemberProperty = PropertyChangedEvent.MemberProperty;
PrintPropertyDetails(MemberProperty, TEXT("MemberProperty"), 1);
UProperty* Property = PropertyChangedEvent.Property;
PrintPropertyDetails(Property, TEXT("Property"), 5);
}
#endif