2025-05-21 TIL

알고리즘 문제

이번 문제에서는 홀수와 짝수인 경우 알고리즘이 달라질 수 있다는 부분은 빠르게 이해했지만, 팰린드롬 알고리즘을 어떻게 구현해야할지 아이디어를 떠올리지 못해서 문제를 풀기 어려웠습니다.
[프로그래머스][C++] 가장 긴 팰린드롬

최종 프로젝트

오늘은 다음과 같은 작업을 수행했습니다.

  1. 상호작용을 통해 레벨을 이동하는 액터 추가
  2. UGameInstanceSubsystemUDeveloperSettings을 사용한 DataTable 관리
  3. 무기가 버려지는 기능 추가(즉, 아이템 버리기)
  4. 무기 슬롯 시스템의 버그 수정

레벨 이동을 위한 액터

레벨 이동을 위해서 이동하려는 레벨의 이름을 저장하거나 직접 작성하여 이동하는 방법을 가장 먼저 떠올렸지만 레벨의 이름을 직접 작성해야하므로, 휴먼에러가 우려됐습니다.
그렇기 때문에 상호작용되는 액터를 이용한 레벨 이동에서는 액터의 인스턴스에서 이동하려는 레벨을 참조하도록 하여 참조된 애셋의 이름을 가져와 해당 애셋의 이름으로 레벨을 이동하게 구현했습니다.

이때, 레벨 애셋을 참조하는 포인터 작성에 다양한 문제를 겪었습니다.

처음에는 다음과 같은 이유로하드 레퍼런스인 TObjectPtr을 사용하고, 후에 최적화가 필요하다면 소프트 레퍼런스로 전환하고자 했습니다.

  • TSoftObjectPtr로 저장하면 로직이 좀 더 복잡해지기 때문에 최대한 간단하고 빠르게 구현하기 위함입니다.
  • 레벨의 수가 많지 않기 때문에 메모리에 항상 로드되어있어도 문제가 되지 않을 것으로 판단했습니다.

하지만, 하드 레퍼런스를 사용했을 때 참조된 레벨에서 참조중인 액터가 있는 레벨로 이동할 때 에러가 나면서 프로그램이 터지는 문제가 있었습니다. 메모리 문제인 것 같기 때문에 TWeakObjectPtr을 사용했고, 문제는 해결했습니다.

그런데 TWeakObjectPtr을 사용했을 때 또 다른 문제가 있었는데, 클래스 인스턴스에 참조하려는 애셋을 지정하면 특정 조건들에서 참조가 사라지는 문제가 발생했습니다.
특정 조건은 다른 레벨로 이동하거나 에디터를 껐다 켜는 등 메모리가 해제되는 경우로 추측됩니다.

그래서 결과적으로 TSoftObjectPtr을 사용하여 TObjectPtrTWeakObjectPtr을 사용했을 때 발생하는 문제를 해결해주었습니다.

UGameInstanceSubsystem과 UDeveloperSettings을 사용한 DataTable 관리

데이터 테이블을 여러 곳에서 사용할 수 있는데, 이것을 한 곳에서 관리하기 위해서 UGameInstanceSubsystem을 사용하였습니다.
그런데, UGameInstanceSubsystem은 블루프린트 클래스를 만들 수 없다는 문제가 있었습니다.

그래서 이 문제를 해결하기 위해 방법을 찾던 중 다른 객체를 참조하기 위한 방법으로 UDeveloperSettings을 사용할 수 있다는 것을 알게됐고, 적용해보았습니다.
적용한 방법은 UCLASS(Config = Game, DefaultConfig)으로 Config 폴더의 DefaultGame.ini에 참조한 객체가 저장되도록 해주었습니다.
이 방법으로 참조 객체를 설정하기 위해서는 두 가지 방법이 있습니다.

  1. 해당 파일에 직접 참조하려는 애셋을 작성해줄 수 있습니다.
  2. 언리얼 엔진에서 프로젝트 세팅에 Game카테고리에 해당 UDeveloperSettings를 상속받은 세팅 클래스가 있기 때문에 에디터 상에서도 간단하게 참조를 변경할 수 있습니다.

이렇게 참조된 데이터테이블을 UGameInstanceSubsystem에서 Initialize시 각 데이터 테이블을 로드하도록 해주었습니다.

무기가 버려지는 기능

아이템이 버려지는 기능을 구현해보기 전에 무기가 버려지도록 구현을 먼저 해보았습니다.

무기는 데이터 테이블을 사용하기 때문에 데이터 테이블에 대해 위에 설명한 것처럼 관리해주었습니다.

버려지거나 무기 액터를 소유하고 있을 때 등 특정 조건에서 DataTableData에 대한 RowNameFName으로 저장하도록 구현했습니다.
그에 따라 클래스들에 필요한 변수나 함수들을 설정해 주었습니다.

무기가 땅에 버려질 때 구현했던 변수와 함수들로 데이터테이블을 참조하여 값을 가져와 스태틱 메시 및 관련 변수를 세팅해주었습니다.
이렇게 액터 클래스는 하나를 사용하지만, 메시와 변수값들을 변경해주어 여러 클래스를 생성할 필요 없도록 구현했습니다.
그 결과 공통 로직을 하나의 클래스에 집중할 수 있기 때문에 유지보수가 용이해졌습니다.
또한, 데이터 테이블에 값만 추가하면 되기 때문에 확장이 용이해졌습니다.
이외에도 클래스를 적게 생성하기 때문에 메모리와 성능에 최적화가 됐을 것이며, 문제가 발생했을 때 한 클래스만 살펴보면 되기 때문에 테스트가 용이해졌습니다.

이런 부분에 대해 알아보니 데이터 중심 설계(Data-Driven Design)이라고 부르며, 효과적인 설계 방식이라는 것을 알게됐습니다.
앞으로도 잘 기억해둘 필요가 느껴지고, 이런 방식으로 구현하는 것은 잘했다고 생각됩니다.

RowName을 변수로 저장한 이유는 특정 데이터테이블에서 필요한 값을 바로 찾기 위함도 있지만, 후에 세이브/로드 시에 사용하고자 하기 때문입니다.
좀 더 자세하게 설명하자면, 저장된 RowName을 사용하여 이 값으로 데이터테이블에서 데이터를 바로 가져올 수 있기 때문에 저장된 액터를 다시 생성 하는 용도로 사용하고자 합니다.

무기 슬롯 시스템의 버그 수정

무기 슬롯 시스템에 다음과 같은 상황에서의 버그가 있었고, 해당 문제들을 수정해주었습니다.

  • 무기 슬롯은 가득 찼고, 착용 중인 무기가 없을 때 추가적으로 무기를 획득하는 경우
  • 장착하려는 슬롯에 무기가 없는 경우
  • 장착하려는 슬롯이 이미 장착 중인 경우

위의 버그는 프로토타이핑 동안 빠르게 작업하기 위해 예외 처리를 하지 않았던 부분이기 때문에 조건식을 사용하여 해결했습니다.

Date:     Updated:

카테고리:

태그:

TIL 카테고리 내 다른 글 보러가기

댓글남기기