In fact, the solution has been around for a long time, and the engine developers are using it.
How this is implemented can be viewed in USplineComponent:
If we are talking about components, then in order to prevent resetting property values to predefined ones, we need to do 3 steps:
- We need to define the FMyComponentInstanceData structure, which will store the values of properties that need to be restored when the component is recreated:
USTRUCT()
struct FMyComponentInstanceData : public FSceneComponentInstanceData {
GENERATED_BODY()
public:
// As an example, let's take a reference to instance of another component.
// We will store 2 property values: current property value, and property value before user
// construction script:
/** Current property value. */
UPROPERTY()
TObjectPtr<UOtherComponent> ReferenceToOtherComponent;
/** Before user construction script property value. */
UPROPERTY()
TObjectPtr<UOtherComponent> ReferenceToOtherComponentPreUCS;
public:
FMyComponentInstanceData() {}
explicit FMyComponentInstanceData(const UMyComponent* SourceComponent)
: FSceneComponentInstanceData(SourceComponent) {}
virtual ~FMyComponentInstanceData() = default;
virtual void ApplyToComponent(UActorComponent* Component, const ECacheApplyPhase CacheApplyPhase) override {
Super::ApplyToComponent(Component, CacheApplyPhase);
CastChecked<UMyComponent>(Component)->ApplyComponentInstanceData(
this, (CacheApplyPhase == ECacheApplyPhase::PostUserConstructionScript));
}
};
- Define the method in our component that will create and fill this structure:
virtual TStructOnScope<FActorComponentInstanceData> GetComponentInstanceData() const override
{
TStructOnScope<FActorComponentInstanceData> InstanceData = MakeStructOnScope<FActorComponentInstanceData, FMyComponentInstanceData>(this);
FMyComponentInstanceData* MyComponentInstanceData = InstanceData.Cast<FMyComponentInstanceData>();
// Store the property value
MyComponentInstanceData->ReferenceToOtherComponent = ReferenceToOtherComponent;
return InstanceData;
}
- Define the method in our component that will restore the properties when component is recreated:
void ApplyComponentInstanceData(struct FMyComponentInstanceData* ComponentInstanceData, const bool bPostUCS)
{
if (bPostUCS)
{
// Here we can handle changes in user construction script if needed.
// If the property was changed in the user construction script, then there
// is no need to restore the property value:
if(!IsReferenceToOtherComponentChangedInUCS())
{
// Restoring property value
ComponentInstanceData->ReferenceToOtherComponent= ComponentInstanceData->ReferenceToOtherComponentPreUCS;
}
}
else
{
ComponentInstanceData->ReferenceToOtherComponentPreUCS = ReferenceToOtherComponent;
}
ReferenceToOtherComponent = ComponentInstanceData->ReferenceToOtherComponent;
SomeComponentUpdatesRelatedToPropertyChange();
}
Note:
-
In this example, it is assumed that UMyComponent is a descendant from the USceneComponent class. Otherwise, creating and filling of the FMyComponentInstanceData structure may be somewhat more difficult to take into account the algorithm for restoring the properties of the parent component class.
-
The UMyComponent class has the
TObjectPtr<UOtherComponent> ReferenceToOtherComponent
property, which refers to an instance of another component. -
The UMyComponent::IsReferenceToOtherComponentChangedInUCS() method code is not given because it depends on the meaning of the property being restored and the logic of the component (let me remind you once again that you can spy on the example of the implementation in the USplineComponent class in the source code of the engine).