블루프린트 인터페이스를 통한 통신
이전 절에서 이벤트 디스패처로 느슨한 결합을 구현했다면, 이번에는 인터페이스 방식으로 통신 구조를 확장해 봅니다.
여기서는 블루프린트 인터페이스(Blueprint Interface)를 통신에 적용하는 방법과, 직접 참조/이벤트 디스패처 대비 장단점을 비교합니다. 특히 다형성(Polymorphism) 기반 설계에서 인터페이스가 왜 강력한지에 집중합니다.
블루프린트 인터페이스를 통한 통신이란?
블루프린트 인터페이스를 이용한 통신은 특정 기능의 '계약'을 기반으로 합니다. 인터페이스는 어떤 클래스들이 구현해야 할 함수의 선언(Declaration) 만을 포함하며, 실제 구현(Definition)은 각 클래스에 맡깁니다. 즉, 나는 이런 기능을 제공할 수 있다는 약속을 하는 것이죠.
다른 블루프린트에서 이 인터페이스의 기능을 호출할 때는, 호출 대상이 실제로 이 인터페이스를 구현했는지 여부만 확인합니다. 구현했다면, 그 클래스가 어떻게 구현했는지와 상관없이 해당 인터페이스 함수를 호출할 수 있습니다. 이는 마치 어떤 스마트폰이든 충전기 규격만 맞으면 충전할 수 있는 것과 같습니다. 충전기는 어떤 브랜드의 폰인지 알 필요 없이, 충전 규격만 지키면 됩니다.
인터페이스 통신 과정
블루프린트 인터페이스를 활용한 통신은 크게 세 단계로 이루어집니다.
블루프린트 인터페이스 정의: 통신에 사용될 함수(기능)를 인터페이스에 선언합니다. (3장 4절 복습)
- 콘텐츠 브라우저에서 마우스 우클릭 >
블루프린트(Blueprints)>블루프린트 인터페이스(Blueprint Interface)선택. (예:BPI_Damageable) - 인터페이스 에디터에서 함수를 추가하고 입출력 핀을 정의합니다. (예:
ApplyDamage함수에DamageAmount(Float) 입력 핀 추가) - 주의: 인터페이스 함수에는 로직을 구현하지 않습니다.
클래스에서 인터페이스 구현 및 함수 정의: 해당 기능을 제공할 블루프린트 클래스(예: BP_Enemy, BP_PlayerCharacter)에서 인터페이스를 구현하고, 인터페이스 함수를 정의합니다.
- 인터페이스를 구현할 액터 블루프린트(예:
BP_Enemy)를 엽니다. - 클래스 세팅(Class Settings)을 클릭합니다.
- 디테일 패널의
Implemented Interfaces섹션에서BPI_Damageable을 추가합니다. - 블루프린트를 컴파일하면 좌측 함수(Functions) 패널의
인터페이스섹션에ApplyDamage함수가 나타납니다. ApplyDamage함수 위에서 마우스 우클릭 >이벤트 구현(Implement Event)을 선택합니다.- 이벤트 그래프에 생성된
Event ApplyDamage노드 뒤에 실제 대미지 처리 로직을 구현합니다. (예:CurrentHealth변수에서DamageAmount를 빼는 로직) BP_PlayerCharacter에도 동일하게BPI_Damageable인터페이스를 구현하고Event ApplyDamage로직을 정의합니다.
인터페이스 메시지 호출: 다른 블루프린트(예: BP_Projectile - 발사체)에서 이 인터페이스 함수를 메시지 형태로 호출합니다.
BP_Projectile블루프린트 에디터를 엽니다.Projectile이 다른 액터와 충돌했을 때 대미지를 주고 싶다고 가정합니다.Event Hit노드 뒤에Other Actor출력 핀에서 드래그하여Does Implement Interface노드를 검색하여 연결합니다.- 이 노드는 연결된 액터 레퍼런스가 특정 인터페이스를 구현했는지 불리언 값을 반환합니다.
Interface핀에BPI_Damageable을 선택합니다.
- 이 노드는 연결된 액터 레퍼런스가 특정 인터페이스를 구현했는지 불리언 값을 반환합니다.
Does Implement Interface의Return Value출력 핀을Branch노드의Condition핀에 연결합니다.- 이
Branch노드는 충돌한 액터가BPI_Damageable인터페이스를 구현했는지 여부에 따라 실행 흐름을 나눕니다.
- 이
Branch노드의True출력 핀에서 드래그하여ApplyDamage를 검색합니다. 이때,ApplyDamage (Message)노드를 선택합니다.Message노드는 대상이 인터페이스를 구현했는지 자동으로 확인하고, 구현했을 경우 해당 인터페이스 함수를 호출해줍니다.Does Implement Interface와Branch노드를 사용하지 않고 바로Message노드를 연결해도 되지만,Does Implement Interface를 통해 명시적으로 확인하는 것이 안정성 확보에 도움이 됩니다.
ApplyDamage (Message)노드의Target핀에는Event Hit의Other Actor핀을 연결합니다.DamageAmount입력 핀에는 발사체가 줄 대미지 값을 입력합니다. (예:20.0)- 블루프린트를 컴파일하고 저장합니다.
인터페이스 통신의 장점
- 강력한 다형성(Polymorphism):
ApplyDamage (Message)노드는BP_Enemy,BP_PlayerCharacter모두에 동일하게 동작합니다. 발사체 블루프린트는 대상이 적인지 플레이어인지 알 필요 없이,BPI_Damageable을 구현한 객체인지 여부만 확인하면 됩니다. 이 구조는 확장성을 크게 높여 줍니다. - 느슨한 결합(Loose Coupling): 호출하는 쪽(발사체)은 호출되는 쪽(적, 플레이어)의 구체적인 클래스 타입을 몰라도 됩니다. 오직 인터페이스만 알면 됩니다. 이는 나중에 새로운
BPI_Damageable을 구현하는 오브젝트(예:BP_DestructibleBox)가 추가되어도 발사체 블루프린트를 전혀 수정할 필요가 없다는 것을 의미합니다. - 유지보수 용이성: 특정 기능을 변경하고 싶다면, 해당 기능을 구현한 클래스만 수정하면 됩니다. 인터페이스 자체나 인터페이스를 호출하는 다른 블루프린트에는 영향을 주지 않습니다.
- 명확한 계약: 인터페이스는 이 기능을 구현하는 모든 클래스는 이 함수를 가져야 한다는 명확한 계약을 제시합니다. 이는 팀 작업 시 각 클래스의 책임과 기능을 명확히 정의하는 데 큰 도움이 됩니다.
직접 참조, 이벤트 디스패처, 인터페이스 비교
| 특징 | 직접 참조 (Direct Reference) | 이벤트 디스패처 (Event Dispatcher) | 블루프린트 인터페이스 (Blueprint Interface) |
|---|---|---|---|
| 결합도 | 강한 결합 (Tight Coupling) | 느슨한 결합 (Loose Coupling) | 느슨한 결합 (Loose Coupling) |
| 통신 방향 | 일대일 (정확한 대상 지정) | 일대다 (여러 대상에게 알림) | 다대다 (다양한 클래스에서 구현 가능, 다양한 클래스에서 호출 가능) |
| 주요 용도 | 특정 액터 인스턴스에 직접 접근, 변수 값 확인 | 특정 사건 발생 시 여러 대상에게 알림 | 특정 능력을 가진 객체에게 공통 명령 전달, 다형성 구현 |
| 안정성 | Access None 발생 가능성 높음 (유효성 검사 필수) | 바인드된 대상이 없으면 아무 일도 일어나지 않음 | 대상이 인터페이스를 구현했는지 확인 필요 |
| 복잡도 | 가장 간단 | 중간 | 중간 ~ 높음 (객체 지향 개념 이해 필요) |
인터페이스는 특히 다형성을 기반으로 하는 설계에 가장 적합한 통신 기법입니다. 게임 내에 다양한 종류의 오브젝트들이 공통적인 특정 행동(예: 상호작용, 대미지 받기, 활성화/비활성화)을 해야 할 때, 인터페이스를 사용하면 매우 유연하고 확장 가능한 시스템을 구축할 수 있습니다.
이번 절에서는 블루프린트 인터페이스를 통한 통신 방법을 심도 있게 다루었습니다. 직접 참조, 이벤트 디스패처, 그리고 인터페이스는 각각 다른 상황과 요구사항에 적합한 통신 기법이므로, 각 방법의 장단점을 이해하고 적절하게 선택하여 사용하는 것이 중요합니다.