//재화 종류
public enum CurrencyType
{
ilmenite,
trinitite,
iron58
}
public class CurrencyManager : MonoBehaviour
{
public static CurrencyManager instance;
void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
InitializeCurrencies();
InitializeMultipliers();
LoadSavedData();
shower = new CheckCurrency[System.Enum.GetValues(typeof(CurrencyType)).Length];
for (int i = 0; i < shower.Length; i++)
{
shower[i] = new CheckCurrency();
}
}
else
Destroy(gameObject);
}
//재화 전체 관리 딕셔너리
[SerializeField]
private Dictionary<CurrencyType, BigDouble> currencies = new Dictionary<CurrencyType, BigDouble>();
//재화 배율 관리 배열
[SerializeField]
private double[] additivePercentages;
[SerializeField]
private double[] multiplicativeMultipliers;
//재화 변경 이벤트
public event Action<CurrencyType,BigDouble> OnCurrencyChanged;
#region Currency
// 배율 배열 초기화 (CurrencyType 개수만큼)
private void InitializeCurrencies()
{
additivePercentages = new double[System.Enum.GetValues(typeof(CurrencyType)).Length];
foreach (CurrencyType type in System.Enum.GetValues(typeof(CurrencyType)))
{
currencies[type] = 0;
additivePercentages[(int)type] = 1f; // 기본 배율 1
}
}
//재화 조회
public BigDouble GetCurrency(CurrencyType type)
{
if (!currencies.ContainsKey(type))
currencies[type] = 0;
return currencies[type];
}
//재화 추가
public void AddCurrency(CurrencyType type, BigDouble amount)
{
if (!currencies.ContainsKey(type))
currencies[type] = 0;
BigDouble scaledAmount = amount;
Debug.Log($"Initial amount: {scaledAmount}");
// 배율 계산
BigDouble additivePercentage = additivePercentages[(int)type]; // % 값 (예: 10%)
BigDouble multiplicativeMultiplier = multiplicativeMultipliers[(int)type]; // 배수 값 (예: 1.5)
// 총 배율 계산
BigDouble totalMultiplier = (1 + (additivePercentage / 100.0)) * multiplicativeMultiplier;
Debug.Log($"Total multiplier: {totalMultiplier}");
// 스케일링된 금액 계산
scaledAmount *= totalMultiplier;
Debug.Log($"Final scaled amount: {scaledAmount}");
Debug.Log($"Original: {NumberFormatter.FormatNumber(amount)}, " +
$"After multipliers: {NumberFormatter.FormatNumber(scaledAmount)}");
// 상대적인 증가 적용
BigDouble currentAmount = currencies[type];
if (currentAmount > 1e15 && scaledAmount < currentAmount * 1e-5)
{
// 증가량이 너무 작아서 반영되지 않을 경우 상대적인 증가 적용
BigDouble growthFactor = 1 + (scaledAmount / currentAmount);
currencies[type] *= growthFactor;
}
else
{
currencies[type] += scaledAmount;
}
OnCurrencyChanged?.Invoke(type, currencies[type]);
SaveData();
}
//재화 소모
public bool TrySpendCurrency(CurrencyType type, BigDouble amount)
{
if (!currencies.ContainsKey(type) || currencies[type] < amount)
return false;
currencies[type] -= amount;
OnCurrencyChanged?.Invoke(type, currencies[type]);
SaveData();
return true;
}
//재화 고정
public void SetCurrency(CurrencyType type, BigDouble amount)
{
if (currencies.ContainsKey(type))
{
currencies[type] = amount;
}
else
{
Debug.Log("그런 재화는 없어");
}
}
#endregion
#region Multiplier
private void InitializeMultipliers()
{
int length = System.Enum.GetValues(typeof(CurrencyType)).Length;
additivePercentages = new double[length];
multiplicativeMultipliers = new double[length];
for (int i = 0; i < length; i++)
{
additivePercentages[i] = 0f;
multiplicativeMultipliers[i] = 1f; // 곱셈 배율의 기본값은 1
}
}
public void AddAdditivePercentage(CurrencyType type, float additionalPercentage)
{
additivePercentages[(int)type] += additionalPercentage;
}
public void AddMultiplicativeMultiplier(CurrencyType type, float multiplier, bool isSet = false)
{
if (isSet)
multiplicativeMultipliers[(int)type] = multiplier;
else
multiplicativeMultipliers[(int)type] += multiplier;
}
public BigDouble GetMultiplier(CurrencyType type)
{
return additivePercentages[(int)type];
}
#endregion
private void SaveData()
{
foreach (CurrencyType type in System.Enum.GetValues(typeof(CurrencyType)))
{
// BigDouble 값을 문자열로 변환하여 저장
PlayerPrefs.SetString($"Currency_{type}", currencies[type].ToString());
// additivePercentages와 multiplicativeMultipliers 저장
PlayerPrefs.SetString($"Additive_{type}", additivePercentages[(int)type].ToString());
PlayerPrefs.SetString($"Multiplier_{type}", multiplicativeMultipliers[(int)type].ToString());
}
PlayerPrefs.Save();
}
private void LoadSavedData()
{
foreach (CurrencyType type in System.Enum.GetValues(typeof(CurrencyType)))
{
if (PlayerPrefs.HasKey($"Currency_{type}"))
{
// BigDouble.Parse를 사용하여 문자열을 BigDouble로 변환
currencies[type] = BigDouble.Parse(PlayerPrefs.GetString($"Currency_{type}"));
additivePercentages[(int)type] = double.Parse(PlayerPrefs.GetString($"Additive_{type}"), System.Globalization.CultureInfo.InvariantCulture);
multiplicativeMultipliers[(int)type] = double.Parse(PlayerPrefs.GetString($"Multiplier_{type}"), System.Globalization.CultureInfo.InvariantCulture);
}
else
{
// 저장된 데이터가 없는 경우 기본값 설정
currencies[type] = 0;
additivePercentages[(int)type] = 0.0;
multiplicativeMultipliers[(int)type] = 1.0;
}
}
}
private void OnApplicationQuit()
{
SaveData();
}
[SerializeField]
CheckCurrency[] shower;
private void Update()
{
for (int i = 0; i < shower.Length; i++)
{
CurrencyType type = (CurrencyType)i; // enum의 순서대로 접근
if (currencies.ContainsKey(type))
{
shower[i].SetCurrencyChecker(type.ToString(), currencies[type]);
}
}
}
}
[System.Serializable]
public class CheckCurrency
{
public string name;
public BigDouble amount;
public void SetCurrencyChecker(string name, BigDouble amount)
{
this.name = name;
this.amount = amount;
}
}
첫 개발 작품으로 증분형 게임 만들면서 여러가지 재화들을 이 스크립트 하나에서 다루고 있는데 질문할게 몇 가지 있어요
1. 각 재화마다 소모될지 안될지 설정을 할 필요가 있는데 지금 Enum과 딕셔너리로 다루고 있는 재화들을 클래스로 갈아엎어야 하나요?
2. 증분형 게임에 보통 순수 BigDouble을 사용하나요? 게임이 후반으로 가면 초반 재화들은 지수가 e1000은 넘을거 같은데 BigDouble은 e308까지 감당 가능하고
그 위의 숫자는 무한으로 취급한다길래 보통 한계를 고려하고 설계해서 쓰는지 아니면 다른 더 큰 수를 감당할 수 있는 시스템을 이용하는지 궁금합니다.
3. 이지세이브가 없어서 각각 재화랑 배율은 저렇게 저장하는데 작동 자체는 잘 되지만 보안 같은 외부적으로 괜찮은 코드인지 궁금합니다.
2번 같은 경우는 e307까지만 계산하고 n×e307부턴 다른 변수에 n값을 저장하면 될거 같은데? - dc App
그 방법 머리속에 있긴 했는데 혹시 특정한 이름 같은거 없나요? 검색을 어떻게 해야할지 모르겠어서
이름같은건 없을거 같은데... 근데 링크드리스트로 만들어놓고 링크걸어두면 링크를 선언해넣고 하기 쉬울듯요 - dc App
배열로 연결해놓던가요 - dc App
2. Double만으로 1년 넘게 서비스하다가 감당 안되서 에셋 스토어의 'Infinite Value (BigDecimal)' 에셋 구매해서 씀. 아직까지 큰 문제는 없음
https://github.com/Razenpok/BreakInfinity.cs
넘어가면
이걸로 충분할거임
진짜 엄청 큰 무한에 가까운 수를 쓰고싶으면 이것처럼 구현하면됨
https://github.com/Patashu/break_eternity.js