XR플밍 - 12. UnityEngine3D Reactive 프로그래밍 - 기업협약 프로젝트 31일차 (9/29)

이형원·5일 전
0

XR플밍

목록 보기
210/215

1. 금일 한 업무 정리

  • 신화석 시스템 구현

2. 문제의 발생과 해결 과정

신화석을 어떻게 구성할 것인가에 대해서, 우선은 시스템적인 기능을 먼저 구현하였다.

시스템적인 기능을 구현하기 위해 다음과 같은 3가지를 먼저 구성하였다.

  1. db에 신화석을 저장하고 로드하는 기능 추가
  2. 신화석을 획득하는 방법을 계산하는 메소드 추가
  3. 신화석을 소모하는 방법을 계산하는 메소드 추가

2.1 db에 신화석을 저장하고 로드하는 기능

신화석을 표현하기 위해, 간단하게 신화석을 재화의 개념처럼 db에 저장하고 로드하는 방식을 사용한다.

유저 데이터로 저장되는 항목 중에는, 다이아몬드와 골드 같은 재화가 존재한다. 여기서 모든 캐릭터의 강화 재료로 쓰이는 공통 재화로 통용될 수 있는 신화석에 대해서는 이와 같이 하나의 재화로 표현하고, 이 재화의 획득과 소모를 별도로 구현하는 방식을 사용하는 것이 적절하다고 판단했다.

이에 따라 DBManager에 아래와 같이 재화를 획득, 소모하는 기능을 추가하여 db에 등록하는 과정을 추가하였다.

/// <summary>
/// 9/29 추가
/// 유저의 현재 캐릭터 신화석 값을 읽어와 지정한 값만큼 증가시킨 뒤 저장하는 메서드
/// </summary>
/// <param name="subAmount"></param>
/// <returns></returns>
public async Task AddMythStoneAsync(int subAmount)
{
    string uid = FirebaseManager.Auth.CurrentUser.UserId;
    var mythPieceRef = FirebaseManager.DataReference.Child("UserData").Child(uid).Child("MythStone");

    DataSnapshot snapshot = await mythPieceRef.GetValueAsync();

    int current = 0;

    if (snapshot.Exists && snapshot.Value != null)
    {
        current = Convert.ToInt32(snapshot.Value);
    }

    int next = current + subAmount;

    await mythPieceRef.SetValueAsync(next);
}

/// <summary>
/// 9/29 추가
/// 유저의 현재 캐릭터 신화석 값을 읽어와 지정한 값만큼 감소시킨 뒤 저장하는 메서드
/// </summary>
/// <param name="subAmount"></param>
/// <returns></returns>
public async Task SubtractMythStoneAsync(int subAmount)
{
    string uid = FirebaseManager.Auth.CurrentUser.UserId;
    var diaRef = FirebaseManager.DataReference.Child("UserData").Child(uid).Child("MythStone");

    DataSnapshot snapshot = await mythPieceRef.GetValueAsync();

    int current = 0;

    if (snapshot.Exists && snapshot.Value != null)
    {
        current = Convert.ToInt32(snapshot.Value);
    }

    int next = current - subAmount;

    await mythPieceRef.SetValueAsync(next);
}

2.2 신화석을 획득하는 과정 계산

신화석을 획득하는 조건은 다음과 같다.


* 획득 비율도 이와 동일하게 진행

이와 같은 시스템을 구성하기 위해서는 다음과 같은 과정이 필요하다.

  1. 캐릭터의 현재 레벨 및 보유 조각 수 대비 최대 레벨 강화까지 필요한 조각 수에 대한 계산
  2. 현재 획득한 조각 수를 더했을 때, 캐릭터의 최대 레벨 강화까지 필요한 조각 수를 초과했는지에 대한 계산하고, 초과한 개수만큼의 신화석 개수 변환
  3. 신화석 개수를 db에 반영

1. 캐릭터의 현재 레벨 및 보유 조각 수 대비 최대 레벨 강화까지 필요한 조각 수에 대한 계산

이 부분을 위해서, LevelUpData에 함수를 추가했다.

