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

이형원·2025년 9월 24일
0

XR플밍

목록 보기
205/215

1. 금일 한 업무 정리

  • 스테이터스 증강 유닛 반영 추가
  • 스테이터스 증강 5종, 회복 증강 1종, 재화 증강 1종 추가
  • 캐릭터 강화 레벨별 강화 스테이터스 누적 합산 기능 구현

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

강화 레벨별 강화 스테이터스 누적 합산 기능은 다음과 같이 구현하였다.

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[CreateAssetMenu(fileName = "Unit_Data", menuName = "Data/Unit/Unit_Data")]
public class UnitData : MetaData
{
    [Header("MetaData")]
    public Grade Grade;
    public string AddressableAddress;
    public GameObject UnitPrefab => Manager.Resources.Load<GameObject>(AddressableAddress);
    public int ID;
    public int PerferredLine;
    public int Cost;

    [Header("AnimationData")]
    public bool isNotChange;
    public AnimatorData AnimatiorData;

    [Header("Attack_Data")]
    public UnitSkill Skill;
    public UnitAttackData AttackData; // Melee, Ranged 등 공격 타입에 따라 다름

    [Header("Unit_Stat")]    
    public UnitStats[] UnitStats; // 3개 1,2,3 성
    public UnitStats[] UpgradeStats; // UnitStats의 레벨 강화 반영

    [Header("Synergy")]
    public ClassType ClassSynergy;
    public Synergy Synergy;

    [Header("Upgrade")]
    public UpgradeUnitData UpgradeData;
    public LevelUpData LevelUpData;
    public UpgradeStatData UpgradeStatData;

    public UnitStats GetUnitStat(int level)
    {
        return UpgradeStats[level];
    }

    public void Init()
    {
        UpgradeData?.Init(Grade, LevelUpData);

        if(UpgradeData != null)
        {
            UpgradeData.OnLevelUp += UpdateUpgradeStats;
            UpdateUpgradeStats();
        }
    }

    private void OnDestroy()
    {
        if(UpgradeData != null)
        {
            UpgradeData.OnLevelUp -= UpdateUpgradeStats;
        }
    }

    public void UpdateUpgradeStats()
    {
        UpgradeStats = new UnitStats[UnitStats.Length];
        for (int i = 0; i < UnitStats.Length; i++)
        {
            UpgradeStats[i] = UnitStats[i].Clone();
        }

        if (UpgradeStatData == null) return;

        var gradeGrowth = UpgradeStatData.StatData.Find(g => g.CharGrade == Grade);
        if (gradeGrowth.Stats == null || gradeGrowth.Stats.Count == 0) return;

        int currentLevel = UpgradeData?.CurrentUpgradeData.UpgradeLevel ?? 0;

        foreach (var levelGrowth in gradeGrowth.Stats)
        {
            if (levelGrowth.Level <= currentLevel)
            {
                foreach (var statGrowth in levelGrowth.Stats)
                {
                    for (int i = 0; i < UpgradeStats.Length; i++)
                    {
                        UpgradeStats[i].AddStat(statGrowth.Type, statGrowth.Value);
                    }
                }
            }
        }
    }

    public UnitDataDTO ToDTO(UnitData data)
    {
        return new UnitDataDTO
        {
            ClassSynergy = (int)data.ClassSynergy,
            Cost = data.Cost,
            Description = data.Description,
            Grade = data.Grade,
            ID = data.ID,
            Name = data.name,
            PrefferedLine = data.PerferredLine,
            Synergy = (int)data.Synergy,
            UnitStats = data.UnitStats
        };
    }
}

[System.Serializable]
public class UnitDataDTO
{
    public int ClassSynergy;
    public int Cost;
    public string Description;
    public Grade Grade;
    public int ID;
    public string Name;
    public int PrefferedLine;
    public int Synergy;
    public UnitStats[] UnitStats;
}
using System;
using UnityEngine;

[System.Serializable]
public class UnitStats
{
    [Header("Status")]
    public int MaxHealth;
    public int MaxMana;
    public int ManaGain;
    public float AttackSpeed;
    public float MoveSpeed;

    [Header("Damage")]
    public int PhysicalDamage;
    public int MagicDamage;

    [Header("CritRate")]
    public int CritChance;

    [Header("Defense")]
    public int PhysicalDefense;
    public int MagicDefense;

