나만의 작은 도서관

[TIL][C++] 250804 MMO 서버 개발 72일차: 블루프린트 클래스 vs C++ 클래스, 블루프린트는 왜 성능이 떨어질까? - 노드 해석 비용(오버헤드 발생), 그럼 블루프린트의 용도는 무엇인가? 그저 C++이 무서운 겁쟁이들의 쉼터일까? 본문

Today I Learn

[TIL][C++] 250804 MMO 서버 개발 72일차: 블루프린트 클래스 vs C++ 클래스, 블루프린트는 왜 성능이 떨어질까? - 노드 해석 비용(오버헤드 발생), 그럼 블루프린트의 용도는 무엇인가? 그저 C++이 무서운 겁쟁이들의 쉼터일까?

pledge24 2025. 8. 5. 01:11
주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다. 

블루프린트 클래스 vs C++ 클래스

  • 언리얼에서 특정 기능을 구현할 때 사용하는 방식으로 1) 블루프린트와 2) C++ 이 있다. 이 둘은 상호보완적인 관계를 가지고 있긴 하지만, 블루프린트 Only, C++ Only 방식으로 게임 개발을 할 수 있기 때문에 한쪽을 쓰지 않는다고 문제가 되는 경우는 크게 없다.
  • 문제는 이러한 경향 때문에 오히려 헷갈린다는 것이다. 한쪽으로만 개발을 할 수 있는데 왜 하나만 남겨두지 않고 둘 다 있는 것일까? 섞어서 쓰는 경우에는 어떤 이유 때문인가?

 

블루프린트 클래스

  • C++에 대해서는 크게 할 말이 없으니, 블루프린트에 대해서 알아보자. 블루프린트, 정확히 블루프린트 클래스는 Unity의 프리팹(prefab)과 같은 역할을 한다. 프리팹은 게임 오브젝트의 세팅을 저장해 두고, 필요할 때마다 맵에 배치하거나, 다른 스크립트의 입력으로 사용할 때 활용되는데, 블루프린트도 그러한 역할을 맡는다는 것이다.

 

프리팹과 다른 블루프린트 클래스의 특성: 일부 로직을 소스 코드에서 빼두는 게 가능

  • 하지만 프리팹과 다른 점은 일부 로직의 구현을 소스 코드에서 빼낼 수 있다는 것이다. 유니티의 경우 현재 프리팹에 기능을 추가하기 위해서는 스크립트를 추가하는 방식밖에 없었다.(내가 알기로는) 그래서 무언가 새롭게 추가할 때 스크립트를 추가하는데, 블루프린트 클래스의 경우 그럴 필요 없이 현재 클래스에서만 사용해서 로직의 재사용 가능성이 없거나, 로직의 흐름을 시각적으로 판단하고 싶을 때 블루프린트로 로직을 짤 수 있다.
  • 블루프린트는 일반적인 프로그래밍 언어처럼 변수, 함수를 작성하는 기능이 존재하며, 각 노드들을 연결하여 로직을 구성한다.
    • 이때, 노드에서 활용하는 함수는 엔진에서 미리 제공하는 것을 사용하거나, UFUNCTION 매크로 함수를 통해 엔진에 등록한 함수들을 사용할 수 있다.
    • 커스텀 이벤트도 만들 수 있다. 이벤트는 특정 조건이 만족되었을 때 실행되는 방식의 작업으로, 사용자가 임의로 발동시키거나 시스템에 등록해 두고 알아서 발동시키게 할 수도 있다.

 

블루프린트의 단점

  • 블루프린트는 단점이 몇 가지 존재한다. 그중 가장 주의 깊게 봐야 하는 단점은 바로 “성능 저하”인데, 기본적으로 블루프린트 방식으로 로직을 작성하면 C++ 방식의 로직 실행 성능보다 떨어진다.

 

블루프린트는 왜 성능이 떨어질까? - 노드 해석 비용(오버헤드 발생)

  • C++은 컴파일 방식이며, 함수를 호출하는 코드는 함수 포인터만 남게 되기 때문에 호출 시, 고민할 것 없이 포인터를 타고 이동하면 된다.
  • 하지만, 블루프린트는 인터프리터 방식이기 때문에 각 노드의 순서가 미리 정해져 있더라도 뒤에 실행될 노드들이 미리 기계어로 번역되어 있지 않는다. 즉, 각 블루프린트 노드들은 런타임에 해석되어 실행된다는 것이다.

 

헷갈리면 안 되는 점 : 함수 자체의 실행은 빠르다

  • 윗 말이 “각 노드의 함수 원본 코드는 런타임에 컴파일된다”는 것을 의미하지는 않는다. UFUNCTION을 통해 등록된 함수의 경우, 함수 자체는 이미 기계어로 컴파일되어 있으며, 각 노드가 인터프리터 방식으로 해석되는 부분은 “이 노드가 호출해야 할 함수는 무엇인지, 다음 노드는 무엇인지 등”이다. 함수를 찾은 뒤 함수를 실행하는 속도는 거의 네이티브와 같다.