/// <summary>
/// 현재 캐릭터의 등급, 레벨을 기반으로, 최대 레벨을 찍기까지
/// 남은 조각 개수를 반환하는 함수
/// </summary>
/// <param name="grade"></param>
/// <param name="level"></param>
public int GetCumulativePiece(Grade grade, int level)
{
    RequirePiece requirePiece = RequirePieceData.Find(r => r.Grade == grade);
    if (requirePiece == null)
    {
        Debug.LogError($"[{name}] {grade} 등급에 맞는 RequirePiece 데이터가 없습니다.");
        return 0;
    }

    int cumulativePiece = 0;

    int index = level - 1;
    if (index < 0)
    {
        index = 0;
        cumulativePiece += 10;
    }

    for(int i = index; i < requirePiece.LevelRatio.Count; i++)
    {
        cumulativePiece += requirePiece.LevelRatio[i].RequirePiece;
    }
    Debug.Log(cumulativePiece);

    return cumulativePiece;
}

이와 같이 계산하여, 현재 레벨 및 등급을 기준으로 추가로 필요한 조각 개수를 구할 수 있기 때문에, 이와 현재 보유 조각 수를 비교하여 조각의 최대치까지 얻었는지 구할 수 있다.

2. 현재 획득한 조각 수를 더했을 때, 캐릭터의 최대 레벨 강화까지 필요한 조각 수를 초과했는지에 대한 계산. 그리고 초과한 개수만큼의 신화석 개수 변환 과정.

RandomGachaSystem에 아래와 같이 함수를 추가하였다.

private bool IsOveredPieceUpperLimit(UnitData data, int inputPiece, out int overPiece, out int mythPiece)
{
    Grade grade = data.Grade;
    int level = data.UpgradeData.CurrentUpgradeData.UpgradeLevel;
    int requirePiece = data.LevelUpData.GetCumulativePiece(grade, level);

    if (data.UpgradeData.CurrentUpgradeData.CurrentPieces + inputPiece < requirePiece )
    {
        overPiece = 0;
        mythPiece = 0;
        return false;
    }
    else
    {
        if(data.UpgradeData.CurrentUpgradeData.CurrentPieces > requirePiece)
        {
            overPiece = inputPiece;
        }
        else
        {
            overPiece = data.UpgradeData.CurrentUpgradeData.CurrentPieces + inputPiece - requirePiece;
        }
        
        int pieceRatio = 0;
        switch(grade)
        {
            case Grade.NORMAL: pieceRatio = overPiece * 2; break;
            case Grade.RARE: pieceRatio = overPiece * 4; break;
            case Grade.UNIQUE: pieceRatio = overPiece * 6; break;
            case Grade.LEGEND: pieceRatio = overPiece * 9; break;
        }
        Debug.Log($"{data.Name}의 조각 개수 초과. {overPiece}개를 초과하여 신화석 {pieceRatio}만큼 지급");

        mythPiece = pieceRatio;

        return true;
    }
}

3. 신화석 개수를 db에 반영

RandomGachaSystem의 뽑기 과정에서, db에 반영하는 내용을 추가한다.

private async void ItemSelect(int number)
{
    if (_gradeCharRandom.GetList() == null) RandomInit(_prob);

    for (int i = 0; i < number; i++)
    {
        UnitData data = ReturnData();

        int pieceNum = ReturnPieceByGrade(data);

        if (IsOveredPieceUpperLimit(data, pieceNum, out int overPiece, out int mythPiece))
        {
            if (overPiece != pieceNum)
            {
                data.UpgradeData.AddPiece(pieceNum - overPiece);
                await DBManager.Instance.charDB.SaveCharacterUpgradeData(data);
            }

            _resultUI.HeroGachaUpdate(data, i, pieceNum.ToString());

            await DBManager.Instance.AddMythStoneAsync(mythPiece);
        }
        else
        {
            data.UpgradeData.AddPiece(pieceNum);

            _resultUI.HeroGachaUpdate(data, i, pieceNum.ToString());

            await DBManager.Instance.charDB.SaveCharacterUpgradeData(data);
        }
    }

    _resultUI.gameObject.SetActive(true);
}

