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

이형원·2025년 9월 22일
0

XR플밍

목록 보기
203/215

1. 금일 한 업무 정리

  • 증강 데이터 중 스테이터스 증강 Modifier 적용 및 해제 구현
  • 골드 획득량 증강 Modifier 적용 및 해제 구현

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

방법은 다음과 같이 구현하였다.

AugmentManager에서 증강 데이터를 저장한다. 이때 스테이터스를 저장하는 방식은 AugmentManager에서 AUGData 증강 SO를 저장한 다음. 해당 증강에서의 조건대로 증강 적용 여부를 판정하고 퍼센티지를 UnitStats로 계산하여 증강 데이터를 저장한다. 이때 Modifier를 해당 증강 데이터가 적용되는 캐릭터의 이름 기준으로 저장되며, 캐릭터의 배치와 해제시 증강 데이터를 적용 및 해제하도록 한다.

  • AugmentManager
using System.Collections.Generic;
using UnityEngine;

public class AugmentManager : InGameSingleton<AugmentManager>
{
    #region Stat
    [Header("Status")]
    public Stat<int> MaxHealth;
    public Stat<float> AttackSpeed;

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

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

    [Header("CritRate")]
    public Stat<int> CritChance;

    [Header("Currency")]
    public Stat<float> GoldBonus;

    [Header("CurrentAugment")]
    public AUGData currentAugment;
    // 테스트용
    [SerializeField] private AUGData AUGData;

    // 캐릭터 스테이터스 적용
    private Dictionary<UnitBase, HashSet<string>> _appliedStatusAugments = new();

    private Dictionary<AUGData, HashSet<string>> _appliedCurrencyAugments = new();
    #endregion

    private void Start()
    {
        SelectAugment(AUGData);
    }

    public void SelectAugment(AUGData data)
    {
        currentAugment = data;
    }

    #region Unit Status Augment

    /// <summary>
    /// 유닛 스테이터스 증강 추가
    /// </summary>
    /// <param name="unit"></param>
    public void ApplyAugment(UnitBase unit)
    {
        if (currentAugment == null || !IsAugmentTarget(unit) || currentAugment.EffectType == EffectType.Currency) return;

        if (!_appliedStatusAugments.ContainsKey(unit))
            _appliedStatusAugments[unit] = new HashSet<string>();

        if (_appliedStatusAugments[unit].Contains(currentAugment.AUGID))
        {
            Debug.LogWarning($"[AugmentManager] {unit.name} 에 {currentAugment.AUGID} 는 이미 적용됨.");
            return;
        }

        _appliedStatusAugments[unit].Add(currentAugment.AUGID);
        currentAugment.ApplyEffect(unit);
    }

    /// <summary>
    /// 유닛 스테이터스 증강 해제
    /// </summary>
    /// <param name="unit"></param>
    public void ReleaseAugment(UnitBase unit)
    {
        if (currentAugment == null) return;

        if (_appliedStatusAugments.TryGetValue(unit, out var augments))
        {
            if (augments.Contains(currentAugment.AUGID))
            {
                currentAugment.RemoveEffect(unit);
                augments.Remove(currentAugment.AUGID);

                if (augments.Count == 0)
                    _appliedStatusAugments.Remove(unit);
            }
            else
            {
                Debug.LogWarning($"[AugmentManager] {unit.name} 에 {currentAugment.AUGID} 는 적용되지 않아 해제할 수 없음.");
            }
        }
    }

    /// <summary>
    /// 유닛 스테이터스 증강 적용 여부 판정
    /// </summary>
    /// <param name="unit"></param>
    /// <returns></returns>
    public bool IsAugmentTarget(UnitBase unit)
    {
        if (currentAugment.TargetType == EffectTargetType.Ally) return true;
        else if (currentAugment.TargetType == EffectTargetType.SameClassType && unit.Status.Data.ClassSynergy == currentAugment.Class)
        {
            Debug.Log("클래스가 같음");
            return true;
        }
        // 리더일 경우 추가 필요

        return false;
    }

    #endregion

    #region Currency Augment

    /// <summary>
    /// 재화 획득 증강 추가
    /// </summary>
    public void ApplyAugment()
    {
        if (currentAugment == null) return;

        if (!_appliedCurrencyAugments.ContainsKey(currentAugment))
            _appliedCurrencyAugments[currentAugment] = new HashSet<string>();

        if (_appliedCurrencyAugments[currentAugment].Contains(currentAugment.AUGID))
        {
            Debug.LogWarning($"[AugmentManager] {currentAugment.name} 에 {currentAugment.AUGID} 는 이미 적용됨.");
            return;
        }

        _appliedCurrencyAugments[currentAugment].Add(currentAugment.AUGID);
        for (int i = 0; i < currentAugment.StatTypes.Length; i++)
        {
            AddAugment(currentAugment.StatTypes[i], currentAugment.currentRate, currentAugment.Name);
        }
    }

    /// <summary>
    /// 재화 획득 증강 해제
    /// </summary>
    public void ReleaseAugment()
    {
        if (currentAugment == null) return;

        if (_appliedCurrencyAugments.TryGetValue(currentAugment, out var augments))
        {
            if (augments.Contains(currentAugment.AUGID))
            {
                for (int i = 0; i < currentAugment.StatTypes.Length; i++)
                {
                    RemoveAugment(currentAugment.StatTypes[i], currentAugment.Name);
                }
                augments.Remove(currentAugment.AUGID);

                if (augments.Count == 0)
                    _appliedCurrencyAugments.Remove(currentAugment);
            }
            else
            {
                Debug.LogWarning($"[AugmentManager] {currentAugment.name} 에 {currentAugment.AUGID} 는 적용되지 않아 해제할 수 없음.");
            }
        }
    }

