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

이형원·2025년 9월 26일
0

XR플밍

목록 보기
207/215

1. 금일 한 업무 정리

  • 캐릭터 강화 스테이터스 UI 활성화 반영
  • 캐릭터 강화 시 조각+골드 둘 다 소모 후 레벨업하도록 반영
  • 캐릭터 최초 획득 조건 변경 -> 캐릭터 가챠는 조각만 등장, 조각 10개 획득 시 캐릭터 해금 가능 방식으로 변경
  • 캐릭터 가챠 테이블 업데이트 및 기획 측 제시한 가챠 확률 및 조각 개수 업데이트
  • 가챠 진행 시 다이아 재화 확인 후 진행 및 db 반영 확인

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

2.1 캐릭터 강화 스테이터스 UI 반영

강화 스테이터스 증가에 대한 부분은 이와 같이 처리했다.

private void StatusUpgradeUIUpdate()
{
    for(int i = 0; i < _characterUpgradeLevelText.Length; i++)
    {
        _characterUpgradeLevelText[i].text = $"LV.{2 * (i + 1)}";

        List<StatusGrowth> statusGrowths = _currentCharUnit.Status.Data.UpgradeStatData.GetCurrentStatusData(_currentCharUnit.Status.Data.Grade, 2 * (i + 1));

        StringBuilder sb = new StringBuilder();
        for(int j = 0; j < statusGrowths.Count; j++)
        {
            sb.Append($"{StatTypeTranslate(statusGrowths[j].Type)} {statusGrowths[j].Value} ");
            if (j != statusGrowths.Count - 1 && j % 2 == 1) sb.Append("\n");
        }

        sb.Append("증가");

        _characterUpgradeDescriptionText[i].text = sb.ToString();
    }

    ActiveUpgradeStatusInfo();
}

private void ActiveUpgradeStatusInfo()
{
    int currentUpgradeLevel = _currentCharUnit.Status.Data.UpgradeData.CurrentUpgradeData.UpgradeLevel / 2;

    for(int i = 0; i < currentUpgradeLevel; i++)
    {
        _upgradeStatusDisablePanel[i].SetActive(false);
    }
    for(int i = currentUpgradeLevel; i < _upgradeStatusDisablePanel.Length; i++)
    {
        _upgradeStatusDisablePanel[i].SetActive(true);
    }
}

private string StatTypeTranslate(StatType type)
{
    string text = "";

    switch(type)
    {
        case StatType.MaxHealth: text = "최대체력"; break;
        case StatType.MaxMana: text = "최대마나"; break;
        case StatType.ManaGain: text = "마나 회복속도"; break;
        case StatType.AttackSpeed: text = "공격속도"; break;
        case StatType.MoveSpeed: text = "공격속도"; break;
        case StatType.PhysicalDamage: text = "물리공격력"; break;
        case StatType.MagicDamage: text = "마법공격력"; break;
        case StatType.CritChance: text = "크리티컬확률"; break;
        case StatType.CritDamage: text = "크리티컬 데미지"; break;
        case StatType.PhysicalDefense: text = "물리방어력"; break;
        case StatType.MagicDefense: text = "마법방어력"; break;
        case StatType.Shield: text = "쉴드"; break;
        case StatType.AttackRange: text = "공격범위"; break;
        case StatType.AttackCount: text = "공격횟수"; break;
        case StatType.CurHp: text = "현재체력"; break;
        case StatType.CurMana: text = "현재마나"; break;
    }
    return text;
}
  • 스테이터스 정보를 가져오기 위해 함수 추가
using System;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "UpgradeStatData", menuName = "Data/Upgrade/UpgradeStatData")]
public class UpgradeStatData : ScriptableObject
{
    public List<GradeGrowth> StatData;

    public List<StatusGrowth> GetCurrentStatusData(Grade grade, int level)
    {
        GradeGrowth gradeGrowth = StatData.Find(g => g.CharGrade == grade);
        if (gradeGrowth.Equals(default(GradeGrowth)))
        {
            Debug.LogWarning($"[UpgradeStatData] 해당 Grade({grade}) 데이터가 없음");
            return null;
        }

        LevelGrowth levelGrowth = gradeGrowth.Stats.Find(l => l.Level == level);
        if (levelGrowth.Equals(default(LevelGrowth)))
        {
            Debug.LogWarning($"[UpgradeStatData] Grade({grade}) 내 Level({level}) 데이터가 없음");
            return null;
        }

        return levelGrowth.Stats; // 해당 레벨의 StatusGrowth 리스트 반환
    }
}

[Serializable]
public struct GradeGrowth
{
    public Grade CharGrade;
    public List<LevelGrowth> Stats;
}

[Serializable]
public struct LevelGrowth
{
    public int Level;
    public List<StatusGrowth> Stats;
}

[Serializable]
public struct StatusGrowth
{
    public StatType Type;
    public float Value;
}

