게임에서 상점 시스템은 플레이어들이 게임 내 아이템이나 장비를 구매하고 업그레이드하는 방법을 제공하여 게임의 새로운 경험과 도전을 제공합니다. 상점 시스템을 구현하여 플레이어가 아이템을 구매할 수 있도록 하였습니다.
💬 목차
- 총 세개의 목차로 구성되어 있습니다. 글 별로 아직 언급되지 않은 클래스의 호출이 있을 수 있습니다. 모든 글을 참조하면 충분히 이해할 수 있습니다.
- 이번 글에서는 아이템 상점을 구현하여 직전 글에서 다뤘던 아이템 상점 슬롯을 활용하여 구현해보겠습니다.
- 이번 글에서는 상점 데이터를 저장하고 불러오는 기능 함수 또한 함께 포함되어있지만, 세이브 및 로드 시스템은 추후에 다루겠습니다.
1. [유니티] 상점 시스템(1) - 상점 슬롯
[📌현재 글] 2. [유니티] 상점 시스템(2) - 상점
3. [유니티] 상점 시스템(3) - 상점 매니저
📖 구현 내용
- 플레이어는 화폐를 가지고 해당 아이템을 구매할 수 있습니다.
- 에디터에서 상점에서 판매하는 아이템을 미리 등록합니다.
- 각 상점별로 상점 진척도 레벨을 이용하여 구매할 수 있는 아이템을 제한할 수 있습니다.
- 각 상점별로 판매하는 아이템은 재고가 있어 재고를 모두 소진할경우 해당 아이템을 더 이상 구매할 수 없습니다.
- 상점의 재고 및 진척도 레벨은 저장되어 나중에 게임을 로드하면 현 상태를 불러옵니다.
✅ 구현
- 이번 글의 목적은 아이템 상점을 구현하여 아이템 상점 슬롯을 활용하여 구현하는것을 다룹니다.
· ItemShop.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using GameSave;
public class ItemShop : MonoBehaviour
{
[field: Header("아이템 상점의 고유번호")]
[field: SerializeField] public int ShopId { private set; get; } = 0;
[Header("상점에서 판매할 아이템")]
[SerializeField] public ItemShopSlotInfo[] mSellItemInfos;
[HideInInspector] public int ShopLevel = 0; // 상점의 아이템 판매 단계
private void Awake()
{
// 인스턴스로 생성하여 삽입
List<ItemShopSlotInfo> itemShopSlotInfos = new List<ItemShopSlotInfo>();
foreach(ItemShopSlotInfo shotSlotInfo in mSellItemInfos)
itemShopSlotInfos.Add(new ItemShopSlotInfo(shotSlotInfo));
// 배열 재구성
mSellItemInfos = itemShopSlotInfos.ToArray();
}
/// <summary>
/// 게임이 로드되는경우 상점 정보를 불러옴
/// </summary>
/// <param name="shopInfo">불러올 상점 정보</param>
public void LoadFromData(ShopInfo shopInfo)
{
for(int i = 0; i < mSellItemInfos.Length; ++i)
mSellItemInfos[i].ItemAmount = shopInfo.sellItemInfos[i].itemAmount;
ShopLevel = shopInfo.shopLevel;
}
/// <summary>
/// 게임을 저장하는경우 상점의 정보를 내보냄
/// </summary>
/// <returns>저장하고자 하는 상점의 정보</returns>
public ShopInfo GetShopGameData()
{
ShopInfo shopInfo = new ShopInfo();
shopInfo.shopId = this.ShopId;
shopInfo.shopLevel = this.ShopLevel;
shopInfo.sellItemInfos = mSellItemInfos
.Select(sellItemInfo =>
new SellItemInfo
{
itemAmount = sellItemInfo.ItemAmount,
sellItemEntityCode = (int)sellItemInfo.SellItem.ID
}).ToArray();
return shopInfo;
}
}
[field: Header("아이템 상점의 고유번호")]
[field: SerializeField] public int ShopId { private set; get; } = 0;
- 상점의 고유 번호입니다.
- 해당 상점의 번호를 이용하여 세이브 및 로드를 할 때 식별하여 정확한 상점에 로드 및 세이브를 할 수 있습니다.
[Header("상점에서 판매할 아이템")]
[SerializeField] public ItemShopSlotInfo[] mSellItemInfos;
- 상점에서 판매할 아이템을 인스펙터에서 정의할 수 있습니다.
- ItemShopSlotInfo는 판매할 아이템 하나의 정보를 정의하며 배열로 선언되어 여러개의 아이템을 한번에 판매할 수 있습니다.
[HideInInspector] public int ShopLevel = 0; // 상점의 아이템 판매 단계
- 상점의 진척도 레벨을 정의합니다.
- 이 값에 따라 플레이어가 상점에서 구매할 수 있는 아이템이 제한됩니다.
- 외부에서 이 값을 변경하여 진척도를 바꿀 수 있습니다.
private void Awake()
{
// 인스턴스로 생성하여 삽입
List<ItemShopSlotInfo> itemShopSlotInfos = new List<ItemShopSlotInfo>();
foreach(ItemShopSlotInfo shotSlotInfo in mSellItemInfos)
itemShopSlotInfos.Add(new ItemShopSlotInfo(shotSlotInfo));
// 배열 재구성
mSellItemInfos = itemShopSlotInfos.ToArray();
}
- 게임 세이브 및 로드에 상관 없이 씬이 불러와지면 인스펙터에서 정의한 아이템 판매 데이터들을 초기화하여 판매 준비를 합니다.
/// <summary>
/// 게임이 로드되는경우 상점 정보를 불러옴
/// </summary>
/// <param name="shopInfo">불러올 상점 정보</param>
public void LoadFromData(ShopInfo shopInfo)
{
for(int i = 0; i < mSellItemInfos.Length; ++i)
mSellItemInfos[i].ItemAmount = shopInfo.sellItemInfos[i].itemAmount;
ShopLevel = shopInfo.shopLevel;
}
- 씬이 이전에 저장되어 불러와진경우 해당 아이템 상점이 불러와진 데이터를 대상으로 기존의 데이터들을 재구성합니다.
- 재고 및 아이템 상점의 진척도 등을 갱신합니다.
/// <summary>
/// 게임을 저장하는경우 상점의 정보를 내보냄
/// </summary>
/// <returns>저장하고자 하는 상점의 정보</returns>
public ShopInfo GetShopGameData()
{
ShopInfo shopInfo = new ShopInfo();
shopInfo.shopId = this.ShopId;
shopInfo.shopLevel = this.ShopLevel;
shopInfo.sellItemInfos = mSellItemInfos
.Select(sellItemInfo =>
new SellItemInfo
{
itemAmount = sellItemInfo.ItemAmount,
sellItemEntityCode = (int)sellItemInfo.SellItem.ID
}).ToArray();
return shopInfo;
}
- 게임을 저장하기위해 현재 상점 데이터 정보를 필요한 데이터만 추출하여 세이브 및 로드 매니저에 리턴합니다.
- 상점의 세이브 및 로드를 위한 ShopGameDataManager에서 사용하는 데이터 구조는 다음과 같습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Quest;
namespace GameSave
{
...
#region ShopGameDataManager
[Serializable]
public struct SellItemInfo // 아이템 슬롯 한개의 판매 정보
{
public int sellItemEntityCode; // 판매 아이템의 아이템코드
public int itemAmount; // 아이템의 남은 총 개수(재고량)
}
[Serializable]
public struct ShopInfo
{
public int shopId; // 상점의 고유 ID
public int shopLevel; // 현재 상점의 판매 단계
public SellItemInfo[] sellItemInfos; // 상점에서 판매중인 아이템들의 정보
}
[Serializable]
public struct ShopGameData
{
public ShopInfo[] shopInfos;
}
#endregion
}
- 게임을 저장하고 로드하는 시스템은 추후에 다루겠습니다.
✅ 사용
· 판매할 아이템 정의
- 아이템 상점을 사용할 대상에게 ItemShop 컴포넌트를 사용하도록 하고 판매할 아이템을 정의하도록 합니다.
- 여러개의 아이템을 판매하도록 설정하며 판매할 아이템인 Sell Item은 인벤토리 시스템에서 사용하는 아이템들을 다룹니다.
- Sell Item은 "[유니티] 인벤토리 시스템(2) - 아이템(ScriptableObject)"에서 다룹니다.
· 상점 열기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using NPC.State;
[RequireComponent(typeof(AnimatorBlender))]
public class NPCBlacksmith : NPCBase
{
...
[field: Space(30)]
[field: Header("[Blacksmith]")]
[Header("아이템 상점 컴포넌트")]
[SerializeField] private ItemShop mItemShop;
...
public override void ExecQuoteSelectedBehavior(int selectedQuoteID)
{
switch (selectedQuoteID)
{
case 3:
ItemShopManager.Instance.OpenItemShop(mItemShop.mSellItemInfos, mItemShop.ShopLevel);
break;
case 4:
QuoteManager.Instance.DisplayQuoteOverlay(12, this);
break;
}
}
...
}
- "[유니티] 대화 시스템(1) - Overlay Canvas"에서 사용하는 기능을 이용하여 상점을 이용하고싶다고 한 경우 아이템 상점을 열 수 있습니다.
- 대화 시스템은 열기위한 수단 중 하나일 뿐 아래의 함수가 상점을 여는 핵심 코드입니다.
ItemShopManager.Instance.OpenItemShop(mItemShop.mSellItemInfos, mItemShop.ShopLevel);
- 상점 매니저를 이용하여 아이템 상점을 열도록 합니다.
- 전달하는 인자는 열고자하는 아이템 상점의 판매 아이템 정보들과 현재 상점의 진척도 레벨입니다.
- 상점 매니저는 다음 글에서 다루겠습니다.
'unity game modules' 카테고리의 다른 글
[유니티] 제작 시스템(1) - 레시피 (0) | 2023.04.02 |
---|---|
[유니티] 상점 시스템(3) - 상점 매니저 (0) | 2023.04.02 |
[유니티] 상점 시스템(1) - 상점 슬롯 (0) | 2023.04.02 |
[유니티] 경사면 미끄러짐 (0) | 2023.03.27 |
[유니티] 대화 시스템(3) - 말풍선 (1) | 2023.03.27 |