가중치 랜덤 확률 함수는 모바일 게임에서의 가챠 즉, 캐릭터 뽑기나 카드뽑기, 아이템 드랍 등 많은 곳에 사용 됩니다. 그때마다 로직을 짜기엔 시간이 아깝기 때문에 이번시간은 어디서나 쉽고 간편하게 사용 가능한 가중치 함수를 구현해 보고 사용 예시코드 까지 제공해 보겠습니다.

[가중치 랜덤 코드]

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);
    }
}

위 아이템 드랍 코드는 간단한 예시입니다. 실제로는 아이템 드랍 테이블을 받아와서 사용하시면 됩니다.

[마무리 글]

랜덤 함수는 순서의 영향을 받지 않도록 사용해야 하며, 뽑기나 아이템 드랍등 중요한 시스템에 사용되기에 유틸리티를 잘 작성해 두시면 간편하게 사용할 수 있기에 개발 속도를 많이 올려 줍니다. 제가 제시한 코드를 잘 숙지 하셔서 간편하게 사용하시길 바랍니다.

2 thoughts on “[Unity] 유니티 가중치 랜덤 (뽑기, 가챠, 아이템 드랍율 사용 예시코드)”

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다