게임에서 상자 시스템은 보상을 통해 유저들에게 재미와 긴장감을 제공하며, 게임 내 아이템을 무작위로 획득하는 기회를 제공합니다. 또한 유저는 상자에 아이템을 보관하는 기능을 이용할 수 있습니다. 상자 시스템을 구현하고 이를 정리하였습니다.
📺 미리보기
💬 목차
- 총 두 개의 목차로 구성되어 있습니다.
- 글 별로 아직 언급되지 않은 클래스의 호출이 있을 수 있습니다. 이 사항은 모든 글을 참조하면 충분히 이해할 수 있습니다.
[📌현재 글] 2. [유니티] 상자 시스템(2) - 상자 다이얼로그
📖 구현 내용
- 월드에 배치되어있는 상자를 바라보고 키를 누르면 상자가 열립니다.
- 플레이어에게 아이템을 지급하기위한 목적으로 상자를 처음 열을경우 설정에 따라 아이템이 무작위로 생성됩니다.
- 아이템이 생성될 때 아이템이 생성되는 위치 또한 무작위로 설정됩니다.
- 처음으로 상자를 여는것이 아닌경우 저장된 아이템이 유지되며 해당 아이템을 꺼낼 수 있습니다.
- 플레이어의 아이템을 상자에 집어넣어 보관할 수 있습니다.
- 상자의 데이터가 저장되어 게임을 세이브 및 로드를 할 때 해당 사항이 저장됩니다.
- 상자를 열 때 UI의 영역의 크기를 다르게 설정할 수 있습니다. 예) 2x3 크기, 5x5크기 등...
✅ 구현
- 이번 글에서는 월드에 배치할 상자를 이용할 수 있도록 다이얼로그 창을 제공하는 기능을 다룹니다.
· ChestDialogManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace ChestSystem
{
public class ChestDialogManager : Singleton<ChestDialogManager>
{
private static bool mIsDialogActive = false;
public static bool IsDialogActive
{
get
{
return mIsDialogActive;
}
}
[Header("다이얼로그 최상위 게임오브젝트")]
[SerializeField] private GameObject mDialogGo;
[Header("다이얼로그의 그리드 레이아웃 컴포넌트")]
[SerializeField] private GridLayoutGroup mGridLayout;
[Header("인벤토리 슬롯 오브젝트 프리팹")]
[SerializeField] private GameObject mSlotPrefab;
private RectTransform mDialogRectTransform;
private ChestController mCurrentChest; // 현재 다이얼로그박스에 표시된 상자의 정보
private void Awake()
{
// 초기화시 전역 활성화상태 해제
ChestDialogManager.mIsDialogActive = false;
mDialogRectTransform = mDialogGo.GetComponent<RectTransform>();
}
private void Update()
{
// 다이얼로그가 활성 상태에서 ESC를 누르면 ?
if (mIsDialogActive && Input.GetKeyDown(KeyCode.Escape))
CloseDialog();
}
public void TryOpenDialog(ChestController chestController)
{
// 이미 다이얼로그가 활성화 되어있다면?
if(mIsDialogActive)
return;
// 현재 정보를 등록
mCurrentChest = chestController;
// 다이얼로그 크기 및 슬롯 초기화
InitDialog(mCurrentChest.ChestInfo.row, mCurrentChest.ChestInfo.col);
foreach (ChestSlotItem slotItem in mCurrentChest.ChestInfo.chestSlotItems)
{
InventorySlot slot = mGridLayout.transform.GetChild(slotItem.itemPositionIndex).GetComponent<InventorySlot>();
InventoryMain.Instance.AcquireItem(slotItem.itemCode, slot, slotItem.itemCount);
}
mDialogGo.SetActive(true);
mIsDialogActive = true;
UtilityManager.UnlockCursor();
mCurrentChest.PlayChestAnim(true);
}
public void CloseDialog()
{
// 현재 상자 인벤토리의 모든 슬롯을 가져옴
InventorySlot[] inventorySlotItems = mGridLayout.transform.GetComponentsInChildren<InventorySlot>(false);
// 아이템 슬롯 데이터 리스트를 를 생성후 개수가 0개 이상의 아이템들만 갱신
List<ChestSlotItem> slotItems = new List<ChestSlotItem>();
for (int i = 0; i < inventorySlotItems.Length; ++i)
if (inventorySlotItems[i].ItemCount > 0)
slotItems.Add(new ChestSlotItem()
{
itemCode = inventorySlotItems[i].Item.ID,
itemCount = inventorySlotItems[i].ItemCount,
itemPositionIndex = i,
});
// 슬롯 아이템의 정보를 대치
mCurrentChest.ChestInfo.chestSlotItems.Clear();
mCurrentChest.ChestInfo.chestSlotItems = slotItems;
// 오브젝트 비활성화
mDialogGo.SetActive(false);
mIsDialogActive = false;
// 마우스 잠금
UtilityManager.TryLockCursor();
// 아이템 설명 비활성화
ItemDescription.Instance.DisableToolTip();
mCurrentChest.PlayChestAnim(false);
}
/// <summary>
/// 다이얼로그를 초기화하고 인벤토리 슬롯과 크기를 자동으로 조정
/// </summary>
private void InitDialog(int row, int col)
{
// 현재 자식(인벤토리 슬롯) 개수 구하기
int currentSlotCount = mGridLayout.transform.childCount;
// 부족한 인벤토리 슬롯 인스턴스
for (int i = currentSlotCount; i < row * col; ++i)
Instantiate(mSlotPrefab, Vector3.zero, Quaternion.identity, mGridLayout.transform);
// 첫번째 인덱스부터 총 개수까지 슬롯 활성화 및 슬롯 초기화
for (int i = 0; i < row * col; ++i)
{
InventorySlot slot = mGridLayout.transform.GetChild(i).GetComponent<InventorySlot>();
slot.ClearSlot();
slot.gameObject.SetActive(true);
}
// 초과하는 인벤토리 슬롯 비활성화
for (int i = row * col; i < currentSlotCount; ++i)
mGridLayout.transform.GetChild(i).gameObject.SetActive(false);
// 셀 한개의 사이즈 구하기
float cellWidth = mGridLayout.cellSize.x + mGridLayout.spacing.x;
float cellHeight = mGridLayout.cellSize.y + mGridLayout.spacing.y;
// 트랜스폼의 가로, 세로 길이 계산 및 적용
float width = row * cellWidth - mGridLayout.spacing.x;
float height = col * cellHeight - mGridLayout.spacing.y;
mDialogRectTransform.sizeDelta = new Vector2(width + mGridLayout.padding.left + mGridLayout.padding.right, height + mGridLayout.padding.top + mGridLayout.padding.bottom);
}
}
}
private static bool mIsDialogActive = false;
public static bool IsDialogActive
{
get
{
return mIsDialogActive;
}
}
- 현재 상자 다이얼로그 창이 열려있는지 확인하기위한 변수입니다.
- 상자 다이얼로그 창이 열려있을 때 또 다른 창 열기를 시도할 때 중복으로 열리는 상황을 방지하고자 사용할 수 있습니다.
[Header("다이얼로그 최상위 게임오브젝트")]
[SerializeField] private GameObject mDialogGo;
- 다이얼로그 창을 토글하기위한 오브젝트입니다.
[Header("다이얼로그의 그리드 레이아웃 컴포넌트")]
[SerializeField] private GridLayoutGroup mGridLayout;
- 상자의 가로 세로 크기를 기반으로 크기를 재조정하기위한 그리드 레이아웃 컴포넌트입니다.
[Header("인벤토리 슬롯 오브젝트 프리팹")]
[SerializeField] private GameObject mSlotPrefab;
- 상자 다이얼로그 창을 인벤토리슬롯으로 채우기위해 인스턴스를 하기 위한 프리팹입니다.
- 해당 프리팹은 InventorySlot을 사용하며 "[유니티] 인벤토리 시스템(3) - 아이템 슬롯(인벤토리 슬롯)"에서 이 내용을 다룹니다.
private RectTransform mDialogRectTransform;
- 다이얼로그 창의 크기를 조절하기위한 RectTransform입니다.
public void TryOpenDialog(ChestController chestController)
{
// 이미 다이얼로그가 활성화 되어있다면?
if(mIsDialogActive)
return;
// 현재 정보를 등록
mCurrentChest = chestController;
// 다이얼로그 크기 및 슬롯 초기화
InitDialog(mCurrentChest.ChestInfo.row, mCurrentChest.ChestInfo.col);
foreach (ChestSlotItem slotItem in mCurrentChest.ChestInfo.chestSlotItems)
{
InventorySlot slot = mGridLayout.transform.GetChild(slotItem.itemPositionIndex).GetComponent<InventorySlot>();
InventoryMain.Instance.AcquireItem(slotItem.itemCode, slot, slotItem.itemCount);
}
mDialogGo.SetActive(true);
mIsDialogActive = true;
UtilityManager.UnlockCursor();
mCurrentChest.PlayChestAnim(true);
}
- 다이얼로그 창 열기를 시도합니다.
- 만약 이미 창이 열려있다면 리턴하여 창을 열지 않도록 합니다.
- 현재 설정된 인벤토리 슬롯들에게 아이템을 설정하여 상자의 아이템들을 보여줍니다.
- InitDialog를 이용하여 다이얼로그 창 구성을 초기화합니다.
/// <summary>
/// 다이얼로그를 초기화하고 인벤토리 슬롯과 크기를 자동으로 조정
/// </summary>
private void InitDialog(int row, int col)
{
// 현재 자식(인벤토리 슬롯) 개수 구하기
int currentSlotCount = mGridLayout.transform.childCount;
// 부족한 인벤토리 슬롯 인스턴스
for (int i = currentSlotCount; i < row * col; ++i)
Instantiate(mSlotPrefab, Vector3.zero, Quaternion.identity, mGridLayout.transform);
// 첫번째 인덱스부터 총 개수까지 슬롯 활성화 및 슬롯 초기화
for (int i = 0; i < row * col; ++i)
{
InventorySlot slot = mGridLayout.transform.GetChild(i).GetComponent<InventorySlot>();
slot.ClearSlot();
slot.gameObject.SetActive(true);
}
// 초과하는 인벤토리 슬롯 비활성화
for (int i = row * col; i < currentSlotCount; ++i)
mGridLayout.transform.GetChild(i).gameObject.SetActive(false);
// 셀 한개의 사이즈 구하기
float cellWidth = mGridLayout.cellSize.x + mGridLayout.spacing.x;
float cellHeight = mGridLayout.cellSize.y + mGridLayout.spacing.y;
// 트랜스폼의 가로, 세로 길이 계산 및 적용
float width = row * cellWidth - mGridLayout.spacing.x;
float height = col * cellHeight - mGridLayout.spacing.y;
mDialogRectTransform.sizeDelta = new Vector2(width + mGridLayout.padding.left + mGridLayout.padding.right, height + mGridLayout.padding.top + mGridLayout.padding.bottom);
}
- 상자 정보에서 row, col을 기반으로 상자 다이얼로그 창의 크기와 그리드 레이아웃의 행과 열의 수를 재구성합니다.
- 인벤토리 슬롯이 부족한경우 인스턴스를 하여 슬롯을 초기화해줍니다.
- 만약 이미 슬롯들이 충분히 있다면 해당 슬롯을 재사용합니다. 슬롯이 초과되었다면 사용할 슬롯을 제외하고 나머지 슬롯들을 비활성화하여 그리드 레이아웃에서 보이지 않도록 설정합니다.
public void CloseDialog()
{
// 현재 상자 인벤토리의 모든 슬롯을 가져옴
InventorySlot[] inventorySlotItems = mGridLayout.transform.GetComponentsInChildren<InventorySlot>(false);
// 아이템 슬롯 데이터 리스트를 를 생성후 개수가 0개 이상의 아이템들만 갱신
List<ChestSlotItem> slotItems = new List<ChestSlotItem>();
for (int i = 0; i < inventorySlotItems.Length; ++i)
if (inventorySlotItems[i].ItemCount > 0)
slotItems.Add(new ChestSlotItem()
{
itemCode = inventorySlotItems[i].Item.ID,
itemCount = inventorySlotItems[i].ItemCount,
itemPositionIndex = i,
});
// 슬롯 아이템의 정보를 대치
mCurrentChest.ChestInfo.chestSlotItems.Clear();
mCurrentChest.ChestInfo.chestSlotItems = slotItems;
// 오브젝트 비활성화
mDialogGo.SetActive(false);
mIsDialogActive = false;
// 마우스 잠금
UtilityManager.TryLockCursor();
// 아이템 설명 비활성화
ItemDescription.Instance.DisableToolTip();
mCurrentChest.PlayChestAnim(false);
}
- 상자 다이얼로그 창을 닫을경우 상자 슬롯의 아이템들을 확인하여 현재 아이템들의 상태를 저장합니다.
· UI
- 인벤토리 슬롯과 비슷한 디자인으로 구성하였습니다.
- 하지만 이 UI의 크기는 상자의 설정에 따라 유동적으로 변합니다.
- 그리드 레이아웃의 하나의 인벤토리 슬롯을 넣어두고 이를 복사하여 사용할 수 있도록 합니다.
'unity game modules' 카테고리의 다른 글
[유니티] 게임 저장 시스템(2) - 저장 및 불러오기 예시 (0) | 2023.04.02 |
---|---|
[유니티] 게임 저장 시스템(1) - 슬롯 구성 (0) | 2023.04.02 |
[유니티] 상자 시스템(1) - 상자 데이터 (0) | 2023.04.02 |
[유니티] 제작 시스템(4) - 매니저 (0) | 2023.04.02 |
[유니티] 제작 시스템(3) - 제작소 (0) | 2023.04.02 |