Non-Blueprint C++ Model Variables Reset on Play

I have noticed that model variables in a C++ class are treated in what appears to be a very strange way. Unless the variable is made public AND contains a UPROPERTY() it reverts to whatever its default value is when the Play button is clicked.

This behavior is very disconcerting and unexpected. Can someone please advise of if this is a bug or a “feature” of Unreal Engine? I am currently using UE 4.25.3.

Below is a simple example which was showing this behavior on my computer when I would place the mUnzipDir in different combinations (see commented code in header file).

A_Test.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "A_Test.generated.h"

UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class FMIKIT_API AA_Test : public AActor
{
	GENERATED_BODY()

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

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

#if WITH_EDITOR
	virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif

public:

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FMU Settings")
		FFilePath mPath;

// With UPROPERTY() - this works as expected
	//UPROPERTY(BlueprintReadOnly, Category = "FMU Settings")
	//FString mUnzipDir;

// Without UPROPERTY() -> this gets reset to default "Empty"
	//FString mUnzipDir;
private:

// As private variable, UPROPERTY() not permitted -> this gets reset to default "Empty"
	FString mUnzipDir;
};

A_Test.cpp

#include "A_Test.h"

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

#if WITH_EDITOR
void AA_Test::PostEditChangeProperty(struct FPropertyChangedEvent& e)
{
	//Super::PostEditChangeProperty(e);

	if (e.MemberProperty->GetFName().ToString() == TEXT("mPath"))
	{
		mUnzipDir = "hello";
	}
}
#endif

// Called when the game starts or when spawned
void AA_Test::BeginPlay()
{
	//SetActorTickInterval(1.f);
	Super::BeginPlay();

	FString  test= mUnzipDir; // mUnzip is empty if the mUnzip is NOT public and contains UPROPERTY()
}

This is a serious issue as when I use the UPROPERTY() I then get an Access violation reading location error with my actual project. However, when I don’t use the UPROPERTY() the error goes away. So there appears to be some other strange behaviors/restrictions occurring when UPROPERTY() is used.

It’s not a feature, it limitation of C/C++, or rether cost of being low-level. Once you compile the code, the resulting machine code (code that CPU runs) lacks any information about structure of the code. Because of UE4 (practically any C/C++ code) need to track variables on it own and does that by adding variables in to reflection system which is added by generated code by tool called UnrealHeaderTool, that search for all macros like UPROPERT() etc. and generates the code based on that.

If variable won’t be added to reflection system, UE4 don’t have idea about it’s existence there for it can not manage it state it also not being saved or serialized in any way. When you hit play all actors get recreated and put in default state (to be more exact UE4 create separate world instance each time you hit Play, as Editor world instance is for level editing and should hod level inital state of the world), resetting all unregistered variables in process.

Knowing that you should notice what is issue with your code. By your actor being reset and you set that variable it in PostEditChangeProperty that never gonna be called (or else you in PIE and edit varable in Property Editor). You should set that varable on initation, best event for that will be PostInitProperties

It’s being called when resisted variables been set to there default state (in case of blueprint object loaded in from Class Default Object (CBO) set by loaded blueprint asset, something that natively don’t exist in C++, that why they need to be loaded by extra code). At that point mPath should have it’s initial state and you can set mUnzipDir to whatever you want. Also remember to always call Super, if you don’t do that you block engine code which can cause unexpected behaviors.

Your explanation makes sense and I believe I am beginning to understand the purpose behind it. I’ve been playing with the PostInitProperties() to no avail so far for my full code. Quick follow-up though, when I place a UPROPERTY() tag on my variable, everything works as expected as I noted previously except that mUnzipDir now causes a Access violation reading location when sent to another function. It seems that UPROPERTY() puts some sort of restricted memory reference on things. Same thing occurs if i do NOT have the UPROPERTY() tag and set variables in BeginPlay(). If this is too abstract I understand, but doesn’t hurt asking.

If it helps, specifically that access error is caused by this line in my other function RemoveDllDirectory(dllDirectoryCookie);. If I comment it out then the access error goes away and things work great with greater understanding you shed on how UE4 works.

Solved the issue with the Access error. Had to do with relative vs full path issues. FFilePath returns a relative path and would fail. In anycase, thanks again for your deeper dive. That was really helpful.