가중치 랜덤 확률 함수는 모바일 게임에서의 가챠 즉, 캐릭터 뽑기나 카드뽑기, 아이템 드랍 등 많은 곳에 사용 됩니다. 그때마다 로직을 짜기엔 시간이 아깝기 때문에 이번시간은 어디서나 쉽고 간편하게 사용 가능한 가중치 함수를 구현해 보고 사용 예시코드 까지 제공해 보겠습니다.
[가중치 랜덤 코드]
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class WeightedItem<T>
{
public T item;
public float weight;
}
public class WeightedRandomUtility
{
public static T GetWeightedRandom<T>(List<WeightedItem<T>> weightedItems)
{
// 만약 항목이 없으면 기본값 반환
if (weightedItems.Count == 0)
return default(T);
// 모든 가중치를 더하여 총합을 구함
float totalWeight = 0f;
foreach (var weightedItem in weightedItems)
{
totalWeight += weightedItem.weight;
}
// 0부터 총합 사이의 랜덤 값을 생성
float randomValue = UnityEngine.Random.value * totalWeight;
// 랜덤 값이 어느 범위에 속하는지 확인하여 항목 선택
foreach (var weightedItem in weightedItems)
{
randomValue -= weightedItem.weight;
if (randomValue <= 0)
return weightedItem.item;
}
// 여기까지 왔다면 뭔가 잘못된 것이 아니므로 마지막 항목 반환
return weightedItems[weightedItems.Count - 1].item;
}
}
이 유틸리티 함수는 가중치의 총합이 100이 아니더라도 사용이 가능 하다는 장점이 있으며, 순서가 확률에 영향을 미치지 않습니다. NextDouble함수를 사용하여 0~1 범위에서 랜덤 값을 가져와 총 가중치에 곱하여 사용하기 때문입니다.
using UnityEngine;
using System.Collections.Generic;
public class ExampleUsage : MonoBehaviour
{
[SerializeField]
private List<WeightedItem<string>> weightedItems = new List<WeightedItem<string>>();
void Start()
{
weightedItems.Add(new WeightedItem<string> { item = "Item1", weight = 0.5f });
weightedItems.Add(new WeightedItem<string> { item = "Item2", weight = 0.3f });
weightedItems.Add(new WeightedItem<string> { item = "Item3", weight = 0.2f });
string selectedItem = WeightedRandomUtility.GetWeightedRandom(weightedItems);
Debug.Log("Selected Item: " + selectedItem);
}
}
위 코드와 같이 어떤 데이터라도 사용이 가능 합니다. 일일이 데이터를 Add 시키는 부분이 번거로울 수 있습니다.
[캐릭터 뽑기 예시 코드]
using UnityEngine;
using System;
using System.Collections.Generic;
// CharacterData 클래스 정의
[System.Serializable]
public class CharacterData
{
public string name;
public int level;
public float weight; // CharacterData 객체 내부에 가중치를 정의합니다.
// 추가적인 필드들을 여기에 정의할 수 있습니다.
public CharacterData(string name, int level, float weight)
{
this.name = name;
this.level = level;
this.weight = weight;
}
}
// WeightedItem 클래스 정의
[System.Serializable]
public class WeightedItem<T>
{
public T item;
public float weight;
}
// 익스텐션 메서드 정의
public static class WeightedItemExtensions
{
// 제네릭으로 작성된 익스텐션 메서드
public static List<WeightedItem<T>> ToWeightedItemList<T>(this List<T> dataList, Func<T, float> weightSelector)
{
List<WeightedItem<T>> weightedItemList = new List<WeightedItem<T>>();
foreach (T data in dataList)
{
weightedItemList.Add(new WeightedItem<T> { item = data, weight = weightSelector(data) });
}
return weightedItemList;
}
}
public class ExampleUsage : MonoBehaviour
{
void Start()
{
// CharacterData 객체를 담는 리스트 생성
List<CharacterData> characterList = new List<CharacterData>
{
new CharacterData("Alice", 10, 0.4f),
new CharacterData("Bob", 15, 0.3f),
new CharacterData("Charlie", 20, 0.3f)
};
// CharacterData 리스트를 WeightedItem 리스트로 변환
List<WeightedItem<CharacterData>> weightedCharacterList = characterList.ToWeightedItemList(character => character.weight);
// 가중치에 따라 무작위로 CharacterData 객체를 선택합니다.
CharacterData selectedCharacter = WeightedRandomUtility.GetWeightedRandom(weightedCharacterList).item;
// 선택된 CharacterData 객체 정보 출력
Debug.Log("Selected Character: " + selectedCharacter.name + ", Level: " + selectedCharacter.level);
}
}
위는 캐릭터 데이터를 랜덤 확률 함수를 실행시키는 코드 입니다. ToWeightedItemList를 이용하여 선택할 데이터 리스트와 확률을 담아서 손쉽게 사용할 수 있습니다.
[아이템 드랍 예시 코드]
using UnityEngine;
using System;
using System.Collections.Generic;
// ItemData 클래스 정의
[System.Serializable]
public class ItemData
{
public string itemName;
public float dropRate; // 아이템 드랍 확률을 추가합니다.
public ItemData(string itemName, float dropRate)
{
this.itemName = itemName;
this.dropRate = dropRate;
}
}
// WeightedItem 클래스 정의
[System.Serializable]
public class WeightedItem<T>
{
public T item;
public float weight;
}
// 익스텐션 메서드 정의
public static class WeightedItemExtensions
{
// 제네릭으로 작성된 익스텐션 메서드
public static List<WeightedItem<T>> ToWeightedItemList<T>(this List<T> dataList, Func<T, float> weightSelector)
{
List<WeightedItem<T>> weightedItemList = new List<WeightedItem<T>>();
foreach (T data in dataList)
{
weightedItemList.Add(new WeightedItem<T> { item = data, weight = weightSelector(data) });
}
return weightedItemList;
}
}
public class ItemDrop : MonoBehaviour
{
// 아이템 드랍 확률을 고려한 아이템 리스트
public List<ItemData> itemDataList = new List<ItemData>
{
new ItemData("Health Potion", 0.3f), // 체력 포션 드랍 확률: 30%
new ItemData("Mana Potion", 0.2f), // 마나 포션 드랍 확률: 20%
new ItemData("Gold Coin", 0.5f) // 골드 코인 드랍 확률: 50%
};
// 아이템을 드랍하는 메서드
public void DropItem()
{
// 아이템 리스트를 WeightedItem 리스트로 변환
List<WeightedItem<ItemData>> weightedItemList = itemDataList.ToWeightedItemList(item => item.dropRate);
// 가중치에 따라 무작위로 아이템을 선택합니다.
ItemData droppedItem = WeightedRandomUtility.GetWeightedRandom(weightedItemList).item;
// 선택된 아이템을 출력합니다.
Debug.Log("Dropped Item: " + droppedItem.itemName);
}
}
위 아이템 드랍 코드는 간단한 예시입니다. 실제로는 아이템 드랍 테이블을 받아와서 사용하시면 됩니다.
[마무리 글]
랜덤 함수는 순서의 영향을 받지 않도록 사용해야 하며, 뽑기나 아이템 드랍등 중요한 시스템에 사용되기에 유틸리티를 잘 작성해 두시면 간편하게 사용할 수 있기에 개발 속도를 많이 올려 줍니다. 제가 제시한 코드를 잘 숙지 하셔서 간편하게 사용하시길 바랍니다.
‘Random’ 정적 형식의 변수를 선언할 수 없습니다. CS0723
이러면서 오류가 나네요
급하게 작성하다 보니 실수가 있었네요 수정했습니다. 오류 지적 감사합니다 ^^