나만의 작은 도서관
[TIL][C++] 250821 MMO 서버 개발 84일차: [언리얼] 키 입력 바인딩하기(PlayerController 버전), 프로토버프 repeatedField 복사에 대한 문제 본문
Today I Learn
[TIL][C++] 250821 MMO 서버 개발 84일차: [언리얼] 키 입력 바인딩하기(PlayerController 버전), 프로토버프 repeatedField 복사에 대한 문제
pledge24 2025. 8. 21. 23:30주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다.
[언리얼] 키 입력 바인딩하기(PlayerController 버전)
1 - PlayerController 클래스를 하나 만들어준다.

- C++ 클래스 만들 때 목록을 잘 찾아보면 PlayerController가 있을 것이다.
2 - 키 입력 시 실행시키고 싶은 함수를 정의한다.
void AInGamePlayerController::ToggleInventory()
{
// 위젯이 없으면 생성.
if (!InventoryWidget && InventoryWidgetClass)
{
InventoryWidget = CreateWidget<UUserWidget>(this, InventoryWidgetClass);
}
if (InventoryWidget)
ToggleWidget(InventoryWidget, WidgetType::WIDGET_INVENTORY);
}
- 위 코드는 간단한 인벤토리 UI 토글 코드이다. 열려있으면 닫고, 닫혀있으면 연다.
3 - SetupInputComponent 함수에 액션을 바인드 한다.
void AInGamePlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAction("ToggleStatusWindow", IE_Pressed, this, &AInGamePlayerController::ToggleStatusWindow);
InputComponent->BindAction("ToggleInventory", IE_Pressed, this, &AInGamePlayerController::ToggleInventory);
}
- 참고로 BindAction은 무슨 키가 눌려야지 실행되는지를 정의하는 게 아니라, 무슨 키든 눌렸을 때 실행되어야 할 로직을 바인드 하는 것이다.
- 위 코드를 예시로 들자면, ToggleInventory라는 액션이 감지되었을 때 어떤 걸 실행하겠느냐? 할 때 AInGamePlayerController::ToggleInventory 함수를 실행하겠다고 바인딩할 뿐이라는 거다. 해당 액션이 어떤 키에 바인딩될지는 아직 설정하지 않았다.
4 - 키와 액션 매핑하기

- 액션 감지 시 실행시킬 함수를 바인딩했다면, 이제 액션을 키에 매핑해야 한다. 언리얼 에디터 상단 바에서 Edit -> Project Settings -> Engine 목록 → Input에서 액션을 매핑하면 된다. 이름은 BindAction과 동일하게 해줘야 한다.
- 위 예시에서는 키보드 T 키와 I 키에 매핑했다.
프로토버프 repeatedField 복사에 대한 문제
[들어가기 전에 사전 지식: repeated 필드 순차 접근하는 법]
// XXX_size(): repeated 필드에 들어있는 데이터 개수
int email_count = ObjectInfo.player_info().equipment_size()
// 인덱스로 접근
for(int i = 0; i < email_count; i++){
cout << "Email " << i << ": " << person.emails(i) << endl;
}
// range-based for loop로 접근
for (const auto& email : person.emails()) {
std::cout << "Email: " << email << std::endl;
}
- repeated 필드의 경우 같은 타입의 데이터를 여러 개 들고 있는 필드인데, 아래와 같이 사용할 수 없다.
class Protocol::Item* Equipment = new Protocol::Item();
Equipment->CopyFrom(ObjectInfo.player_info().equipment())
- 안 되는 이유는 Protocol::Item은 아이템 하나만 저장하기 때문. vector를 저장할 땐 vector가 왼쪽에 와야 하는 것처럼 repeated 필드를 저장하려면 repeated 필드가 왼쪽에 와야 한다. 따라서 아래 두 가지 방식 중 하나로 복사해야 한다.
- (아래 코드는 언리얼 기준으로 만들었다. 갑자기 언리얼 코드가 나온 게 이상하다는 걸 알지만 그냥 넣었다)
// repeated 필드는 아래와 같은 타입으로 저장할 수 있다.
const google::protobuf::RepeatedPtrField<Protocol::Item>& source_items
= ObjectInfo.player_info().equipment();
// 방법 1. TArray를 사용한다.
{
TArray<Protocol::Item> Equipment;
Equipment.Empty();
for (const auto& item : source_items)
{
Equipment.Add(item);
}
// move를 이용해 성능을 높인 방식
// for (auto&& item : source_items)
// {
// Equipment.Add(std::move(item));
// }
}
// 방법 2. Protobuf 방식을 유지한다.
{
google::protobuf::RepeatedPtrField<Protocol::Item> Equipment;
Equipment.CopyFrom(source_items);
// Equipment.MergeFrom(source_items); // 이미 저장된 데이터 뒤에 추가하는 방식(Append)
}
