The biggest thing that stands out is this:
//StatsComponent.cpp
UStatsComponent::UStatsComponent(const class FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
Stats = **NewObject<URPGStats>(this)**;
check(Stats); //always valid
}
Objects constructed in this fashion will not have the proper flags set that mark it as a subobject for serialization. I suspect what is happening here is that the constructor is invoked, then in ~FObjectInitializer, property values are serialized in. Since Stats is not properly marked as a subobject, it has no serialized value and ends up being zeroed out. See if you absolutely need a subobject, something like “stats” is usually best represented as a struct. Especially in this case where StatsComponent seems to be just a container for the stats data. If you do want an object, then make use of that object initializer and use CreateDefaultSubobject. You will also need to mark the Stats property as Instanced, otherwise all your stats components will share the same Stats subobject.
Now, that probably doesn’t explain why the StatsComponent itself is getting reset to null. Most likely Blueprint serialization is involved. Have you tried recreating a different blueprint from scratch to see if it also happens? If it persists, use a data breakpoint to see when it’s being overwritten. (In Visual Studio, use the debugger to break in UStatsComponent::UStatsComponent, right after Stats is assigned. Then in the breakpoints window, use New Data Breakpoint and provide &Stats.) The debugger should break when the value gets overwritten, which may or may not help clarifying. My educated guess is that it will break inside FObjectInitializer::~FObjectInitializer, in InitProperties or possibly InstanceSubobjects.
Creating subobjects inline is finnicky and certainly doesn’t work for components. You shouldn’t be trying to use the drop down menu, but should rather be using the yellow arrow to revert to defaults. But if the Blueprint’s default object is serialized with a null component, as seems to be the case, then the default value for your blueprint is in fact null and using the yellow arrow (if it even shows up) will just revert to None. You would have to fix this by reverting the blueprint defaults to that of the native base class.
Unfortunately, this is where my experience breaks down – I’ve never confirmed where the serialized data for blueprint CDOs lies. Since they are an asset type, my theory is that they go through the DDC (derived data cache) like all other assets. When I last had issues serializing blueprint defaults, I fiddled with lots of different things like recompiling, resaving, recreating, as well as deleting the contents of the DDC. I’m not sure which part worked but the DDC might be worth a try. Delete or rename the DerivedDataCache folder for your game project, you might also have to do it for the Engine DerivedDataCache folder.
Edit: Well ****, I just realized I already said most of this about the DDC earlier in this thread.
You’ll find most of them in ObjectBase.h. But unlike the class/property/function keywords, metadata keywords aren’t lexically parsed by the header tool, so they don’t need a definition in ObjectBase and therefore get forgotten from time to time… As AllowPrivateAccess was. For the most part, they can be figured out by searching the codebase to see how they are used, though.