
작물을 팔 때 마다 가격을 달라질 수 있게 하는 단순한 시세변동 시스템을 추가해봤다.
우선 해당 기능을 추가하고자 한 취지는, 유저가 수익성이 가장 높은 작물 하나만 기르는 상황을 막고자 함이었다.
따라서 기본적으로 유저가 아이템을 판매 하면 물건의 가격을 내리되, 날이 지날 때마다 랜덤으로도 해당 작물의 가격을 변동되도록 해봤다.
이를 위해 추가한 클래스는 다음과 같은 두 클래스이다.
public class CropPriceData
{
public int cropCode;
public int cropStockQuantity;
public float priceModifier;
private int _defaultStockQuantity = 100;
private float _defaultPriceModifier = 1f;
private int _minStockQuantity;
private int _maxStockQuantity;
public CropPriceData(CropsSO cropsSO)
{
cropCode = cropsSO.itemCode;
cropStockQuantity = _defaultStockQuantity;
priceModifier = _defaultPriceModifier;
_minStockQuantity = cropStockQuantity / 2;
_maxStockQuantity = cropStockQuantity * 2;
}
public void AddStockQuantity(int p_value)
{
cropStockQuantity -= p_value;
cropStockQuantity = Mathf.Clamp(cropStockQuantity, _minStockQuantity, _maxStockQuantity);
}
public float SetPriceModifier()
{
float modifier = ((float)cropStockQuantity / (float)_defaultStockQuantity);
return Mathf.Clamp(modifier, 0.5f, 2.0f);
}
}
CropPriceData 클래스는 농작물의 재고와 가격 변동 데이터를 관리하는 클래스이다.
AddStockQuantity(int)메서드를 통해 판매하거나, 자동으로 변동이 있을 때, cropStockQuantity값을 수정할 수 있다.
이때 Clamp메서드를 이용해 최대치와 최소치를 설정할 수 있도록 했다.
SetPriceModifier()메서드에서 재고량에 따라 가격변동 변수를 제어할 수 있도록 했다.
public class DynamicPricingSystem
{
private Dictionary<CropsSO, CropPriceData> _cropPriceDictionary = new Dictionary<CropsSO, CropPriceData>();
public DynamicPricingSystem(ItemDatabase p_itemDatabase)
{
List<ItemSO> itemSOs = p_itemDatabase.GetAllEntries();
for (int i = 0; i < itemSOs.Count; i++)
{
if (itemSOs[i] is CropsSO crop)
{
_cropPriceDictionary.Add(crop, new CropPriceData(crop));
}
}
}
public List<CropPriceData> SavePriceDatas()
{
List<CropPriceData> cropPriceDatas = new List<CropPriceData>();
foreach(var (crop, price) in _cropPriceDictionary)
{
cropPriceDatas.Add(price);
}
return cropPriceDatas;
}
public void LoadPriceDatas(List<CropPriceData> p_cropPriceData)
{
for (int i = 0; i < p_cropPriceData.Count; i++)
{
LoadPriceData((CropsSO)ItemCodeMapper.GetItemSo(p_cropPriceData[i].cropCode), p_cropPriceData[i]);
}
}
private void LoadPriceData(CropsSO p_cropsSO, CropPriceData p_cropPriceData)
{
_cropPriceDictionary[p_cropsSO].cropCode = p_cropPriceData.cropCode;
_cropPriceDictionary[p_cropsSO].cropStockQuantity = p_cropPriceData.cropStockQuantity;
_cropPriceDictionary[p_cropsSO].priceModifier = p_cropPriceData.priceModifier;
}
private void ModifyStocks()
{
foreach (var (crop, value) in _cropPriceDictionary)
{
ModifyStock(value);
}
}
private void ModifyStock(CropPriceData p_cropData)
{
// 싼 경우
if (p_cropData.priceModifier < 1f)
{
p_cropData.AddStockQuantity(Random.Range(-10, 5));
}
// 비싼 경우
else if (p_cropData.priceModifier >= 1f)
{
p_cropData.AddStockQuantity(Random.Range(-20, 5));
}
}
public void UpdatePrices()
{
ModifyStocks();
foreach (var (crop, value) in _cropPriceDictionary)
{
value.priceModifier = value.SetPriceModifier();
}
}
public void SellCrop(CropsSO p_cropSO, int p_amount)
{
if (!_cropPriceDictionary.ContainsKey(p_cropSO)) return;
_cropPriceDictionary[p_cropSO].AddStockQuantity(p_amount);
}
public float GetSellPriceModifier(ItemSO p_itemSO)
{
if (p_itemSO is CropsSO crop)
{
return _cropPriceDictionary[crop].priceModifier;
}
return 1;
}
}
DynamicPricingSystem 클래스는 전체 농작물의 가격 데이터를 관리하고 업데이트하는 클래스이다.
UpdatePrices() 메서드를 통해 시간이 지나면서 생기는 랜덤한 재고 변화를 구현하고, 가격 변동 변수를 업데이트할 수 있다.
SavePriceDatas() 와 LoadPriceDatas(List<CropPriceData>)를 통해 진행된 시세에 대한 데이터를 저장하고 불러올 수 있도록 했다.
가격 변동을 구현하는 가장 핵심적인 메서드는 SetPriceModifier()이다.
public float SetPriceModifier()
{
float modifier = ((float)cropStockQuantity / (float)_defaultStockQuantity);
return Mathf.Clamp(modifier, 0.5f, 2.0f);
}
재고 값에 따라 최소 0.5에서 2.0 사이의 값을 유지하도록 했다.
왜냐하면 지나치게 극단적인 가격이 될 경우, 그 가격을 정상적인 수치로 되돌리기 힘들 수 있다는 판단 때문이었다.
재고 값을 설정할 때, Clamp를 이용해 최대치와 최소치를 설정하기는 했지만, 혹시라도 예상을 벗어나는 상황을 위해, 이 메서드에도 Clamp메서드를 사용했다.
아무리 게임 내에서 시세 시스템을 구현해도, 그것이 게임을 다시 켤 때마다 초기 상태로 돌아간다면 쓸모가 없다.
이를 위해 저장, 로드 기능 또한, 구현할 필요가 있었다.
public List<CropPriceData> SavePriceDatas()
{
List<CropPriceData> cropPriceDatas = new List<CropPriceData>();
foreach(var (crop, price) in _cropPriceDictionary)
{
cropPriceDatas.Add(price);
}
return cropPriceDatas;
}
public void LoadPriceDatas(List<CropPriceData> p_cropPriceData)
{
for (int i = 0; i < p_cropPriceData.Count; i++)
{
if (ItemCodeMapper.GetItemSo(p_cropPriceData[i].cropCode) is CropsSO cropsSO)
LoadPriceData(cropsSO, p_cropPriceData[i]);
}
}
private void LoadPriceData(CropsSO p_cropsSO, CropPriceData p_cropPriceData)
{
_cropPriceDictionary[p_cropsSO].cropCode = p_cropPriceData.cropCode;
_cropPriceDictionary[p_cropsSO].cropStockQuantity = p_cropPriceData.cropStockQuantity;
_cropPriceDictionary[p_cropsSO].priceModifier = p_cropPriceData.priceModifier;
}