    #endregion

    #region Augument Management
    public void AddAugment(StatType statType, float value, string source)
    {
        switch (statType)
        {
            case StatType.MaxHealth:
                MaxHealth.AddModifier((int)value, source);
                break;
            case StatType.PhysicalDamage:
                PhysicalDamage.AddModifier((int)value, source);
                break;
            case StatType.MagicDamage:
                MagicDamage.AddModifier((int)value, source);
                break;
            case StatType.CritChance:
                CritChance.AddModifier((int)value, source);
                break;
            case StatType.PhysicalDefense:
                PhysicalDefense.AddModifier((int)value, source);
                break;
            case StatType.MagicDefense:
                MagicDefense.AddModifier((int)value, source);
                break;
            case StatType.AttackSpeed:
                AttackSpeed.AddModifier(value, source);
                break;
            case StatType.GoldBonus:
                GoldBonus.AddModifier(value, source);
                break;
        }
    }

    public void RemoveAugment(StatType statType, string source)
    {
        switch (statType)
        {
            case StatType.MaxHealth:
                MaxHealth.RemoveModifier(source);
                break;
            case StatType.PhysicalDamage:
                PhysicalDamage.RemoveModifier(source);
                break;
            case StatType.MagicDamage:
                MagicDamage.RemoveModifier(source);
                break;
            case StatType.CritChance:
                CritChance.RemoveModifier(source);
                break;
            case StatType.PhysicalDefense:
                PhysicalDefense.RemoveModifier(source);
                break;
            case StatType.MagicDefense:
                MagicDefense.RemoveModifier(source);
                break;
            case StatType.AttackSpeed:
                AttackSpeed.RemoveModifier(source);
                break;
            case StatType.GoldBonus:
                GoldBonus.RemoveModifier(source);
                break;
        }
    }
    #endregion
}
  • AUGData
using UnityEngine;

[CreateAssetMenu(fileName = "AUGEffect", menuName = "Data/AUG/AUGEffect")]
public class AUGData : MetaData
{
    public string AUGID;
    public SubGrade Grade;

    public EffectTargetType TargetType;
    public ClassType Class;

    public TriggerType Trigger;
    public EffectType EffectType;
    public EffectTime ApplyTime;

    public StatType[] StatTypes;

    public float[] Rate = new float[3];

    public float currentRate;

    public void ApplyRate(SubGrade grade)
    {
        switch (grade)
        {
            case SubGrade.SILVER: currentRate = Rate[0]; break;
            case SubGrade.GOLD: currentRate = Rate[1]; break;
            case SubGrade.PRISM: currentRate = Rate[2]; break;
            default: currentRate = 0; break;
        }
    }

    /// <summary>
    /// 캐릭터 스테이터스 증강 적용
    /// </summary>
    /// <param name="unit"></param>
    public void ApplyEffect(UnitBase unit)
    {
        ApplyRate(Grade);
        for (int i = 0; i < StatTypes.Length; i++)
        {
            unit.Status.Data.UnitStats[0].AddAugments(StatTypes[i], currentRate, unit.Status.Data.Name);
        }
    }

    /// <summary>
    /// 재화 증강 적용
    /// </summary>
    public void ApplyEffect()
    {
        ApplyRate(Grade);
        if (EffectType == EffectType.Currency)
        {
            for (int i = 0; i < StatTypes.Length; i++)
            {
                AugmentManager.Instance.AddAugment(StatTypes[i], currentRate, Name);
            }
        }
    }

    /// <summary>
    /// 캐릭터 스테이터스 증강 삭제
    /// </summary>
    /// <param name="unit"></param>
    public void RemoveEffect(UnitBase unit)
    {
        for (int i = 0; i < StatTypes.Length; i++)
        {
            unit.Status.Data.UnitStats[0].RemoveAugments(StatTypes[i], unit.Status.Data.Name);
            Debug.Log("삭제");
        }
    }

    /// <summary>
    /// 재화 증강 삭제
    /// </summary>
    public void RemoveEffect()
    {

    }
}
  • 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 void AddStat(UnitStats stats, StatEffectModifier modifier, bool persent)
    {
        switch (modifier.StatType)
        {
            case StatType.MaxHealth:
                break;
            case StatType.MaxMana:
                break;
            case StatType.ManaGain:
                break;
            case StatType.AttackSpeed:
                break;
            case StatType.MoveSpeed:
                break;
            case StatType.PhysicalDamage:
                break;
            case StatType.MagicDamage:
                break;
            case StatType.CritChance:
                break;
            case StatType.CritDamage:
                break;
            case StatType.PhysicalDefense:
                break;
            case StatType.MagicDefense:
                break;
            case StatType.AttackRange:
                break;
            case StatType.AttackCount:
                break;
            case StatType.CurHp:
                break;
            case StatType.CurMana:
                break;
            case StatType.GoldBonus:
                break;
        }
    }
    public void AddAugments(StatType status, float rate, string name)
    {
        StatEffectModifier modifier = CalculateAugment(status, rate);
        AugmentManager.Instance.AddAugment(modifier.StatType, modifier.Value, name);
    }

    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)
    {
        AugmentManager.Instance.RemoveAugment(status, name);
    }
}

3. 개선점 및 과제

3.1 증강 - 회복 기능 구현 및 연동 작업

3.2 캐릭터 성장 데이터 구현

3.3 UI 폴리싱

3.4 리팩토링

profile
게임 만들러 코딩 공부중

0개의 댓글