UChildActorComponent->SetChildActorClass() Fails To Cast To Custom C++ Actor Class

I am trying to create a matrix of Cell actors that are components of a Grid actor. My process for doing so is to create UChildActorComponents inside the Grid’s constructor, set their subclass to the Cell, and then cast the ChildActorComponent’s ChildActor to my Cell class so I can begin setting the fields of the component. It has checks to see if the ChildActorComponent is valid, if the actor that belongs to the ChildActorComponent is valid, and finally if the actor that belongs to the ChildActor component is indeed of type ACell. Currently, the UE_LOG statement that would indicate the cast was successful (ie, the actor of ChildActorComponent is of type ACell) is not being reached. I’d appreciate any help in resolving this issue, it seems like something that is fairly common so I expect someone has run into something like this before. Here is the Grid constructor for reference.

AGrid::AGrid(){

FVector BoxDim(65.0f, 65.0f, 5.0f);

Rows = 1;
Columns = 1;

int currentCell = 0;

for (int i = 0; i < Rows; i++)
{
	for (int j = 0; j < Columns; j++)
	{

		//Calculate X and Y components of the cell's location.
		float X_Comp = (BoxDim.X * j * 2.0f);
		float Y_Comp = (BoxDim.Y * i * 2.0f);
		

		//Set up the transform to correctly place the cell
		FVector Spacing(X_Comp,Y_Comp,0.0f);

		FTransform CellTransform;
		
		CellTransform.SetLocation(Spacing);
		CellTransform.SetRotation(FQuat(0.0f,0.0f,0.0f,0.0f));
		CellTransform.SetScale3D(FVector(1.0f, 1.0f, 1.0f));

		UChildActorComponent* ChildActorComponent = NewObject<UChildActorComponent>();

		if (ChildActorComponent)
		{
			UE_LOG(LogTemp, Display, TEXT("ChildActorComponent is valid"));

			ChildActorComponent->SetRelativeTransform(CellTransform);

			//This should create the ChildActorComponent's Child Actor 
			ChildActorComponent->SetChildActorClass(ACell::StaticClass());

			AActor* ChildActor = ChildActorComponent->GetChildActor();

			//Check to see if the child actor 
			if (ChildActor)
			{
				UE_LOG(LogTemp, Display, TEXT("Child Actor is Valid"));
			}

			//Cast Child Actor to ACell, should be allowable because we set the child's actor class to ACell earlier
			ACell* AsCell = Cast<ACell>(ChildActor);

			if (AsCell)
			{
				UE_LOG(LogTemp, Display, TEXT("Child Actor has successfully been cast to cell."));

				FVector2D Coordinates(i, j);
				AsCell->SetCoordinates(Coordinates);
				AsCell->SetGrid(this);
			}
			else
			{
				UE_LOG(LogTemp, Display, TEXT("Child Actor cast has failed."));
			}
		}			
	}
}

}

Hi AndrewPrograhams,

Try GetChildActorTemplate() instead:

If you’re creating a large amount of these, consider creating normal actors and attaching them to the parent actor rather than use childactorcomponents as they’re not that fast. Even better - use instanced static meshes and control them all from one actor.

Thanks for the quick reply!

I attempted your recommendation like this:

if (ChildActorComponent->GetChildActorTemplate())
{
	UE_LOG(LogTemp, Display, TEXT("Child Actor template is valid."));
}

Unfortunately the line was not reached. I think this was a good try though, now we can conclude that neither GetChildActorClass() nor GetChildActorTemplate() are returning valid data, meaning for some reason the SetChildActorClass(ACell::StaticClass()) is not working as expected.

I am curious about one of your other approaches. I would like to try simply creating instances of ACell and attaching them to the Grid actor directly. Because the cells must be rendered in the viewport when a Grid actor is put into the level, I cannot only add the created cells to the TMap<FVector2D, ACell*> Cells field of the Grid like such, there needs to be an attachment to the Grid. While this gives the grid access to the Cells, it does not render them.

		FVector2D Coordinates(i, j);

		ACell* Cell = NewObject<ACell>();
		Cell->SetCoordinates(Coordinates);
		Cell->SetGrid(this);
		Cells.Add(Coordinates, Cell);

If you’re up to it, it would be great to know how I would add these as components to the grid. The Actor class does not seem to have a SetupAttachment() function. It would be Ideal to do something like this,

		FVector2D Coordinates(i, j);

		ACell* Cell = NewObject<ACell>();
		Cell->SetCoordinates(Coordinates);
		Cell->SetGrid(this);
		Cells.Add(Coordinates, Cell);
		//Cell->SetupAttachment(RootComponent);

however this is not a valid solution.

Thanks again for taking the time to help me!

1 Like

You can use the AttachToActor method to attach:

	FVector2D Coordinates(i, j);

	ACell* Cell = NewObject<ACell>();
	Cell->SetCoordinates(Coordinates);
	Cell->SetGrid(this);
	Cells.Add(Coordinates, Cell);
	Cell->AttachToActor(this,FAttachmentTransformRules::KeepRelativeTransform);

You are making it more complicated than necessary. Spawning actors at constructor time can be done by overriding OnConstruction(const FTransform & Transform) . Just be sure to call the Super::OnConstruction() at the end of your override. And be sure to mark the array you store your actors in as transient. Yea it is possible to use ChildActorComponents to spawn stuff in at BP constructor time, but I don’t like it because it adds another layer of complexity. Subobjects must be created in the constructor, OnConstruction() is the earliest you can call GetWorld->SpawnActor().

As for spawning a grid of a class you want, I would suggest creating a custom box component over a custom actor.

  1. Create a subclass of UBoxComponent for your grid cells.
  2. Create a subclass of AActor, that will spawn your grid.
  3. Override the onconstructor event.
  4. MyBoxComp * Cell = NewObject<USpaceBox(this,MyBoxComp::StaticClass(),FinalName); Use AppendInt on FString to give the components you spawn unique names. (New Object can’t be called in the actual constructor, must be called in OnConstruction() )
    5.Cell->RegisterComponent(); Cell->AttachToComponent(RootComponent,Rules); Cell->SetRelativeLocation(Location);
  5. Probably want bindings for OnComponentBeginOverlap,OnComponentEndOverlap. Use addUniqueDynamic.

Hi AndrewPrograhams!

I believe as others pointed out it’s typically better to just spawn actors at runtime for something like this. Iirc child actor components are typically used when you want to adjust the transform of a child actor at editor time as if it were attached like a regular component (though I don’t use them much myself so don’t quote me on that :sweat_smile:)

However, I do believe the approach you posted should work, but you are not attaching the child actor component to anything or registering it. That should be done like this

UChildActorComponent* ChildActorComponent = NewObject<UChildActorComponent>(ParentActor);
if (ChildActorComponent)
{
    ChildActorComponent->SetupAttachment(ComponentToAttachTo);
    ChildActorComponent->RegisterComponent();

    ChildActorComponent->SetChildActorClass(ACell::StaticClass());
    AActor* ChildActor = ChildActorComponent->GetChildActor();
    if (ChildActor)
    {
        UE_LOG(LogTemp, Display, TEXT("Child Actor is Valid"));
    }
}

RecourseDesign’s answer is how I would usually approach it, though you typically want to use World->SpawnActor<ACell>() instead of just calling NewObject<ACell>()

You can also use regular components instead of making the cells individual actors - depends on your use case.