C++: Function1() → Function2() → Function3()  (직접 호출)

Blueprint: Node1 → [노드 검색] → Node2 → [노드 검색] → Node3
          ↑                    ↑                    ↑
      실행 컨텍스트 관리    다음 노드 찾기      실행 컨텍스트 관리

 

그럼 블루프린트의 용도는 무엇인가? 그저 C++ 이 무서운 겁쟁이들의 쉼터일까?

  • 결론만 말하자면 아니다. 위에서도 잠깐 말했지만, 블루프린트는 일부 로직을 소스코드로부터 분리해서 구현할 수 있다는 장점이 있다.
  • 이러한 점은 깔끔하게 프로젝트를 관리하거나 디자이너와 협력할 때 유용하게 쓰일 수 있는데, 개인적으로 생각했을 때 아래와 같이 사용하는 것이 가장 좋아 보인다.

 

“C++은 기능을 정의하고, 블루프린트는 기능 간의 흐름을 디자인한다”

  • C++에서 직접적으로 캐릭터 특징을 디자인하거나, 전투의 흐름을 설계하지 않는 것이다. 이러한 게임 디자인적인 측면은 전부 블루프린트에게 맡긴다. C++은 그저 필요한 기능을 만들고, 이에 대한 활용은 하지 않는다.
    • 예를 들어, 각 캐릭터마다 특징이 있고 어떤 캐릭터가 대미지를 받으면 괴랄하게 멀리 날아간다고 가정해 보자. 이때 C++은 날아가는 조건과 이벤트가 발생했을 때의 흐름을 정의하지 않는다. 대신, 날아가는 기능을 정의할 뿐이다. 충격 수치, 날아가는 정도, 날아가는 조건 등은 블루프린트에서 정의한다.
  • 아래는 AI한테 물어본 결과인데, C++에서 작성할 내용은 아래와 같다.
    • 성능이 중요한 로직: 복잡한 계산, 물리 시뮬레이션, AI 로직, 대량의 데이터 처리 등 성능의 핵심적인 부분은 C++을 사용한다.(블루프린트보다 훨씬 빠르게 실행되기 때문)
    • 엔진 수준의 기능 구현: 게임 엔진 자체의 핵심 기능(렌더링, 물리, 네트워킹 등)을 확장하거나 수정할 때 사용
    • 코드 재사용성 및 모듈화: 여러 프로젝트나 클래스에서 공통적으로 사용되는 기능을 C++로 라이브러리 형태로 활용하여 재사용성을 높인다.
  • 아래도 똑같이 물어본 결과인데, 블루프린트에서 작성할 내용은 아래와 같다.
    • 빠른 프로토타이핑 및 이터레이션: 게임 디자이너나 아티스트가 코딩 없이도 게임 로직을 빠르게 구현하고 테스트할 수 있도록 하고 싶을 때 사용
    • 게임 플레이 로직: 상호작용, UI 이벤트 처리 등 게임 플레이어와 직접적으로 관련된 로직을 구현할 때 사용
    • C++ 클래스의 확장: C++ 클래스에서 정의된 함수를 블루프린트로 노출시켜, C++의 기능을 블루프린트에서 쉽게 활용하고 확장할 때 사용한다. 예를 들어, C++로 정의된 기본 캐릭터 클래스에 블루프린트로 점프나 공격과 같은 특정 동작을 추가할 수 있다.

 

요약: 블루프린트 vs. C++

특징 C++ 블루프린트
용도 핵심 엔진 기능, 성능이 중요한 로직, 복잡한 시스템 구현 게임 플레이 로직, 빠른 프로토타이핑, 디자이너 작업
성능 우수 (컴파일된 코드) 상대적으로 느림 (인터프리터 방식)
개발자 프로그래머 프로그래머, 디자이너, 아티스트
가독성 텍스트 기반으로 복잡한 로직은 파악이 어려울 수 있음 비주얼 노드 기반으로 로직 흐름 파악이 용이
디버깅 IDE(Visual Studio 등)를 사용한 강력한 디버깅 도구 엔진 내장 디버깅 도구 (블루프린트 디버거)

 

결론

  • C++은 기반을 다지는 역할을 하며, 게임의 핵심 시스템, 성능에 민감한 로직만 구현하고 게임 플레이 로직 디자인은 하지 않는다.
  • 블루프린트는 게임의 살을 붙이는 역할을 하며, C++로 만든 뼈대 위에서 디자이너들이 게임 플레이 로직 디자인을 한다.