2.3 신화석을 소모하는 방법을 계산하는 메소드 추가

UpgradeUnitData에 아래와 같이 LevelUp 과정을 진행하는 것으로, 신화석 소모를 통한 레벨업을 구현했다.
개인적으로는, 이 부분은 리팩토링이 좀 필요한 구간이라고 생각한다.

public async void LevelUp()
{
    // 최대레벨 변수 추가?
    if (CurrentUpgradeData.UpgradeLevel >= 10) return;

    if (CurrentUpgradeData.UpgradeLevel == 0)
    {
        if (CurrentUpgradeData.CurrentPieces >= 10)
        {
            CurrentUpgradeData.CurrentPieces -= 10;
            CurrentUpgradeData.UpgradeLevel += 1;

            OnLevelUp?.Invoke();
        }
    }
    else
    {
        int requiredPiece = GetRequiredPiece();
        int requiredGold = GetRequiredGold();

        string uid = FirebaseManager.Auth.CurrentUser.UserId;
        var goldRef = FirebaseManager.DataReference.Child("UserData").Child(uid).Child("Gold");
        var mythStoneRef = FirebaseManager.DataReference.Child("UserData").Child(uid).Child("MythStone");

        DataSnapshot snapshot = await goldRef.GetValueAsync();
        DataSnapshot snapshot2 = await mythStoneRef.GetValueAsync();

        int currentGold = 0;

        if (snapshot.Exists && snapshot.Value != null)
        {
            currentGold = Convert.ToInt32(snapshot.Value);
        }

        int currentMythStone = 0;

        if (snapshot2.Exists && snapshot2.Value != null)
        {
            currentMythStone = Convert.ToInt32(snapshot2.Value);
        }

        int conversedMythstone = currentMythStone / MythStonePieceRatio(_grade);

        if (CurrentUpgradeData.CurrentPieces >= requiredPiece && currentGold >= requiredGold)
        {
            CurrentUpgradeData.CurrentPieces -= requiredPiece;
            CurrentUpgradeData.UpgradeLevel += 1;
            await DBManager.Instance.SubtractGoldAsync(requiredGold);

            OnLevelUp?.Invoke();
        }
        else if (CurrentUpgradeData.CurrentPieces + conversedMythstone >= requiredPiece && currentGold >= requiredGold)
        {
            int subMythStone = requiredPiece - CurrentUpgradeData.CurrentPieces;
            await DBManager.Instance.SubtractMythStoneAsync(subMythStone * MythStonePieceRatio(_grade));

            CurrentUpgradeData.CurrentPieces = 0;
            CurrentUpgradeData.UpgradeLevel += 1;
            await DBManager.Instance.SubtractGoldAsync(requiredGold);

            OnLevelUp?.Invoke();
        }
    }
}

private int MythStonePieceRatio(Grade grade)
{
    int pieceRatio = 0;

    switch (grade)
    {
        case Grade.NORMAL: pieceRatio = 2; break;
        case Grade.RARE: pieceRatio = 4; break;
        case Grade.UNIQUE: pieceRatio = 6; break;
        case Grade.LEGEND: pieceRatio = 9; break;
    }

    return pieceRatio;
}

3. 개선점 및 과제

3.1 신화석 개념의 UI 적용

현재 신화석 관련 UI 기획이 아예 없다 보니, UI적 표현이 빠져 있는 상태이다. 신화석을 획득한 경우에 대한 가챠 연출 UI 추가, 신화석의 소모 과정 중 개수에 대한 UI 표기 및 사용 여부 체크 등의 과정이 필요해 보인다.

3.2 마법석 가챠, 강화 추가

현재 마법석을 다른 담당자가 구성하고 있고, 완성이 되면 해당 데이터를 바탕으로 가챠 및 강화를 추가할 예정이다.

3.3 UI 폴리싱

3.4 리팩토링

profile
게임 만들러 코딩 공부중

0개의 댓글