블루프린트 인터페이스를 통한 통신
이전 절에서 이벤트 디스패처를 통해 블루프린트 간의 느슨한 결합을 구현하는 방법을 알아보았습니다. 이번 절에서는 3장에서 잠시 소개했던 블루프린트 인터페이스(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 발생 가능성 높음 (유효성 검사 필수) | 바인드된 대상이 없으면 아무 일도 일어나지 않음 | 대상이 인터페이스를 구현했는지 확인 필요 |
복잡도 | 가장 간단 | 중간 | 중간 ~ 높음 (객체 지향 개념 이해 필요) |
인터페이스는 특히 다형성을 기반으로 하는 설계에 가장 적합한 통신 기법입니다. 게임 내에 다양한 종류의 오브젝트들이 공통적인 특정 행동(예: 상호작용, 대미지 받기, 활성화/비활성화)을 해야 할 때, 인터페이스를 사용하면 매우 유연하고 확장 가능한 시스템을 구축할 수 있습니다.
이번 절에서는 블루프린트 인터페이스를 통한 통신 방법을 심도 있게 다루었습니다. 직접 참조, 이벤트 디스패처, 그리고 인터페이스는 각각 다른 상황과 요구사항에 적합한 통신 기법이므로, 각 방법의 장단점을 이해하고 적절하게 선택하여 사용하는 것이 중요합니다.