여기서 유의점으로는, StatType를 다시 맞는 텍스트로 변경하는 작업과 스테이터스가 자연스럽게 표시될 수 있도록 스테이터스 두 개 표시 후 엔터를 하는 방식의 추가가 필요했다.

  • 텍스트 변경x 의 경우

  • 텍스트 변경만 적용하고, 엔터에 대한 조건을 넣지 않았을 경우

출력에는 문제가 없으나, 미관상 보기가 안 좋다.

  • 텍스트 변경, 엔터 추가 밎 마지막에 (증가)라는 멘트를 추가함

2.2 캐릭터 강화 요소에서 재화 추가, 확인 및 재화 소모 로직

using Firebase.Database;
using System;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "Unit_UpgradeUnitData", menuName = "Data/Upgrade/Unit_UpgradeUnitData")]
public class UpgradeUnitData : ScriptableObject
{
    // 캐릭터의 업그레이드 레벨 - 레벨이 0일 때는 획득하지 않은 상태
    private Grade _grade;
    private LevelUpData _levelUpData;

    public CurrentUpgradeData CurrentUpgradeData;

    public event Action OnLevelUp;

    public void Init(Grade grade, LevelUpData data)
    {
        _grade = grade;
        _levelUpData = data;
    }
    
    public int GetRequiredPiece()
    {
        if (CurrentUpgradeData.UpgradeLevel >= 10 || 
            CurrentUpgradeData.UpgradeLevel <= 0) return 0;

        if (_levelUpData == null)
        {
            Debug.LogError($"[{name}] LevelUpData가 설정되지 않았습니다.");
            return 0;
        }

        RequirePiece requirePiece = _levelUpData.RequirePieceData.Find(r=>r.Grade == _grade);
        if (requirePiece == null)
        {
            Debug.LogError($"[{name}] {_grade} 등급에 맞는 RequirePiece 데이터가 없습니다.");
            return 0;
        }

        PieceLevelRatio pieceLevelRatio = requirePiece.LevelRatio.Find(l => l.Level == CurrentUpgradeData.UpgradeLevel + 1);
        if (pieceLevelRatio == null)
        {
            Debug.LogError($"[{name}] {_grade} / {CurrentUpgradeData.UpgradeLevel}에 맞는 PieceLevelRatio 데이터가 없습니다.");
            return 0;
        }

        return pieceLevelRatio.RequirePiece;
    }

    public int GetRequiredGold()
    {
        if (CurrentUpgradeData.UpgradeLevel >= 10 ||
            CurrentUpgradeData.UpgradeLevel <= 0) return 0;

        if (_levelUpData == null)
        {
            Debug.LogError($"[{name}] LevelUpData가 설정되지 않았습니다.");
            return 0;
        }

        RequirePiece requirePiece = _levelUpData.RequirePieceData.Find(r => r.Grade == _grade);
        if (requirePiece == null)
        {
            Debug.LogError($"[{name}] {_grade} 등급에 맞는 RequirePiece 데이터가 없습니다.");
            return 0;
        }

        PieceLevelRatio pieceLevelRatio = requirePiece.LevelRatio.Find(l => l.Level == CurrentUpgradeData.UpgradeLevel + 1);
        if (pieceLevelRatio == null)
        {
            Debug.LogError($"[{name}] {_grade} / {CurrentUpgradeData.UpgradeLevel}에 맞는 PieceLevelRatio 데이터가 없습니다.");
            return 0;
        }

        return pieceLevelRatio.RequireGold;
    }

    public void AddPiece(int piece)
    {
        CurrentUpgradeData.CurrentPieces += piece;
    }

    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 diaRef = FirebaseManager.DataReference.Child("UserData").Child(uid).Child("Gold");

            DataSnapshot snapshot = await diaRef.GetValueAsync();

            int currentGold = 0;

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

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

                OnLevelUp?.Invoke();
            }
        }
    }
}

[CreateAssetMenu(fileName = "Unit_LevelUpData", menuName = "Data/Temp/Unit_LevelUpData")]
public class LevelUpData : ScriptableObject
{
    public List<RequirePiece> RequirePieceData = new();
}

[Serializable]
public class RequirePiece
{
    public Grade Grade;
    public List<PieceLevelRatio> LevelRatio = new List<PieceLevelRatio>();
}

/// <summary>
/// 에디터상 입력을 위해 임시로 List 로 처리함. 추후에 Dictionary로 전환할 필요성 있음
/// </summary>
[Serializable]
public class PieceLevelRatio
{
    public int RequireGold;
    public int RequirePiece;
    public int Level;
}

[Serializable]
public class CurrentUpgradeData
{
    // 현재 보유 캐릭터 조각 수
    public int CurrentPieces;

    public int UpgradeLevel;

    public void SetData(int currentPieces, int upgradeLevel)
    {
        CurrentPieces = currentPieces;
        UpgradeLevel = upgradeLevel;
    }
}

3. 개선점 및 과제

3.1 게임 스테이지 관련 작업 - 증강 시스템 및 인게임 시스템 마저 구현

3.2 마법석 시스템 추가 시 강화 시스템, 가챠 시스템 추가

3.3 UI 폴리싱

3.4 리팩토링

profile
게임 만들러 코딩 공부중

0개의 댓글