How to properly initialize instanced uobjects

I have been trying to get the following behaviour

Create an ActorComponent with an as UPROPERTY marked UObject contining some editable property.
I have encountered several issues with this and the best way to achieve this was to make the UPROPERTY Instanced.
Now I still have the issue of creating the UObject in the ActorComponent. Either the UObject get’s not created at all when adding the ActorComponent to a Blueprint Actor or the properties of the UObject are reset to default (the defaults in the UObject c++ implementation not the one set in the actor blueprint, or the UObject is not created at all when adding the component to the actor in a blueprint.

Here is my example to make this more understandable:

The ActorComponent:

Header:

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "ExampleActorComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class PROJECTROGUE_API UExampleActorComponent : public UActorComponent
{
GENERATED_BODY()

public:
// Sets default values for this component's properties
UExampleActorComponent();

public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Instanced)
class UExampleObject* ExampleObject;
};

CPP:

#include "ExampleActorComponent.h"

// Sets default values for this component's properties
UExampleActorComponent::UExampleActorComponent()
{
// not creating DefaultSubobject of ExampleObject here

PrimaryComponentTick.bCanEverTick = true;

}

the UObject:
.
Header:

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "ExampleObject.generated.h"

UCLASS(ClassGroup = (Custom), DefaultToInstanced, EditInlineNew)
class PROJECTROGUE_API UExampleObject : public UObject
{
GENERATED_BODY()

protected:
UPROPERTY(EditDefaultsOnly)
float DefaultNumber = 1.0f;

UPROPERTY(VisibleAnywhere)
float VisibleNumber = 1.0f;
};

CPP
is empty.

I want to be able to add ExampleActorComponent to an actor blueprint and change the value of DefaultNumber affecting all my actor instances but I can’t get this behavior to work correctly.

The question is now: where do I add the creation of the ExampleObject in the ExampleActorComponent, if I add it in the constructor the values of the DefaultNumber in ExampleObject will reset on play to 1.0 even when changet in an actor blueprint.

I have tried to find a function in ActorComponent that is called after the serialized properties are loaded to check if the ExampleObject already exists and only create one if there is none, but I’m not sure if this is the right approach to the problem.

Thanks for any advice or help with this issue.

Why do you want to create an additional object just to store two variables ,instead of that you can create them inside your Example component and also why do you want to spawn an object through a component and attach it to another actor?

thanks for your reply :slight_smile:

Why do you want to create an
additional object just to store two
variables ,instead of that you can
create them inside your Example component

I’m not just having the two variables in my actual problem, I have a bigger object with some methods (including UFUNCTIONS). I just thought it was easier to understand if I don’t post a lot of unnecessary code.
I also intend to reuse this object in other components, in some even multiple times, so it would generate a lot of duplication to just transfer all the variables to the actual component.

why do you want to spawn an object
through a component and attach it to
another actor?

This is not really my intention. I want them to be attached to to the same actor Ideally the UObject to the ActorComponent if that is possible. Why do you think my intention is to attach it to another actor? m

Best thing you can do is to create it inside of BeginPlay(). You mentioned above that whenever you press play value of DefaultNumber is getting reset to 1.0f, this is because, by default construction script of blueprint is executed every time you move/drag object in the world.You can verify it by just putting and print node in construction script of your actor blueprint,whenever you drag you actor in the world you can see that print node result in output log. If you want to disable it go to class settings and you can find a setting called “Run Construction Script on drag” disable it.

A work around for this that I know is to create your example object in BeginPlay() .But be careful with race conditions.

Cheers!

Thanks for your answer Tony

Just to clarify: if I create the Uobject In BeginPlay it would only exist during play which means I wouldn’t be able to edit the values in blueprint right? Maybe I’m just misunderstanding what exatly your’re suggesting.

Also since I’m not creating the UObject in the construction script how would disabling it fix the reset. is there some behind the scenes stuff going on there? I initially was creating the UObject in the C++ constructor of the ActorComponent it should be a part of. From what I’ve found online the construction script and the c++ constructor should be independent.

Yes it’s(UExampleObject) only going to exist in game and you won’t be able to edit its values in blueprint. Coming to your second question , if you enable the option (Run construction script on drag) it is going to run construction script of that actor and even if you disable that option ,every time you double click a blueprint to open and change any property or something , it is going to be reconstructed (C++ constructors are also called).
If you want to check it try putting some UE_LOG statements in constructors of your actor, component and also actor that you are going to create inside your Example component. Whenever you open blueprint to edit something in it you can see those logs printed ,immediately meaning that it is reconstructed (All C++ constructors and blueprint constructors are called). So,if you create your UExampleObject inside constructor of UExampleComponent it(UExampleObject) is going to be reconstructed every time you open blueprint. That is why I suggested you to create it inside BeginPlay() so that it is only constructed once during game play.

Coming to your last point ,constructor script and c++ constructor are not completely independent. There is an order in which c++ constructor and construction script execute i.e. all the c++ constructors are called first by the engine and then construction scripts. Later after constructors are called (also construction scripts) BeginPlay() of all C++ is executed first and then BeginPlay() in Blueprints. Although we cannot predict constructor and BeginPlay() of which class are called first, as far as I know that’s how execution flows . I recommed you to structure your code according to that execution flow.

And also I don’t know why yo are trying to create UObject inside Actor component ,if possible try creating every variable and method that you need inside your ExampleComponet that will save you the trouble of dealing that UExampleObject.

I hope this explanation clarifies your doubts,Cheers!

Yes it’s(UExampleObject) only going to exist in game and you won’t be able to edit its values in blueprint. Coming to your second question , if you enable the option (Run construction script on drag) it is going to run construction script of that actor and even if you disable that option ,every time you double click a blueprint to open and change any property or something , it is going to be reconstructed (C++ constructors are also called).
If you want to check it try putting some UE_LOG statements in constructors of your actor, component and also actor that you are going to create inside your Example component. Whenever you open blueprint to edit something in it you can see those logs printed ,immediately meaning that it is reconstructed (All C++ constructors and blueprint constructors are called). So,if you create your UExampleObject inside constructor of UExampleComponent it(UExampleObject) is going to be reconstructed every time you open blueprint. That is why I suggested you to create it inside BeginPlay() so that it is only constructed once during game play.

Coming to your last point ,constructor script and c++ constructor are not completely independent. There is an order in which c++ constructor and construction script execute i.e. all the c++ constructors are called first by the engine and then construction scripts. Later after constructors are called (also construction scripts) BeginPlay() of all C++ is executed first and then BeginPlay() in Blueprints. Although we cannot predict constructor and BeginPlay() of which class are called first, as far as I know that’s how execution flows . I recommed you to structure your code according to that execution flow.

And also I don’t know why yo are trying to create UObject inside Actor component ,if possible try creating every variable and method that you need inside your ExampleComponet that will save you the trouble of dealing that UExampleObject.

I hope this explanation clarifies your doubts,Cheers!