캐릭터에 증강 스텟을 반영하기 위해서 어떻게 해야 할지 고민하다가, UnitStatusController로 스텟 반영이 필요한 것을 이벤트를 이용해서 연결하는 방식으로 변경했다.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem.XR;
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.Buff_Debuff) 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.ApplyBuffEffect(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.RemoveBuffEffect(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 Unit Increase
public void ApplyHealAugment(UnitBase unit)
{
if (currentAugment == null || !IsAugmentTarget(unit) || currentAugment.EffectType != EffectType.Increase) 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.ApplyIncreaseEffect(unit);
}
public void ReleaseHealAugment(UnitBase unit)
{
if (currentAugment == null) return;
if (_appliedStatusAugments.TryGetValue(unit, out var augments))
{
if (augments.Contains(currentAugment.AUGID))
{
augments.Remove(currentAugment.AUGID);
if (augments.Count == 0)
_appliedStatusAugments.Remove(unit);
}
else
{
Debug.LogWarning($"[AugmentManager] {unit.name} 에 {currentAugment.AUGID} 는 적용되지 않아 해제할 수 없음.");
}
}
}
#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, UnitStatusController controller)
{
switch (statType)
{
case StatType.MaxHealth:
MaxHealth.AddModifier((int)value, source);
controller?.AddStat(statType, (int)value, source);
break;
case StatType.PhysicalDamage:
PhysicalDamage.AddModifier((int)value, source);
controller?.AddStat(statType, (int)value, source);
break;
case StatType.MagicDamage:
MagicDamage.AddModifier((int)value, source);
controller?.AddStat(statType, (int)value, source);
break;
case StatType.CritChance:
CritChance.AddModifier((int)value, source);
controller?.AddStat(statType, (int)value, source);
break;
case StatType.PhysicalDefense:
PhysicalDefense.AddModifier((int)value, source);
controller?.AddStat(statType, (int)value, source);
break;
case StatType.MagicDefense:
MagicDefense.AddModifier((int)value, source);
controller?.AddStat(statType, (int)value, source);
break;
case StatType.AttackSpeed:
AttackSpeed.AddModifier(value, source);
controller?.AddStat(statType, (int)value, source);
break;
}
}
public void AddAugment(StatType statType, float value, string source)
{
switch (statType)
{
case StatType.GoldBonus:
GoldBonus.AddModifier(value, source);
break;
}
}
public void RemoveAugment(StatType statType, string source, UnitStatusController controller)
{
switch (statType)
{
case StatType.MaxHealth:
MaxHealth.RemoveModifier(source);
controller?.RemoveStat(statType, source);
break;
case StatType.PhysicalDamage:
PhysicalDamage.RemoveModifier(source);
controller?.RemoveStat(statType, source);
break;
case StatType.MagicDamage:
MagicDamage.RemoveModifier(source);
controller?.RemoveStat(statType, source);
break;
case StatType.CritChance:
CritChance.RemoveModifier(source);
controller?.RemoveStat(statType, source);
break;
case StatType.PhysicalDefense:
PhysicalDefense.RemoveModifier(source);
controller?.RemoveStat(statType, source);
break;
case StatType.MagicDefense:
MagicDefense.RemoveModifier(source);
controller?.RemoveStat(statType, source);
break;
case StatType.AttackSpeed:
AttackSpeed.RemoveModifier(source);
controller?.RemoveStat(statType, source);
break;
}
}
public void RemoveAugment(StatType statType, string source)
{
switch (statType)
{
case StatType.GoldBonus:
GoldBonus.RemoveModifier(source);
break;
}
}
#endregion
}
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;
/// <summary>
/// 증강 등급 적용
/// </summary>
/// <param name="grade"></param>
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;
}
}
#region Status Augment
/// <summary>
/// 캐릭터 스테이터스 증강 적용
/// </summary>
/// <param name="unit"></param>
public void ApplyBuffEffect(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, unit.StatusController);
}
}
/// <summary>
/// 캐릭터 스테이터스 증강 삭제
/// </summary>
/// <param name="unit"></param>
public void RemoveBuffEffect(UnitBase unit)
{
for (int i = 0; i < StatTypes.Length; i++)
{
unit.Status.Data.UnitStats[0].RemoveAugments(StatTypes[i], unit.Status.Data.Name, unit.StatusController);
}
}
#endregion
#region Increase Augment
/// <summary>
/// 회복 증강의 회복 적용
/// </summary>
/// <param name="unit"></param>
public void ApplyIncreaseEffect(UnitBase unit)
{
ApplyRate(Grade);
if (StatTypes[0] == StatType.MaxHealth)
{
int increaseRate = (int)(unit.Status.Data.UnitStats[0].MaxHealth * (1 + currentRate / 100));
unit.StatusController.IncreaseHealth(increaseRate);
}
else if (StatTypes[0] == StatType.MaxMana)
{
int increaseRate = (int)(unit.Status.Data.UnitStats[0].MaxMana * (1 + currentRate / 100));
unit.StatusController.IncreaseMana(increaseRate);
}
}
#endregion
}
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 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);
}
}