Unity 방치형 게임 만들기

장현태입니다·2025년 8월 27일

unity 방치형 게임 만들기

방치형 게임의 큰 재미중 하나인 가챠 시스템을 제작하였다.
기본적인 가챠 시스템으로 틀린 내용이 있을 수 있지만 그래도 이해해주시면 감사하겠다

먼저 설계로
데이터 - 데이터 받아오기 + 가챠 매니저 - 스크립터블(루트 테이블) 이렇게 생성해서 가챠 매니저에서 인벤토리로 데이터를 넘기는 형식을 선택했다 물론 가챠 매니저와 데이터를 받아오는것을 나누면 더 좋지만 프로젝트 규모가 커지면서 데이터만 받아오는 스크립트를 작성하기에 조금 부담이 있었다.

참고 사이트
https://www.youtube.com/watch?v=Gj7UU5IU3-E

1. 루트 테이블 만들기

using JHT;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

[CreateAssetMenu(fileName = "RelicsGachaLootTable", menuName = "RelicsGachaLootTable/RlicsGachaTable")]
public class RelicsGachaLootTable : ScriptableObject
{
	// 1
    [SerializeField] private List<RelicsGacha> _items;
    [System.NonSerialized] private bool isInitialized = false;

    private float _totalWeight;

    private void Initialize()
    {
        if (!isInitialized)
    	{
      		_totalWeight = _items.Sum(item => item.weight);
      		isInitialized = true;
    	}
    }
    
	
	
    // 2
    public RelicsGacha GetRandomRange()
    {
        Initialize();

        float diceRoll = UnityEngine.Random.Range(0f, _totalWeight);

        foreach (var item in _items)
        {
            if (item.weight >= diceRoll)
            {
                return item;
            }

            diceRoll -= item.weight;
        }

        throw new System.Exception("GachaItem get failed");
    }
}

[System.Serializable]
public class RelicsGacha
{
    public ItemRarity rarity;
    public float weight;
}

// 1. RelicsGacha 클래스의 정보를 받아와서(enum, 가중치) 리스트에 넣음(직접 설정해줄 데이터) isInitialized의 경우 런타임 중에서만 값이 유지되도록 [System.NonSerialized]를 사용했고, 한번 total값이 계산되면 게임이 실행되는 도중에는 total값을 다시 계산하지 않아도 되기때문에 true로 변경하고 유지한다.

// 2. 가챠 시스템을 실질적으로 사용하는 곳으로 참고하면 좋은 유니티 메뉴얼이 있다
https://docs.unity3d.com/kr/530/Manual/RandomNumbers.html

 for (i = 0; i < probs.Length; i++) {
        if (randomPoint < probs[i])
            return i;
        else
            randomPoint -= probs[i];
    }

메뉴얼의 이부분을 사용해서 구현한 곳으로 해당 값을 찾을경우 바로 반환을 하도록 설정했다. 그래서 굳이 총 weight의 값을 100으로 맞추지 않아도 해당 가중치에 따라 값을 나눌 수 있도록 설정했다(물론 나는 데이터값 저장한 csv가 있어서 받아왔다)

public void GetLevel()
{
    RelicsGacha picked = levelTable.GetRandomRange();
    switch (picked.rarity)
    {
        case ItemRarity.Normal:
            levelResult = UnityEngine.Random.Range(1, 41);
            break;
        case ItemRarity.Rare:
            levelResult = UnityEngine.Random.Range(41, 70);
            break;
        case ItemRarity.Epic:
            levelResult = UnityEngine.Random.Range(71, 85);
            break;
        case ItemRarity.Unique:
            levelResult = UnityEngine.Random.Range(85, 96);
            break;
        case ItemRarity.Legend:
            levelResult = UnityEngine.Random.Range(96, 101);
            break;
    }
}

처음 가져온 데이터 값으로 등급별로 Normal ~ Legend를 나누고 해당 부분의 가중치에 따라 값이 나오면 그 값안에서 균등한 랜덤 레벨을 제공했다

public void GetRarity()
{
    RelicsGacha picked = rarityTable.GetRandomRange();
    rarityResult = picked.rarity;
}

두번째로 그냥 값을 가져와서 해당 희귀도를 적용했다.

지금 내가 작성한 루트 테이블 코드는 양산하기에는 좋지만(나는 하나만 뽑을 수 있게 설정햇음) 추후에 10개 100개 뽑기에 RelicsGacha의 클래스를 사용해야만 데이터를 만들 수 있기 때문에 좋은 코드라고 보기엔 어려운거 같다


++ 추가 팁

Random.value : 0.0 ~ 1.0 사이의 임의의 부동 소수점 수를 제공
사용법 : 해당 결과를 곱하여 0과 선택한 범위 사이의 숫자로 변환

Random.Range : 최솟값과 최댓값 사이의 임의의 숫자를 제공

원 or 구체 안에 랜덤 포인트
Random.insideUnitCircle : 반지름이 1인 원 내부의 점을 임의로 선택하여 반환
Random.insideUnitSphere : 반지름이 1인 구 내부의 점을 임의로 선택하여 반환
Random.onUnitSphere : 반지름이 1인 구의 표면에 있는 점을 임의로 선택하여 반환

기타 타입의 랜덤 값
Random-rotation : 랜덤 회전
Random.ColorHSV : 랜덤 컬러

배열에서 랜덤 항목 선택
var element = myArray[Random.Range(0, myArray.Length)];

0개의 댓글