기존에는 건물을 설치하면 해당 건물이 차지하는 그리드 셀을 점유 처리하는 구조였는데, 파괴 기능을 구현하려면 단순히 Actor만 Destroy()하면 안 된다. 건물이 차지하고 있던 그리드 점유 정보도 같이 해제해야 한다.

1. 파괴 모드

StartDestroyMode()를 호출하면 기존 건설 모드가 켜져 있을 경우 먼저 건설 모드를 취소하고, 파괴 모드만 활성화한다.
이후 Tick을 켜서 매 프레임 화면 중앙 기준으로 파괴 대상을 갱신하도록 했다.

void UKOGridBuildComponent::StartDestroyMode()
{
    if (bIsBuildMode)
    {
        CancelBuildMode();
    }

    bIsDestroyMode = true;
    SetComponentTickEnabled(true);

    UpdateDestroyTargetPreview();
}

2. 바라보는 위치의 건물 찾기

파괴 모드에서는 TraceFromScreenCenter()로 화면 중앙에서 라인트레이스를 쏜다.
맞은 위치를 그리드 좌표로 변환한 뒤, 해당 셀을 점유하고 있는 Actor를 GetOccupyingActorAt()으로 가져온다.

const FIntPoint HitGrid = GridSub->WorldToGridPosition(HitResult.ImpactPoint);

AActor* TargetActor = GridSub->GetOccupyingActorAt(HitGrid);

SetDestroyTargetActor(TargetActor);

건물의 실제 메시를 직접 찾는 게 아니라, 그리드 점유 정보 기준으로 건물을 찾기 때문에

여러 칸을 차지하는 건물도 그중 아무 셀이나 바라보면 같은 건물을 찾을 수 있다.


3. 파괴 대상 시각화

파괴할 건물을 사용자가 눈으로 확인할 수 있도록 DestroyTargetMaterial을 적용했다.
대상 Actor가 바뀌면 이전 대상의 머티리얼을 먼저 복구하고, 새 대상에게 파괴 대상 머티리얼을 적용한다. 이를 위해 원래 머티리얼을 DestroyTargetOriginalMaterials 배열에 저장해 둔다.

CurrentDestroyTargetActor = NewTargetActor;
ApplyDestroyTargetMaterial(NewTargetActor);

머티리얼을 바꾸기 전에 원래 머티리얼을 저장한다.

OriginalData.Materials.Add(MeshComponent->GetMaterial(Index));
MeshComponent->SetMaterial(Index, DestroyTargetMaterial);

파괴 모드를 취소하거나 대상이 바뀌면 저장해 둔 머티리얼을 다시 복구한다.

RestoreDestroyTargetMaterial();

CurrentDestroyTargetActor = nullptr;
DestroyTargetOriginalMaterials.Empty();

4. 실제 파괴 처리

RequestDestroy()에서는 현재 선택된 파괴 대상이 유효한지 먼저 검사한다.
그다음 GridSub->FreeAreaByActor(TargetBuilding)을 호출해서 해당 건물이 차지하던 그리드 셀을 해제한다. 점유 해제가 성공하면 대상 Actor를 Destroy()한다.

if (!GridSub->FreeAreaByActor(TargetBuilding))
{
    UE_LOG(LogTemp, Warning, TEXT("[Destroy] 점유 해제 실패: %s"),
        *TargetBuilding->GetName()
    );
    return;
}

ClearDestroyTargetActor();

TargetBuilding->Destroy();

5. 그리드 점유 해제 구조

건물을 설치할 때는 OccupyArea()에서 건물이 차지하는 모든 셀에 같은 Actor를 저장하고, OccupiedAreaByActor에도 Actor와 점유 영역 정보를 저장한다.

OccupiedArea.Anchor = AnchorLocation;
OccupiedArea.Size = AreaSize;

OccupiedAreaByActor.Add(OccupyingActor, OccupiedArea);

파괴할 때는 FreeAreaByActor()가 이 정보를 이용해서 해당 Actor가 차지하던 영역을 찾아낸다.
그 후 FreeArea()를 호출해 각 셀의 OccupyingActor를 비운다.

if (!OccupiedAreaByActor.RemoveAndCopyValue(OccupyingActor, OccupiedArea))
{
    return false;
}

FreeArea(OccupiedArea.Anchor, OccupiedArea.Size);

정리

파괴 기능은 다음 역할을 한다.

  1. 파괴 모드 진입
  2. 화면 중앙 라인트레이스로 파괴 대상 탐색
  3. 그리드 점유 정보를 기준으로 대상 건물 찾기
  4. 대상 건물에 파괴 표시용 머티리얼 적용
  5. 클릭 시 점유 셀 해제
  6. Actor 삭제
  7. 머티리얼 복구 및 대상 초기화