How to Store References to Subobject into other Subobjects

I have a custom component which has an array of custom object as such:

// Custom Component
UCLASS(meta = (BlueprintSpawnableComponent))
class MYPROJECT_API UMyComponent : public UActorComponent {
    GENERATED_BODY()

   public:
    UMyComponent();

    UPROPERTY(BlueprintReadWrite)
    virtual TArray<UMyObject*> MyObjectArray;

    ...
}


// Custom Object
UCLASS(Blueprintable, BlueprintType)
class MYPROJECT_API UMyObject : public UObject {
    GENERATED_BODY()

   public:
    virtual TArray<UMyObject*> LinkedTo
    
    ...
 }

And inside my character constructor I’m instancing them as such:

AMyCharacter::AMyCharacter() {
    MyComponent = CreateDefaultSubobject<UMyComponent>(TEXT("MyComponent"));

    MyObj1 = CreateDefaultSubobject<UMyObject>(TEXT("MyObj1"));
    MyObj2 = CreateDefaultSubobject<UMyObject>(TEXT("MyObj2"));
    MyObj3 = CreateDefaultSubobject<UMyObject>(TEXT("MyObj3"));

    MyObj1->LinkedTo = { MyObj3, MyObj2 };
    MyObj3->LinkedTo = { MyObj2 };

    MyObjectArray = { MyObj1, MyObj2, MyObj3 };
}

But when I start/play the game it gives all kinds of inconsistencies like crashing due to objects being null or duplicate objects, etc. I suspect this may be because the constructor AMyCharacter() is being called In-Editor i.e. causing duplicates or incorrect references

Should I be instancing & assigning these in the BeginPlay() or some other function?

Note: The linking / reference to other objects is necessary, please do not recommend changing that altogether

I think the problem is that the UMyObject instances created in the constructor are part of the class default object of your character.
The constructor does only run once to create the CDO but is not invoked when actual instances of your character are created.
So I don’t think it is possible to cross reference subobjects like that but you should at least be able to create the subobjects in the constructor of your component.

I have slightly changed your code (see comments in code). I did not test it though.

If it does not work you need to do everything in BeginPlay() as you said.


// Custom Component
UCLASS(meta = (BlueprintSpawnableComponent))
class MYPROJECT_API UMyComponent : public UActorComponent {
    GENERATED_BODY()

   public:
    UMyComponent();

    /** 'Instanced' ensures that each instance of 'UMyComponent' gets its own instances of 'UMyObject'.
     * I have had a similar problem in my own project, especially in builds. I suspect that this is 
     * necessary for subobjects of subobjects.
     */
    UPROPERTY(BlueprintReadWrite, Instanced)
    virtual TArray<TObjectPtr<UMyObject>> MyObjectArray;

    ...
}

// Custom Object
UCLASS(Blueprintable, BlueprintType)
class MYPROJECT_API UMyObject : public UObject {
    GENERATED_BODY()

    public:

    /** 'Transient' prevents the object references to be saved. */
    UPROPERTY(Transient)
    virtual TArray<TObjectPtr<UMyObject>> LinkedTo
    
    ...
 }
 
 AMyCharacter::AMyCharacter() {
    MyComponent = CreateDefaultSubobject<UMyComponent>(TEXT("MyComponent"));

    UMyObject* MyObj1 = CreateDefaultSubobject<UMyObject>(TEXT("MyObj1"));
    UMyObject* MyObj2 = CreateDefaultSubobject<UMyObject>(TEXT("MyObj2"));
    UMyObject* MyObj3 = CreateDefaultSubobject<UMyObject>(TEXT("MyObj3"));

    MyObjectArray = { MyObj1, MyObj2, MyObj3 };
}
 
 AMyCharacter::BeginPlay() {
    MyComponent = CreateDefaultSubobject<UMyComponent>(TEXT("MyComponent"));

    MyObj1 = MyObjectArray[0];
    MyObj2 = MyObjectArray[1];
    MyObj3 = MyObjectArray[2];

    MyObj1->LinkedTo = { MyObj3, MyObj2 };
    MyObj3->LinkedTo = { MyObj2 };
}

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.