안녕하세요 이번에는 Unity에서 가중치 랜덤 뽑기 시스템을 만들어보고자 합니다.
먼저 간단한 가중치 랜덤 뽑기 시스템 클래스를 작성해봤습니다.
각 코드에 대한 설명은 주석을 달아놨습니다.
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 가중치 랜덤 뽑기 시스템 클래스
/// </summary>
public class WeightedRandom<T>
{
public WeightedRandom()
{
_dic = new Dictionary<T, int>();
}
private Dictionary<T, int> _dic;
/// <summary>
/// 가중치 리스트에 아이템과 수량을 추가함
/// </summary>
public void Add(T item, int value)
{
//음수가 들어오면 리턴한다.
if (value < 0)
{
Debug.LogError("음수는 들어갈 수 없습니다.");
return;
}
//만약 딕셔너리에 키가 존재하면?
if (_dic.ContainsKey(item))
{
//해당 키의 값의 수치를 변경한다.
_dic[item] += value;
}
//존재하지 않으면?
else
{
//아이템을 추가한다.
_dic.Add(item, value);
}
}
/// <summary>
/// 가중치 리스트에 아이템이 있으면 지정 수량을 빼고, 지정 수량이 더 크면 리스트에서 아이템을 뺌
/// </summary>
public void Sub(T item, int value)
{
//음수가 들어오면 리턴한다.
if (value < 0)
{
Debug.LogError("음수는 들어갈 수 없습니다.");
return;
}
//만약 딕셔너리에 키가 존재하면?
if (_dic.ContainsKey(item))
{
//키의 값의 크기가 더 크면?
if (_dic[item] > value)
{
//해당 키의 값의 수치를 변경한다.
_dic[item] -= value;
}
//같거나 작으면?
else
{
//삭제한다.
Remove(item);
}
}
else
{
Debug.LogError("아이템이 존재하지 않습니다.");
}
}
/// <summary>
/// 리스트에서 아이템을 삭제
/// </summary>
public void Remove(T item)
{
//만약 딕셔너리에 키가 존재하면?
if (_dic.ContainsKey(item))
{
//해당 키의 데이터를 삭제한다.
_dic.Remove(item);
}
else
{
Debug.LogError("아이템이 존재하지 않습니다.");
}
}
/// <summary>
/// 현재 리스트에 있는 아이템의 가중치를 모두 더해 반환
/// </summary>
public int GetTotalWeight()
{
int totalWeight = 0;
//딕셔너리에 입력된 모든 아이템 가중치 값을 더한다.
foreach (int value in _dic.Values)
{
totalWeight += value;
}
return totalWeight;
}
/// <summary>
/// 아이템 리스트에 있는 모든 아이템의 가중치를 비율로 변환하여 반환 (0, 1 사이)
/// </summary>
public Dictionary<T, float> GetPercent()
{
Dictionary<T, float> _tempDic = new Dictionary<T, float>();
float totalWeight = GetTotalWeight();
foreach (var item in _dic)
{
_tempDic.Add(item.Key, item.Value / totalWeight);
}
return _tempDic;
}
/// <summary>
/// 아이템 리스트에서 랜덤으로 아이템을 뽑아 반환(뽑힌 아이템의 갯수 -1)
/// </summary>
public T GetRamdomItemBySub()
{
//딕셔너리에 들어있는 아이템 갯수가 0이하면
if (_dic.Count <= 0)
{
Debug.LogError("리스트에 아이템이 없습니다. 뽑기 불가능");
return default;
}
//총 가중치를 가져온다.
int weight = 0;
int totalWeight = GetTotalWeight();
//총 가중치 값에 0~1f의 랜덤 값을 곱해 기준점을 구한다.
int pivot = Mathf.RoundToInt(totalWeight * Random.Range(0.0f, 1.0f));
//딕셔너리를 순회하며 가중치를 더하다 기준점 이상이 되면 그 아이템을 반환한다.
foreach (var item in _dic)
{
weight += item.Value;
if (pivot <= weight)
{
_dic[item.Key] -= 1;
return item.Key;
}
}
return default;
}
/// <summary>
/// 아이템 리스트에서 랜덤으로 아이템을 뽑아 반환
/// </summary>
public T GetRamdomItem()
{
//딕셔너리에 들어있는 아이템 갯수가 0이하면 리턴
if (_dic.Count <= 0)
{
Debug.LogError("리스트에 아이템이 없습니다. 뽑기 불가능");
return default;
}
//총 가중치를 가져온다.
int totalWeight = GetTotalWeight();
int weight = 0;
//총 가중치 값에 0~1f의 랜덤 값을 곱해 기준점을 구한다.
int pivot = Mathf.RoundToInt(totalWeight * Random.Range(0.0f, 1.0f));
//딕셔너리를 순회하며 가중치를 더하다 기준점 이상이 되면 그 아이템을 반환한다.
foreach (var item in _dic)
{
weight += item.Value;
if (pivot <= weight)
{
return item.Key;
}
}
return default;
}
/// <summary>
/// 아이템 리스트를 반환
/// </summary>
public Dictionary<T, int> GetList()
{
return _dic;
}
}
사용법
public class Tset : MonoBehaviour
{
private WeightedRandom<string> _weightedRandom = new WeightedRandom<string>();
void Start()
{
//A B C의 가중치를 각각 추가한다.
_weightedRandom.Add("A", 50);
_weightedRandom.Add("B", 40);
_weightedRandom.Add("C", 30);
//A라는 키의 가중치를 5만큼 뺀다.
_weightedRandom.Sub("A", 5);
//리스트에 있는 모든 가중치의 합을 반환한다.
_weightedRandom.GetTotalWeight();
//해당 변수에 들어있는 모든 리스트를 가져온다.
_weightedRandom.GetList();
//해당 변수에 들어있는 모든 리스트의 확률을 반환한다.(0~1값)
_weightedRandom.GetPercent();
//해당 변수에 들어있는 아이템중 하나를 뽑는다.
_weightedRandom.GetRamdomItem();
//해당 변수에 들어있는 아이템중 하나를 뽑고 가중치-1 한다.
_weightedRandom.GetRamdomItemBySub();
}
}
'Unity 공부' 카테고리의 다른 글
[Unity] 컷 아웃 마스크(Cutout Mask) (0) | 2024.01.24 |
---|---|
[Unity] Behavior Tree(BT) (0) | 2023.12.11 |
[Unity] FSM 유한 상태 기계 (0) | 2023.11.21 |
[Unity] PC에 저장된 이미지 파일 불러오기 (0) | 2023.11.02 |
[Unity] 부분 화면 캡처 기능 (0) | 2023.10.31 |