프로젝트에서 타일맵을 만들 일이 있어서 정리를 할 겸 ChildActorComponent로 만들려고했다.
처음에는 AddComponentByClass를 사용하려고 했지만 문제가 많아서 사용하지 않기로 했다.
그 이유는 나는 UFUNCTION의 CallInEditor를 이용해 버튼으로 만들어 생성하려고 했는데, 이 버튼이 Blueprint에서는 정상 작동 하지 않았다.
그래서 Level에 배치해서 사용하는 수 밖에 없었는데, 이렇게 했더니 처음엔 추가가 됬지만 오브젝트가 움직인다던가 설정값이 바뀌면 모두 초기화되어 사라졌다.
이런 문제들 때문에 AI와 인터넷의 도움을 받아 찾아본 결과 NewObject를 이용하면 된다고 했다.
NewObject를 이용해 생성했지만, 이번엔 아웃라이너에선 생성되었다고 뜨는데 디테일에선 안뜨고, 아웃라이너든 디테일이든 선택할 수 없는 문제가 생겼다.
인터넷 서핑을 해보니 NewChildActorComp->CreationMethod = EComponentCreationMethod::Instance;를 붙이면 된다고 해서 붙였더니 해결되었다.
void ARSTileMap::CreateTilesWithChildActorComponent()
{
RS_LOG_F("%d x %d Tile 생성", Width, Height)
check(DefaultTileType)
if (TileParent->GetNumChildrenComponents() > 0)
{
auto& ChildCompArray = TileParent->GetAttachChildren();
int Num = ChildCompArray.Num();
for (int32 i = 0; i < Num; i++)
{
auto& Child = ChildCompArray[0];
if (Child && !Child->IsBeingDestroyed())
{
Child->DestroyComponent();
}
}
}
FVector TileSize = DefaultTileType.GetDefaultObject()->GetTileSize();
for (int32 i = 0; i < Height; i++)
{
for (int32 j = 0; j < Width; j++)
{
FName TileName = FName(FString::Printf(TEXT("Tile %d x %d"), i, j));
//새로운 UChildActorComponent를 생성
UChildActorComponent* NewChildActorComp = NewObject<UChildActorComponent>(this, TileName);
NewChildActorComp->SetChildActorClass(DefaultTileType);
//부모를 설정 후 컴포넌트를 등록
NewChildActorComp->SetupAttachment(TileParent);
NewChildActorComp->CreationMethod = EComponentCreationMethod::Instance;
NewChildActorComp->RegisterComponent();
FVector Location = FVector::ZeroVector;
Location.Y += TileSize.Y * j;
Location.X += TileSize.X * i;
NewChildActorComp->SetRelativeLocation(Location);
}
}
}
그런데 여기서 심각한 문제가 발견된다.
Level의 프로퍼티를 수정해서 그런건지 모르겠지만 가끔 생성되었던 ChildActor들이 모두 날아갔다
그리고 블루프린트를 컴파일을 누르는 순간 ChildActor들이 모두 날아갔다
맵을 다시 로딩하면 생기기는 했지만 너무나도 불안한 형태인게 보여서 튜터분께 질문했다.
튜터분이 보기에도 확실히 너무나도 불안한 형태이고, ChildActorComponent는 패키징 문제 등 문제가 많은 Component라고 한다.
차라리 에디터에서 생성하는걸 포기하고 플레이 때 동적으로 생성하는게 더 좋아보인다고 해셔서 확실히 그런 것 같아 동적으로 바꿨다.
void ARSTileMap::CreateTilesWithSpawnActor()
{
RS_LOG_F("%d x %d Tile 생성", Width, Height)
check(DefaultTileType)
//임시, 타일 전체 삭제
if (Tiles.Num() > 0)
{
for (auto& Tile : Tiles)
{
if (Tile.Get())
{
Tile->Destroy();
}
}
Tiles.Empty();
}
FVector TileSize = DefaultTileType.GetDefaultObject()->GetTileSize();
FVector StartLocation = GetActorLocation();
for (int32 i = 0; i < Height; i++)
{
for (int32 j = 0; j < Width; j++)
{
ARSBaseTile* TileActor = GetWorld()->SpawnActor<ARSBaseTile>(DefaultTileType);
TileActor->SetActorLabel(FString::Printf(TEXT("Tile %d x %d"), i, j));
FVector Location = StartLocation;
Location.X += TileSize.X * j;
Location.Y += TileSize.Y * i;
TileActor->SetActorLocation(Location);
Tiles.Add(TileActor);
}
}
}
확실히 구조가 간단한 만큼 문제가 생기지는 않을 것 같다.