LogActor: Warning: Cube /Game/StarterContent/Maps/UEDPIE_0_Minimal_Default.Minimal_Default:PersistentLevel.Cube_2 has natively added scene component(s), but none of them were set as the actor’s RootComponent - picking one arbitrarily
Actor’s USceneComponents (MeshComponent is a child of it) are structured as a tree. At this moment of construction your object have no RootComponent(so you literally doing Cube->SetupAttachment(nullptr) ), so you are asked to explicitely set root of this tree, which you can do via SetRootComponent(), so your cube’s constructor should looks like:
ACube::ACube()
{
PrimaryActorTick.bCanEverTick = true;
static ConstructorHelpers::FObjectFinder<UStaticMesh>C(TEXT("StaticMesh'/Game/StarterContent/Shapes/Shape_Cube.Shape_Cube'"));
Cube = CreateDefaultSubobject<UStaticMeshComponent>("Cube");
// Cube->SetupAttachment(RootComponent); // <-- removed, as it has no sence before the line added below
Cube->SetStaticMesh(C.Object);
SetRootComponent(Cube); // <-- added
}
Also note that ACube* C[20]; if it’s a global variable - is unsafe way of storing pointers in general case in context of UE, as the UObjects(including AActors) that have zero UPROPERTY() pointers pointing to it will be a garbage collected next time(iirc usually each 60seconds). Usually you store them in other objects like:
UCLASS()
class ACube: public AActor
{
GENERATED_BODY();
UPROPERTY() // <-- that's the key difference
ACube* C[20];
};
In this case, each cube will store pointers to other cubes. And if I want to create the 101st cube, then how should I write it? I don’t quite understand how this will work?
I’m not sure how exactly SpawnActor<> work, since AActors may be already safely tracked by UWorld* so in this particular case you maybe fine.
By unsafe i mean that your UObject may be destroyed ~() mid game by engine without your explicit command. Usually only UObjects that no longer have valid pointers to it will be destroyed, but improperly created UObjects may never had a valid pointers yet still being used. Note that it’s only applicable to UObjects(and all their childrens like AActors & etc).
As for your example - GC won’t do anything to C[1] because:
you storing raw pointers, not one of variety of smart pointers that engine provides;
you array ACube* C[20]; is not visible to reflection system sinceit’s not marked as UPROPERTY(), so GC has even less reasons to care about it.
But what i was talking about, is that object pointed by C[0] may be deleted at the some arbitrary time later, while you can still think that your pointer is valid.
each cube will store pointers to other cubes
While it probably will work, it’s sounds wild. Usually you storing data you need in some manager that created your objects. For example, AGameMode in singleplayer game.
I think IrSoil made a mistake in first answer, you should put your array of cubes in the ASpawner class, not in the ACube class.
UCLASS()
class ASpawner : public AActor
{
GENERATED_BODY();
UPROPERTY()
TArray<ACube*> C;
};
Using a global variable to store actors is not a good idea because if one of the actors is destroyed, either by calling Destroy() or by changing level, then you have absolutely no way to safely tell when an element is no longer valid in your array. They don’t automatically become nullptr when destroyed, and checking validity either by converting to bool or using IsValid is not safe. Of course if you are very meticulous you could theoretically keep your array up to date by making sure to null its elements in every possible scenario of actor destruction, but it’s extra pain for no reason.
A better way to store actors in static/global vars is by using FWeakObjectPtr or TWeakObjectPtr which is built with capabilities to validate object pointer outside of reflection system.
You almost certainly don’t need to do that though. In probably almost every case, it’s better to store your refs in UPROPERTY variable(s) even if that means creating an additional manager/singleton actor just to store them. In your case, you already have one : the spawner.