저장할 때는 저장된 _cropPriceDictionary에서 리스트를 생성하여 저장을 위한 json 파일에 저장할 수 있도록 했다.
로드할 때도, 해당 리스트를 읽어오면서, cropCode를 통해, 기존의 아이템 인터페이스에서 일치하는 CropCode를 찾고, 해당 정보로 다시 덮어 씌우는 기능으로 로드 시스템을 구현했다.
아이템 데이터 베이스에 작물 SO가 있다면, 해당 작물을 다시 _cropPriceDictionary에 추가한다.
private void ModifyStock(CropPriceData p_cropData)
{
// 싼 경우
if (p_cropData.priceModifier < 1f)
{
p_cropData.AddStockQuantity(Random.Range(-10, 5));
}
// 비싼 경우
else if (p_cropData.priceModifier >= 1f)
{
p_cropData.AddStockQuantity(Random.Range(-20, 5));
}
}
ModifyStock(CropPriceData)는 가격 변동 치에 따라 다르게 구현하도록 구현했다.
그리고 고정된 값이 아닌 랜덤된 값이 바뀌도록 하여, 조금이라도 생생한(?) 느낌이 날 수 있도록 했다.
현재는 이 부분이 지나치게 간소화하여 구현되어 있는 것 같아, 만약 수정한다면, 여러 작물들 간의 값을 기반으로 시세 ㅅ시스템을 만드는 것도 좋을 것 같다고 생각이 들었다.
'내일배움캠프 TIL' 카테고리의 다른 글
| 내일배움캠프 73일차 TIL "취침 및 강제 취침 관련 아이디어" (0) | 2024.12.31 |
|---|---|
| 내일배움캠프 72일차 TIL "Transparency Sort Mode" (0) | 2024.12.30 |
| 내일배움캠프 70일차 TIL "Popup UI" (0) | 2024.12.26 |
| 내일배움캠프 69일차 TIL "Mask, Rect Mask 2D" (0) | 2024.12.24 |
| 내일배움캠프 68일차 TIL "IPointerEnterHandler" (0) | 2024.12.23 |