블루프린트 인터페이스 활용
지금까지 액터, 컴포넌트, 상속을 통해 블루프린트에서 객체 지향의 기본 구조를 다뤘습니다.
이번 절에서는 다음 단계인 블루프린트 인터페이스(Blueprint Interface)를 살펴봅니다. 인터페이스는 상속과 다른 방식으로 블루프린트 간 통신을 구성하며, 느슨한 결합(Loose Coupling)으로 확장성과 유지보수성을 높입니다.
인터페이스란 무엇인가?
일상생활의 비유로 설명해 보겠습니다. 여러분이 스마트폰 충전기(인터페이스)를 가지고 있다고 생각해봅시다. 이 충전기는 어떤 종류의 스마트폰이든 상관없이 특정 포트(USB-C, 라이트닝 등)만 가지고 있다면 충전할 수 있습니다. 충전기는 충전한다는 기능만 정의할 뿐, 그 기능을 어떤 스마트폰이 어떻게 구현하는지는 신경 쓰지 않습니다.
블루프린트 인터페이스도 이와 유사합니다. 인터페이스는 특정 기능의 이름만 정의하고, 그 기능을 어떻게 구현할지는 해당 인터페이스를 구현(Implement)하는 블루프린트 클래스에 맡깁니다. 인터페이스 자체는 어떠한 로직이나 변수도 포함하지 않습니다. 오직 이러한 기능이 존재한다는 약속만을 담고 있습니다.
인터페이스가 왜 필요한가?
상속은 A는 B이다 (A is a B)라는 부모-자식 관계를 형성합니다. 예를 들어, BP_SportsCar는 BP_Car이다. 이는 매우 강력하지만, 한 가지 제약이 있습니다. 블루프린트는 단일 상속(Single Inheritance) 만을 지원합니다. 즉, 하나의 블루프린트는 오직 하나의 부모 블루프린트만 가질 수 있습니다.
하지만 실제 게임에서는 서로 다른 블루프린트가 같은 기능을 공유해야 하는 경우가 많습니다.
예를 들어 BP_Door(문), BP_Lever(레버), BP_Button(버튼)이 모두 상호작용 기능을 가져야 할 수 있습니다. 이들을 하나의 부모 클래스로 강제로 묶으면 설계가 부자연스러워집니다. 문은 레버가 아니고, 레버는 버튼이 아니기 때문입니다.
이럴 때 인터페이스가 해결책이 됩니다. 이 세 가지 액터(문, 레버, 버튼)는 모두 BPI_Interactable (상호작용 가능 인터페이스)를 구현할 수 있습니다. 각 액터는 BPI_Interactable이 정의한 Interact (상호작용) 기능을 자신만의 방식으로 구현합니다. 문은 열리고, 레버는 작동하고, 버튼은 눌리는 식으로 말이죠.
인터페이스는 A는 B를 할 수 있다 (A can do B)라는 능력 관계를 정의합니다.
어떤 블루프린트가 특정 인터페이스를 구현했는지만 확인하면, 해당 기능을 안전하게 호출할 수 있습니다. 이 방식은 블루프린트 간 결합도를 낮추고, 나중에 새 상호작용 오브젝트를 추가할 때도 기존 로직 수정 없이 인터페이스 구현만으로 확장할 수 있게 해 줍니다.
블루프린트 인터페이스 생성 및 정의
- 콘텐츠 브라우저(Content Browser)에서 마우스 오른쪽 버튼을 클릭합니다.
블루프린트(Blueprints)>블루프린트 인터페이스(Blueprint Interface)를 선택합니다.- 이름을 지정합니다. (예:
BPI_Interactable- 일반적으로 BPI_ 접두사를 사용합니다.) - 생성된 인터페이스 에셋을 더블클릭하여 인터페이스 에디터를 엽니다.
- 인터페이스 에디터의 좌측
함수(Functions)패널에서+버튼을 클릭하여 새로운 함수를 추가합니다. - 함수 이름을 지정합니다. (예:
Interact) - 이 함수에 필요한 입력(Inputs) 및 출력(Outputs) 매개변수를 디테일 패널에서 추가합니다. (예:
Interact함수는InteractingActor(Actor Reference 타입)이라는 입력 핀을 가질 수 있습니다.) - 참고: 인터페이스 함수에는 로직을 구현할 수 있는 그래프가 없습니다. 오직 함수의 이름과 입출력 매개변수만 정의합니다.
컴파일 및 저장: 인터페이스를 정의한 후에는 반드시 컴파일하고 저장해야 합니다.
인터페이스 구현 및 활용
이제 생성한 인터페이스를 실제 액터 블루프린트에서 구현하고 호출해 봅시다.
- 인터페이스 기능을 추가하고 싶은 액터 블루프린트(예:
BP_Door)를 엽니다. - 블루프린트 에디터의 상단 툴바에서
클래스 세팅(Class Settings)버튼을 클릭합니다. - 디테일 패널의
인터페이스(Interfaces)섹션에서Implemented Interfaces옆의+ 추가(+ Add)버튼을 클릭합니다. - 목록에서 여러분이 만든 인터페이스(예:
BPI_Interactable)를 검색하여 선택합니다. - 이제 블루프린트를 컴파일하면 좌측 함수(Functions) 패널에
인터페이스(Interfaces)섹션이 새로 생기고, 그 아래에Interact함수가 나타난 것을 볼 수 있습니다.
- 좌측 함수 패널의
인터페이스섹션 아래Interact함수 위에서 마우스 오른쪽 버튼을 클릭하고이벤트 구현(Implement Event)을 선택합니다. - 이벤트 그래프에
Event Interact노드가 생성됩니다. 이제 이 이벤트 노드의 실행 핀 뒤에 이 액터가Interact되었을 때 수행할 실제 로직을 구현합니다. - 예시:
Print String노드를 연결하여 문이 상호작용되었습니다!라고 출력하도록 설정하고, 문을 열리는 애니메이션을 재생하는 로직을 추가할 수 있습니다. - 블루프린트를 컴파일하고 저장합니다.
- 이제 다른 블루프린트(예: 플레이어 캐릭터 블루프린트)에서 이
Interact기능을 호출해봅시다. - 플레이어 캐릭터 블루프린트의 이벤트 그래프에서, 예를 들어
E키를 눌렀을 때 특정 액터와 상호작용하는 로직을 만든다고 가정합니다. Event E키 입력 노드를 추가합니다.- 플레이어가 보고 있는 액터나 겹쳐진 액터를 가져오는 로직을 만듭니다. (예:
Line Trace By Channel또는Get Overlapping Actors사용) - 가져온 액터 레퍼런스 핀에서 드래그하여 마우스 오른쪽 버튼을 클릭하고, 여러분의 인터페이스 함수 이름(예:
Interact)을 검색합니다. 이때,Message가 붙은 노드를 선택해야 합니다. (예:Interact (Message)) Interact (Message)노드는 대상 액터가BPI_Interactable인터페이스를 구현했는지 자동으로 확인하고, 구현했다면 해당 액터의Event Interact로직을 실행시킵니다.Target핀에는 상호작용할 액터 레퍼런스를 연결하고, 필요한 경우 입력 매개변수를 연결합니다.
이 방식의 장점은, Interact (Message) 노드를 연결할 때 대상 액터가 BP_Door인지, BP_Lever인지, BP_Button인지 명시적으로 알 필요가 없다는 것입니다. 그저 BPI_Interactable 인터페이스를 구현했는지 여부만 중요합니다.
인터페이스 사용의 이점 정리
- 다형성(Polymorphism) 구현: 하나의 인터페이스 메시지 호출로, 다양한 종류의 블루프린트가 각자 자신만의 방식으로 기능을 수행할 수 있게 합니다.
- 느슨한 결합(Loose Coupling): 블루프린트 간의 의존성을 줄여줍니다. 호출하는 쪽은 특정 클래스에 묶이지 않고, 인터페이스만 알면 되므로 시스템 변경에 유연하게 대처할 수 있습니다.
- 확장성: 새로운 기능이 필요할 때, 기존 인터페이스에 함수를 추가하거나, 새로운 클래스가 기존 인터페이스를 구현하기만 하면 됩니다.
- 협업 효율성: 팀원 간에 기능의 계약을 명확히 정의할 수 있어, 각자 독립적으로 개발하고 나중에 쉽게 통합할 수 있습니다.
블루프린트 인터페이스는 복잡한 게임 시스템을 설계하고, 여러 블루프린트가 서로 유기적으로 상호작용하게 만들 때 매우 유용한 고급 도구입니다. 처음에는 어렵게 느껴질 수 있지만, 인터페이스를 활용하기 시작하면 블루프린트 코드의 유지보수성과 확장성이 크게 향상되는 것을 경험하실 수 있을 것입니다.
이번 절에서는 블루프린트 인터페이스의 개념, 생성 및 활용 방법, 그리고 상속과의 차이점에 대해 알아보았습니다. 인터페이스는 블루프린트의 객체 지향적 설계를 한 단계 더 발전시키는 중요한 요소입니다.