    [Header("Range")]
    public float AttackRange;
    public int AttackCount;

    public UnitStats Clone()
    {
        return new UnitStats
        {
            MaxHealth = this.MaxHealth,
            MaxMana = this.MaxMana,
            ManaGain = this.ManaGain,
            AttackSpeed = this.AttackSpeed,
            MoveSpeed = this.MoveSpeed,
            PhysicalDamage = this.PhysicalDamage,
            MagicDamage = this.MagicDamage,
            CritChance = this.CritChance,
            PhysicalDefense = this.PhysicalDefense,
            MagicDefense = this.MagicDefense,
            AttackRange = this.AttackRange,
            AttackCount = this.AttackCount
        };
    }

    public void AddStat(StatType type, float value)
    {
        switch (type)
        {
            case StatType.MaxHealth:
                MaxHealth += (int)value;
                break;
            case StatType.MaxMana:
                MaxMana += (int)value;
                break;
            case StatType.ManaGain:
                ManaGain += (int)value;
                break;
            case StatType.AttackSpeed:
                AttackSpeed += value;
                break;
            case StatType.MoveSpeed:
                MoveSpeed += value;
                break;
            case StatType.PhysicalDamage:
                PhysicalDamage += (int)value;
                break;
            case StatType.MagicDamage:
                MagicDamage += (int)value;
                break;
            case StatType.CritChance:
                CritChance += (int)value;
                break;
            case StatType.PhysicalDefense:
                PhysicalDefense += (int)value;
                break;
            case StatType.MagicDefense:
                MagicDefense += (int)value;
                break;
            case StatType.AttackRange:
                AttackRange += (int)value;
                break;
            case StatType.AttackCount:
                AttackCount += (int)value;
                break;
        }
    }
    public void AddAugments(StatType status, float rate, string name, UnitStatusController controller)
    {
        StatEffectModifier modifier = CalculateAugment(status, rate);
        AugmentManager.Instance.AddAugment(modifier.StatType, modifier.Value, name, controller);
    }

    public StatEffectModifier CalculateAugment(StatType status, float rate)
    {
        StatEffectModifier modifier = new StatEffectModifier();
        modifier.StatType = status;
        switch (modifier.StatType)
        {
            case StatType.MaxHealth: modifier.Value = Mathf.RoundToInt(MaxHealth * (rate / 100)); break;
            case StatType.MaxMana: modifier.Value = Mathf.RoundToInt(MaxMana * (rate / 100)); break;
            case StatType.ManaGain: modifier.Value = Mathf.RoundToInt(ManaGain * (rate / 100)); break;
            case StatType.AttackSpeed: modifier.Value = AttackSpeed * (rate / 100); break;
            case StatType.MoveSpeed: modifier.Value = MoveSpeed * (rate / 100); break;
            case StatType.PhysicalDamage: modifier.Value = Mathf.RoundToInt(PhysicalDamage * (rate / 100)); break;
            case StatType.MagicDamage: modifier.Value = Mathf.RoundToInt(MagicDamage * (rate / 100)); break;
            case StatType.CritChance: modifier.Value = Mathf.RoundToInt(CritChance * (rate / 100)); break;
            case StatType.PhysicalDefense: modifier.Value = Mathf.RoundToInt(PhysicalDefense * (rate / 100)); break;
            case StatType.MagicDefense: modifier.Value = Mathf.RoundToInt(MagicDefense * (rate / 100)); break;
            case StatType.AttackRange: modifier.Value = AttackRange * (rate / 100); break;
            case StatType.AttackCount: modifier.Value = Mathf.RoundToInt(AttackCount * (rate / 100)); break;
            default: modifier.Value = 0; break;
        }

        return modifier;
    }

    public void RemoveAugments(StatType status, string name, UnitStatusController controller)
    {
        AugmentManager.Instance.RemoveAugment(status, name, controller);
    }
}
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 void ObtainCharacter()
    {
        if (CurrentUpgradeData.UpgradeLevel == 0) CurrentUpgradeData.UpgradeLevel += 1;

    }

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

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

        int requiredPiece = GetRequiredPiece();
        
        if(CurrentUpgradeData.CurrentPieces >= requiredPiece)
        {
            CurrentUpgradeData.CurrentPieces -= requiredPiece;
            CurrentUpgradeData.UpgradeLevel += 1;

            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 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개의 댓글