간단한 인벤토리 시스템 구현
이전 절들에서 위젯 블루프린트의 기본 개념, 동적 UI 생성, 애니메이션, 그리고 게임플레이와의 상호작용 방법을 학습했습니다. 이제 이러한 지식들을 종합하여 간단한 인벤토리 시스템을 실제로 구현해보겠습니다. 인벤토리 시스템은 UI와 게임플레이 로직이 밀접하게 연동되는 좋은 예시이며, 다양한 UI 컴포넌트와 데이터 관리 기법이 함께 사용됩니다.
인벤토리 시스템의 목표
우리가 구현할 인벤토리 시스템은 다음과 같은 기능을 가집니다.
- 플레이어가 아이템을 획득하면 인벤토리에 추가됩니다.
- 인벤토리 UI에 획득한 아이템이 동적으로 표시됩니다.
- 인벤토리 UI는 아이템의 아이콘과 이름을 보여줍니다.
- (선택 사항) 인벤토리 창을 열고 닫을 수 있습니다.
시스템 구현을 위한 준비물
이 시스템을 구현하려면 다음 블루프린트들이 필요합니다.
아이템 데이터 구조체: F_ItemData
(5장 2절에서 생성)
ItemName
(Text)ItemIcon
(Texture2D Soft Object Reference)Description
(Text)AttackPower
(Integer)Weight
(Float)
플레이어 캐릭터 블루프린트: BP_PlayerCharacter
(또는 여러분의 플레이어 캐릭터)
PlayerInventory
(변수 타입:F_ItemData
구조체의 배열 Array)
아이템 획득용 액터: BP_PickupItem
(새로 생성)
- Overlap 이벤트 발생 시
F_ItemData
를 플레이어에게 전달
인벤토리 슬롯 위젯: WBP_InventorySlot
(새로 생성)
- 개별 아이템을 표시하는 UI (아이콘, 이름)
- 아이템 데이터를 받아 자신을 업데이트하는 함수 포함
메인 인벤토리 위젯: WBP_InventoryScreen
(새로 생성)
- 인벤토리 슬롯들을 동적으로 추가할
Grid Panel
포함 - 인벤토리 데이터를 받아 슬롯을 새로고침하는 함수 포함
구현 단계별 가이드
BP_PickupItem
액터 생성
월드에 배치되어 플레이어가 주울 수 있는 아이템을 만듭니다.
액터 블루프린트 생성: BP_PickupItem
(부모 클래스: Actor
)
컴포넌트 추가
Static Mesh Component
(예:Shape_Sphere
또는 실제 아이템 메시)Sphere Collision Component
(Is Overlap Events Enabled 체크)
변수 추가
ItemInfo
(변수 타입:F_ItemData
)- 디테일 패널에서
ItemInfo
변수를 선택하고Instance Editable
및Expose on Spawn
을 체크합니다. 이렇게 하면 월드에 배치된BP_PickupItem
인스턴스마다 다른 아이템 정보를 직접 입력할 수 있습니다.
이벤트 그래프 로직
Sphere Collision
의On Component Begin Overlap
이벤트 노드를 가져옵니다.Other Actor
가BP_PlayerCharacter
인지Cast To BP_PlayerCharacter
노드로 확인합니다.- 캐스팅이 성공하면,
BP_PlayerCharacter
의AddItemToInventory
함수 (다음 단계에서 생성) 를 호출하고ItemInfo
변수(Get 노드)를 전달합니다. - 아이템을 주웠으므로,
Destroy Actor
노드를 호출하여 자신을 제거합니다. - 컴파일 및 저장합니다.
월드 배치: 메인 에디터로 돌아와 BP_PickupItem
인스턴스를 월드에 여러 개 배치하고 각 인스턴스에 다른 ItemInfo
를 입력해봅니다. (예: ItemName
"골드", ItemIcon
골드 아이콘 텍스처 등)
BP_PlayerCharacter
에 인벤토리 로직 추가
플레이어 캐릭터가 아이템을 관리하고 UI 업데이트를 지시하는 역할을 합니다.
변수 추가: PlayerInventory
(변수 타입: F_ItemData
의 배열 Array)
함수 추가: AddItemToInventory
- 입력 핀:
NewItem
(타입:F_ItemData
) - 로직
PlayerInventory
변수에서 드래그하여Add
노드를 호출합니다.NewItem
을 연결합니다.- (선택 사항)
Print String
노드를 연결하여 "아이템 [NewItem.ItemName] 획득!" 메시지를 출력합니다. Get Player Controller
노드를 가져옵니다.Get Player Controller
에서Get Widget of Class
노드를 호출합니다.Widget Class
는WBP_InventoryScreen
을 선택합니다.Get Widget of Class
의Return Value
를Cast To WBP_InventoryScreen
으로 캐스팅합니다.- 캐스팅이 성공하면,
WBP_InventoryScreen
의RefreshInventoryUI
함수 (다음 단계에서 생성) 를 호출하고,PlayerInventory
변수(Get 노드)를RefreshInventoryUI
의 입력 핀에 연결합니다.
- 컴파일 및 저장합니다.
WBP_InventorySlot
위젯 구현
인벤토리의 각 아이템을 개별적으로 표시하는 작은 위젯입니다.
위젯 블루프린트 생성: WBP_InventorySlot
(부모 클래스: User Widget
)
디자이너 탭
Size Box
를 캔버스 패널에 배치하고Width Override
,Height Override
를 100x100 등으로 설정하여 슬롯 크기를 고정합니다.Size Box
의 자식으로Border
를 배치하여 배경색을 줍니다.Border
의 자식으로Vertical Box
를 배치합니다.Vertical Box
의 자식으로Image
(이름:ItemIcon
,Is Variable
체크)와Text
(이름:ItemNameText
,Is Variable
체크)를 배치합니다.Image
와Text
의 정렬 및 패딩을 조절하여 아이콘과 텍스트가 잘 보이도록 합니다.
그래프 탭
- 함수 추가:
UpdateSlotInfo
- 입력 핀:
ItemData
(타입:F_ItemData
) - 로직
ItemData
(입력 핀)에서Break F_ItemData
노드를 가져옵니다.ItemNameText
변수(Set 노드)의Text
핀에ItemName
(Break F_ItemData의 출력)을 연결합니다.ItemIcon
변수(Set Node)의Brush
핀에Make Brush From Texture
노드를 연결합니다.Make Brush From Texture
의Texture
핀에ItemIcon
(Break F_ItemData의Soft Texture2D Object Reference
출력)을 직접 연결합니다. (언리얼 엔진이 소프트 레퍼런스를 자동으로 로드하여 사용합니다. 명시적Load Asset
노드가 필요 없는 경우도 있지만, 안전하게는Load Asset
이후 연결하는 것이 좋습니다.)- 컴파일 및 저장합니다.
- 입력 핀:
WBP_InventoryScreen
위젯 구현
모든 인벤토리 슬롯을 담고 관리하는 메인 인벤토리 UI입니다.
위젯 블루프린트 생성: WBP_InventoryScreen
(부모 클래스: User Widget
)
디자이너 탭
Canvas Panel
에Border
를 배치하여 인벤토리 창의 배경을 만듭니다. (크기와 색상 조절)Border
의 자식으로Grid Panel
을 배치하고 이름을InventoryGrid
로 지정한 후Is Variable
을 체크합니다. (이 패널에 슬롯 위젯들이 추가됩니다.)Grid Panel
의Slot
설정에서Column Fill
또는Row Fill
을 조절하여 그리드 크기를 설정할 수 있습니다.
그래프 탭
- 함수 추가:
RefreshInventoryUI
- 입력 핀:
CurrentInventory
(타입:F_ItemData
의 배열 Array) - 로직
InventoryGrid
변수(Get 노드)에서 드래그하여Clear Children
노드를 호출합니다. (기존 슬롯들을 모두 제거하여 새로 그립니다.)CurrentInventory
(입력 핀)에서 드래그하여For Each Loop
노드를 가져옵니다.Loop Body
핀 뒤에:Create Widget
노드를 호출합니다.Widget Class
는WBP_InventorySlot
을 선택합니다.Owning Player
는Get Owning Player
를 연결합니다.Create Widget
의Return Value
를Cast To WBP_InventorySlot
으로 캐스팅합니다.- 캐스팅이 성공하면,
WBP_InventorySlot
의UpdateSlotInfo
함수를 호출하고For Each Loop
의Array Element
(현재 아이템 데이터)를 연결합니다. InventoryGrid
변수(Get 노드)에서 드래그하여Add Child to Grid
노드를 호출합니다.Target
은InventoryGrid
를 연결합니다.Content
는Cast To WBP_InventorySlot
의As WBP Inventory Slot
출력 핀을 연결합니다.Row
와Column
핀은For Each Loop
의Index
를 활용하여 계산합니다. (예: 5칸짜리 그리드라면Column = Index % 5
,Row = Index / 5
와 같이 계산)
- 컴파일 및 저장합니다.
- 입력 핀:
메인 게임 컨트롤러에서 인벤토리 띄우기
플레이어 컨트롤러에서 인벤토리 창을 관리합니다.
블루프린트 열기: BP_PlayerController
변수 추가: InventoryScreenRef
(타입: WBP_InventoryScreen Object Reference
)
이벤트 그래프 로직
Event BeginPlay
뒤에:Create Widget
노드로WBP_InventoryScreen
을 생성합니다.Owning Player
는Self
(Player Controller 자신)를 연결합니다.Create Widget
의Return Value
를Set InventoryScreenRef
변수에 연결하여 참조를 저장해둡니다.- 컴파일 및 저장합니다. (초기에는 화면에 추가하지 않습니다.)
Event I
(또는 인벤토리 키) 입력 노드를 추가합니다.InventoryScreenRef
변수(Get 노드)에서Is Valid
노드를 연결하여 위젯이 유효한지 확인합니다.Branch
노드(Is Valid
의 출력)의True
핀 뒤에:InventoryScreenRef
에서Is Visible
노드를 호출하여 현재 보이는지 확인합니다.Branch
노드(Is Visible
의 출력)로 분기합니다.True
(현재 보임): 인벤토리 닫기 로직InventoryScreenRef
에서Remove From Parent
노드를 호출하여 화면에서 제거합니다.Set Input Mode Game Only
노드를 호출합니다.Set Show Mouse Cursor
노드를 호출하고bShowMouseCursor
를 해제합니다.
False
(현재 숨겨짐): 인벤토리 열기 로직InventoryScreenRef
에서Add to Viewport
노드를 호출하여 화면에 추가합니다.Set Input Mode UI Only
노드를 호출합니다.Set Show Mouse Cursor
노드를 호출하고bShowMouseCursor
를 체크합니다.InventoryScreenRef
에서RefreshInventoryUI
함수를 호출하고,BP_PlayerCharacter
의PlayerInventory
변수를 가져와 연결합니다. (플레이어 캐릭터의 인벤토리 데이터를 UI에 전달)
테스트
모든 블루프린트를 컴파일하고 저장합니다.
메인 에디터의 월드에 BP_PlayerCharacter
와 여러 BP_PickupItem
인스턴스들을 배치합니다.
게임을 플레이합니다.
BP_PickupItem
에 겹치면 아이템을 획득하고 메시지가 출력되는 것을 확인합니다.
I
키를 누르면 인벤토리 창이 나타나고, 획득한 아이템들이 그리드에 동적으로 표시되는 것을 확인합니다.
I
키를 다시 누르면 인벤토리 창이 닫히고 마우스 커서가 사라지는 것을 확인합니다.
이로써 간단한 인벤토리 시스템 구현이 완료되었습니다! 이 예시는 UI, 데이터 관리, 블루프린트 통신 등 이전에 학습했던 여러 개념들이 어떻게 실제 게임 시스템에서 함께 작동하는지 보여주는 좋은 예시입니다.
이 인벤토리 시스템은 단순히 기본 구현이며, 여기에 아이템 사용, 버리기, 드래그 앤 드롭, 아이템 툴팁, 스택 시스템 등 다양한 기능을 추가하여 더욱 풍부하게 만들 수 있습니다.